From 81845676da8df1aa11329f8db975f70fbb675807 Mon Sep 17 00:00:00 2001 From: Eric Fritz Date: Thu, 14 May 2020 15:29:03 -0500 Subject: [PATCH] codeintel: Remove old typescript services (#10566) --- .dockerignore | 5 - .github/CODEOWNERS | 1 - .github/workflows/lsif.yml | 15 - .vscode/settings.json | 4 - .vscode/tasks.json | 7 - ThirdPartyLicensesNpm.csv | 358 - babel.config.js | 2 - cmd/precise-code-intel/.eslintrc.js | 15 - cmd/precise-code-intel/README.md | 24 - cmd/precise-code-intel/api-server/Dockerfile | 45 - cmd/precise-code-intel/api-server/build.sh | 25 - cmd/precise-code-intel/babel.config.js | 8 - .../bundle-manager/Dockerfile | 46 - .../bundle-manager/build.sh | 25 - cmd/precise-code-intel/docs/api/api.md | 15 - cmd/precise-code-intel/docs/api/api.yaml | 628 -- cmd/precise-code-intel/docs/api/manager.yaml | 462 -- .../docs/database/database.md | 12 - .../docs/database/datamodel.md | 234 - .../docs/database/datamodel.pg.md | 68 - cmd/precise-code-intel/jest.config.js | 7 - cmd/precise-code-intel/jest.setup.js | 5 - cmd/precise-code-intel/package.json | 82 - .../src/@types/express-opentracing/index.d.ts | 3 - cmd/precise-code-intel/src/api-server/api.ts | 80 - .../src/api-server/backend/backend.test.ts | 633 -- .../src/api-server/backend/cursor.ts | 92 - .../src/api-server/backend/database.ts | 175 - .../src/api-server/backend/location.ts | 42 - .../src/api-server/metrics.ts | 40 - .../src/api-server/routes/internal.ts | 95 - .../src/api-server/routes/lsif.ts | 321 - .../src/api-server/routes/uploads.ts | 133 - .../src/api-server/settings.ts | 35 - .../src/api-server/tasks.ts | 81 - .../src/bundle-manager/backend/cache.test.ts | 238 - .../src/bundle-manager/backend/cache.ts | 433 -- .../bundle-manager/backend/database.test.ts | 378 -- .../src/bundle-manager/backend/database.ts | 588 -- .../src/bundle-manager/backend/location.ts | 30 - .../src/bundle-manager/manager.ts | 58 - .../src/bundle-manager/metrics.ts | 83 - .../src/bundle-manager/routes/database.ts | 227 - .../src/bundle-manager/routes/uploads.ts | 100 - .../src/bundle-manager/settings.ts | 60 - .../src/bundle-manager/tasks.ts | 206 - cmd/precise-code-intel/src/shared/api/init.ts | 62 - .../src/shared/api/middleware/errors.ts | 45 - .../src/shared/api/middleware/metrics.ts | 27 - .../src/shared/api/middleware/validation.ts | 85 - .../src/shared/api/pagination/cursor.ts | 29 - .../src/shared/api/pagination/limit-offset.ts | 18 - .../src/shared/api/pagination/link.ts | 23 - .../src/shared/config/config.ts | 116 - .../src/shared/config/settings.ts | 15 - .../src/shared/constants.ts | 27 - .../src/shared/database/inserter.ts | 90 - .../src/shared/database/logger.ts | 47 - .../src/shared/database/metrics.ts | 29 - .../src/shared/database/postgres.ts | 191 - .../src/shared/database/settings.ts | 16 - .../src/shared/database/sqlite.ts | 28 - .../datastructures/bloom-filter.test.ts | 12 - .../src/shared/datastructures/bloom-filter.ts | 39 - .../shared/datastructures/default-map.test.ts | 32 - .../src/shared/datastructures/default-map.ts | 32 - .../datastructures/disjoint-set.test.ts | 18 - .../src/shared/datastructures/disjoint-set.ts | 52 - .../shared/datastructures/orderedset.test.ts | 27 - .../src/shared/datastructures/orderedset.ts | 30 - .../src/shared/datastructures/settings.ts | 13 - .../src/shared/encoding/json.test.ts | 35 - .../src/shared/encoding/json.ts | 72 - .../src/shared/gitserver/gitserver.test.ts | 64 - .../src/shared/gitserver/gitserver.ts | 197 - .../src/shared/gitserver/metrics.ts | 15 - .../src/shared/input.test.ts | 133 - cmd/precise-code-intel/src/shared/input.ts | 68 - cmd/precise-code-intel/src/shared/logging.ts | 83 - .../src/shared/maps.test.ts | 13 - cmd/precise-code-intel/src/shared/maps.ts | 42 - cmd/precise-code-intel/src/shared/metrics.ts | 24 - .../src/shared/models/hash.ts | 22 - .../src/shared/models/queries.ts | 95 - .../src/shared/models/sqlite.ts | 305 - .../src/shared/models/util.ts | 12 - cmd/precise-code-intel/src/shared/paths.ts | 56 - cmd/precise-code-intel/src/shared/settings.ts | 9 - .../src/shared/store/dependencies.test.ts | 236 - .../src/shared/store/dumps.test.ts | 766 --- .../src/shared/store/dumps.ts | 368 -- .../src/shared/store/locks.ts | 71 - .../src/shared/store/metrics.ts | 10 - cmd/precise-code-intel/src/shared/tasks.ts | 71 - .../src/shared/test-util.ts | 170 - cmd/precise-code-intel/src/shared/tracing.ts | 117 - cmd/precise-code-intel/src/shared/util.ts | 8 - .../src/shared/visibility.ts | 79 - .../src/worker/conversion/batch.test.ts | 107 - .../src/worker/conversion/batch.ts | 100 - .../src/worker/conversion/conversion.ts | 51 - .../src/worker/conversion/correlator.test.ts | 323 - .../src/worker/conversion/correlator.ts | 463 -- .../src/worker/conversion/existence.test.ts | 98 - .../src/worker/conversion/importer.ts | 783 --- .../src/worker/conversion/paths.ts | 24 - cmd/precise-code-intel/src/worker/metrics.ts | 29 - cmd/precise-code-intel/src/worker/settings.ts | 24 - cmd/precise-code-intel/src/worker/worker.ts | 173 - .../test-data/lsif-go@ad3507cb.lsif.gz | Bin 220125 -> 0 bytes cmd/precise-code-intel/tsconfig.json | 33 - cmd/precise-code-intel/worker/Dockerfile | 55 - cmd/precise-code-intel/worker/README.md | 7 - cmd/precise-code-intel/worker/build.sh | 31 - cmd/precise-code-intel/worker/main.go | 86 - cmd/precise-code-intel/worker/prometheus.yml | 10 - cmd/precise-code-intel/yarn.lock | 5777 ----------------- cmd/server/build.sh | 3 - dev/ci/yarn-run.sh | 1 - dev/foreach-ts-project.sh | 1 - dev/licenses-npm.sh | 4 - dev/start.sh | 5 - doc/dev/architecture/index.md | 1 - doc/dev/tech_stack.md | 4 - jest.config.js | 8 +- renovate.json | 4 - tsconfig.json | 1 - 127 files changed, 1 insertion(+), 18673 deletions(-) delete mode 100644 cmd/precise-code-intel/.eslintrc.js delete mode 100644 cmd/precise-code-intel/README.md delete mode 100644 cmd/precise-code-intel/api-server/Dockerfile delete mode 100755 cmd/precise-code-intel/api-server/build.sh delete mode 100644 cmd/precise-code-intel/babel.config.js delete mode 100644 cmd/precise-code-intel/bundle-manager/Dockerfile delete mode 100755 cmd/precise-code-intel/bundle-manager/build.sh delete mode 100644 cmd/precise-code-intel/docs/api/api.md delete mode 100644 cmd/precise-code-intel/docs/api/api.yaml delete mode 100644 cmd/precise-code-intel/docs/api/manager.yaml delete mode 100644 cmd/precise-code-intel/docs/database/database.md delete mode 100644 cmd/precise-code-intel/docs/database/datamodel.md delete mode 100644 cmd/precise-code-intel/docs/database/datamodel.pg.md delete mode 100644 cmd/precise-code-intel/jest.config.js delete mode 100644 cmd/precise-code-intel/jest.setup.js delete mode 100644 cmd/precise-code-intel/package.json delete mode 100644 cmd/precise-code-intel/src/@types/express-opentracing/index.d.ts delete mode 100644 cmd/precise-code-intel/src/api-server/api.ts delete mode 100644 cmd/precise-code-intel/src/api-server/backend/backend.test.ts delete mode 100644 cmd/precise-code-intel/src/api-server/backend/cursor.ts delete mode 100644 cmd/precise-code-intel/src/api-server/backend/database.ts delete mode 100644 cmd/precise-code-intel/src/api-server/backend/location.ts delete mode 100644 cmd/precise-code-intel/src/api-server/metrics.ts delete mode 100644 cmd/precise-code-intel/src/api-server/routes/internal.ts delete mode 100644 cmd/precise-code-intel/src/api-server/routes/lsif.ts delete mode 100644 cmd/precise-code-intel/src/api-server/routes/uploads.ts delete mode 100644 cmd/precise-code-intel/src/api-server/settings.ts delete mode 100644 cmd/precise-code-intel/src/api-server/tasks.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/backend/cache.test.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/backend/cache.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/backend/database.test.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/backend/database.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/backend/location.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/manager.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/metrics.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/routes/database.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/routes/uploads.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/settings.ts delete mode 100644 cmd/precise-code-intel/src/bundle-manager/tasks.ts delete mode 100644 cmd/precise-code-intel/src/shared/api/init.ts delete mode 100644 cmd/precise-code-intel/src/shared/api/middleware/errors.ts delete mode 100644 cmd/precise-code-intel/src/shared/api/middleware/metrics.ts delete mode 100644 cmd/precise-code-intel/src/shared/api/middleware/validation.ts delete mode 100644 cmd/precise-code-intel/src/shared/api/pagination/cursor.ts delete mode 100644 cmd/precise-code-intel/src/shared/api/pagination/limit-offset.ts delete mode 100644 cmd/precise-code-intel/src/shared/api/pagination/link.ts delete mode 100644 cmd/precise-code-intel/src/shared/config/config.ts delete mode 100644 cmd/precise-code-intel/src/shared/config/settings.ts delete mode 100644 cmd/precise-code-intel/src/shared/constants.ts delete mode 100644 cmd/precise-code-intel/src/shared/database/inserter.ts delete mode 100644 cmd/precise-code-intel/src/shared/database/logger.ts delete mode 100644 cmd/precise-code-intel/src/shared/database/metrics.ts delete mode 100644 cmd/precise-code-intel/src/shared/database/postgres.ts delete mode 100644 cmd/precise-code-intel/src/shared/database/settings.ts delete mode 100644 cmd/precise-code-intel/src/shared/database/sqlite.ts delete mode 100644 cmd/precise-code-intel/src/shared/datastructures/bloom-filter.test.ts delete mode 100644 cmd/precise-code-intel/src/shared/datastructures/bloom-filter.ts delete mode 100644 cmd/precise-code-intel/src/shared/datastructures/default-map.test.ts delete mode 100644 cmd/precise-code-intel/src/shared/datastructures/default-map.ts delete mode 100644 cmd/precise-code-intel/src/shared/datastructures/disjoint-set.test.ts delete mode 100644 cmd/precise-code-intel/src/shared/datastructures/disjoint-set.ts delete mode 100644 cmd/precise-code-intel/src/shared/datastructures/orderedset.test.ts delete mode 100644 cmd/precise-code-intel/src/shared/datastructures/orderedset.ts delete mode 100644 cmd/precise-code-intel/src/shared/datastructures/settings.ts delete mode 100644 cmd/precise-code-intel/src/shared/encoding/json.test.ts delete mode 100644 cmd/precise-code-intel/src/shared/encoding/json.ts delete mode 100644 cmd/precise-code-intel/src/shared/gitserver/gitserver.test.ts delete mode 100644 cmd/precise-code-intel/src/shared/gitserver/gitserver.ts delete mode 100644 cmd/precise-code-intel/src/shared/gitserver/metrics.ts delete mode 100644 cmd/precise-code-intel/src/shared/input.test.ts delete mode 100644 cmd/precise-code-intel/src/shared/input.ts delete mode 100644 cmd/precise-code-intel/src/shared/logging.ts delete mode 100644 cmd/precise-code-intel/src/shared/maps.test.ts delete mode 100644 cmd/precise-code-intel/src/shared/maps.ts delete mode 100644 cmd/precise-code-intel/src/shared/metrics.ts delete mode 100644 cmd/precise-code-intel/src/shared/models/hash.ts delete mode 100644 cmd/precise-code-intel/src/shared/models/queries.ts delete mode 100644 cmd/precise-code-intel/src/shared/models/sqlite.ts delete mode 100644 cmd/precise-code-intel/src/shared/models/util.ts delete mode 100644 cmd/precise-code-intel/src/shared/paths.ts delete mode 100644 cmd/precise-code-intel/src/shared/settings.ts delete mode 100644 cmd/precise-code-intel/src/shared/store/dependencies.test.ts delete mode 100644 cmd/precise-code-intel/src/shared/store/dumps.test.ts delete mode 100644 cmd/precise-code-intel/src/shared/store/dumps.ts delete mode 100644 cmd/precise-code-intel/src/shared/store/locks.ts delete mode 100644 cmd/precise-code-intel/src/shared/store/metrics.ts delete mode 100644 cmd/precise-code-intel/src/shared/tasks.ts delete mode 100644 cmd/precise-code-intel/src/shared/test-util.ts delete mode 100644 cmd/precise-code-intel/src/shared/tracing.ts delete mode 100644 cmd/precise-code-intel/src/shared/util.ts delete mode 100644 cmd/precise-code-intel/src/shared/visibility.ts delete mode 100644 cmd/precise-code-intel/src/worker/conversion/batch.test.ts delete mode 100644 cmd/precise-code-intel/src/worker/conversion/batch.ts delete mode 100644 cmd/precise-code-intel/src/worker/conversion/conversion.ts delete mode 100644 cmd/precise-code-intel/src/worker/conversion/correlator.test.ts delete mode 100644 cmd/precise-code-intel/src/worker/conversion/correlator.ts delete mode 100644 cmd/precise-code-intel/src/worker/conversion/existence.test.ts delete mode 100644 cmd/precise-code-intel/src/worker/conversion/importer.ts delete mode 100644 cmd/precise-code-intel/src/worker/conversion/paths.ts delete mode 100644 cmd/precise-code-intel/src/worker/metrics.ts delete mode 100644 cmd/precise-code-intel/src/worker/settings.ts delete mode 100644 cmd/precise-code-intel/src/worker/worker.ts delete mode 100644 cmd/precise-code-intel/test-data/lsif-go@ad3507cb.lsif.gz delete mode 100644 cmd/precise-code-intel/tsconfig.json delete mode 100644 cmd/precise-code-intel/worker/Dockerfile delete mode 100644 cmd/precise-code-intel/worker/README.md delete mode 100755 cmd/precise-code-intel/worker/build.sh delete mode 100644 cmd/precise-code-intel/worker/main.go delete mode 100644 cmd/precise-code-intel/worker/prometheus.yml delete mode 100644 cmd/precise-code-intel/yarn.lock diff --git a/.dockerignore b/.dockerignore index 4af2c40639b..23e006230e9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -101,8 +101,3 @@ node_modules/ # Extensions /packages/sourcegraph-extension-api/dist - -# Precise code intel -./cmd/precise-code-intel/node_modules -./cmd/precise-code-intel/out -./cmd/precise-code-intel/test-data diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 463334f9043..53345fcab32 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -223,7 +223,6 @@ Dockerfile @sourcegraph/distribution /cmd/frontend/db/discussion* @slimsag # Precise code intel -/cmd/precise-code-intel/ @sourcegraph/code-intel /cmd/precise-code-intel-api-server/ @sourcegraph/code-intel /cmd/precise-code-intel-bundle-manager/ @sourcegraph/code-intel /cmd/precise-code-intel-worker/ @sourcegraph/code-intel diff --git a/.github/workflows/lsif.yml b/.github/workflows/lsif.yml index 6416ab3d012..5c16cc187fb 100644 --- a/.github/workflows/lsif.yml +++ b/.github/workflows/lsif.yml @@ -30,21 +30,6 @@ jobs: working-directory: web/ run: src lsif upload -github-token=${{ secrets.GITHUB_TOKEN }} - lsif-precise-code-intel: - runs-on: ubuntu-latest - container: sourcegraph/lsif-node - defaults: - run: - working-directory: cmd/precise-code-intel - steps: - - uses: actions/checkout@v1 - - name: Install dependencies - run: yarn --ignore-engines --ignore-scripts - - name: Generate LSIF data - run: lsif-tsc -p . - - name: Upload LSIF data - run: src lsif upload -github-token=${{ secrets.GITHUB_TOKEN }} - lsif-shared: runs-on: ubuntu-latest container: sourcegraph/lsif-node diff --git a/.vscode/settings.json b/.vscode/settings.json index a9037751972..5615a538cd0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -64,10 +64,6 @@ "directory": "shared", "changeProcessCWD": true, }, - { - "directory": "cmd/precise-code-intel", - "changeProcessCWD": true, - }, ], "go.lintTool": "golangci-lint", "shellformat.flag": "-i 2 -ci", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5a874cdf954..d60c27d9312 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -80,13 +80,6 @@ "path": "web/", "problemMatcher": ["$eslint-stylish"], }, - { - "label": "eslint:precise-code-intel", - "type": "npm", - "script": "eslint", - "path": "cmd/precise-code-intel/", - "problemMatcher": ["$eslint-stylish"], - }, { "label": "eslint:release", "type": "npm", diff --git a/ThirdPartyLicensesNpm.csv b/ThirdPartyLicensesNpm.csv index 697a1fbcb86..67283bc1a45 100644 --- a/ThirdPartyLicensesNpm.csv +++ b/ThirdPartyLicensesNpm.csv @@ -619,361 +619,3 @@ "yallist@3.0.3","ISC","https://github.com/isaacs/yallist" "yargs-parser@9.0.2","ISC","https://github.com/yargs/yargs-parser" "yargs@11.0.0","MIT","https://github.com/yargs/yargs" -"@sindresorhus/is@2.1.0","MIT","https://github.com/sindresorhus/is" -"@szmarczak/http-timer@4.0.5","MIT","https://github.com/szmarczak/http-timer" -"@types/cacheable-request@6.0.1","MIT","https://github.com/DefinitelyTyped/DefinitelyTyped" -"@types/http-cache-semantics@4.0.0","MIT","https://github.com/DefinitelyTyped/DefinitelyTyped" -"@types/keyv@3.1.1","MIT","https://github.com/DefinitelyTyped/DefinitelyTyped" -"@types/node@12.7.11","MIT","https://github.com/DefinitelyTyped/DefinitelyTyped" -"@types/responselike@1.0.0","MIT","https://github.com/DefinitelyTyped/DefinitelyTyped" -"@types/retry@0.12.0","MIT","https://github.com/DefinitelyTyped/DefinitelyTyped" -"abbrev@1.1.1","ISC","https://github.com/isaacs/abbrev-js" -"accepts@1.3.7","MIT","https://github.com/jshttp/accepts" -"ajv@6.10.2","MIT","https://github.com/epoberezkin/ajv" -"ansi-color@0.2.1","BSD*","https://github.com/loopj/commonjs-ansi-color" -"ansi-regex@2.1.1","MIT","https://github.com/chalk/ansi-regex" -"ansi-regex@4.1.0","MIT","https://github.com/chalk/ansi-regex" -"ansi-styles@2.2.1","MIT","https://github.com/chalk/ansi-styles" -"ansi-styles@3.2.1","MIT","https://github.com/chalk/ansi-styles" -"any-promise@1.3.0","MIT","https://github.com/kevinbeaty/any-promise" -"app-root-path@3.0.0","MIT","https://github.com/inxilpro/node-app-root-path" -"append-type@1.0.2","MIT-0","https://github.com/shinnn/append-type" -"aproba@1.2.0","ISC","https://github.com/iarna/aproba" -"are-we-there-yet@1.1.5","ISC","https://github.com/iarna/are-we-there-yet" -"argparse@1.0.10","MIT","https://github.com/nodeca/argparse" -"array-flatten@1.1.1","MIT","https://github.com/blakeembrey/array-flatten" -"array-to-sentence@1.1.0","MIT","https://github.com/shinnn/array-to-sentence" -"asn1@0.2.4","MIT","https://github.com/joyent/node-asn1" -"assert-plus@1.0.0","MIT","https://github.com/mcavage/node-assert-plus" -"assert-valid-glob-opts@1.0.0","CC0-1.0","https://github.com/shinnn/assert-valid-glob-opts" -"async-middleware@1.2.1","MIT","https://github.com/blakeembrey/async-middleware" -"async-polling@0.2.1","MIT","https://github.com/cGuille/async-polling" -"async@2.6.3","MIT","https://github.com/caolan/async" -"asynckit@0.4.0","MIT","https://github.com/alexindigo/asynckit" -"aws-sign2@0.7.0","Apache-2.0","https://github.com/mikeal/aws-sign" -"aws4@1.8.0","MIT","https://github.com/mhart/aws4" -"balanced-match@1.0.0","MIT","https://github.com/juliangruber/balanced-match" -"base64-js@1.3.1","MIT","https://github.com/beatgammit/base64-js" -"bcrypt-pbkdf@1.0.2","BSD-3-Clause","https://github.com/joyent/node-bcrypt-pbkdf" -"bintrees@1.0.1","MIT*","https://github.com/vadimg/js_bintrees" -"bloomfilter@0.0.18","BSD-3-Clause","https://github.com/jasondavies/bloomfilter.js" -"body-parser@1.19.0","MIT","https://github.com/expressjs/body-parser" -"brace-expansion@1.1.11","MIT","https://github.com/juliangruber/brace-expansion" -"buffer-writer@2.0.0","MIT","https://github.com/brianc/node-buffer-writer" -"buffer@5.4.3","MIT","https://github.com/feross/buffer" -"bufrw@1.3.0","MIT","https://github.com/uber/bufrw" -"bytes@3.1.0","MIT","https://github.com/visionmedia/bytes.js" -"cacheable-lookup@2.0.0","MIT","https://github.com/szmarczak/cacheable-lookup" -"cacheable-request@7.0.1","MIT","https://github.com/lukechilds/cacheable-request" -"camelcase@5.3.1","MIT","https://github.com/sindresorhus/camelcase" -"caseless@0.12.0","Apache-2.0","https://github.com/mikeal/caseless" -"chalk@1.1.3","MIT","https://github.com/chalk/chalk" -"chalk@2.4.2","MIT","https://github.com/chalk/chalk" -"chownr@1.1.3","ISC","https://github.com/isaacs/chownr" -"cli-highlight@2.1.1","ISC","https://github.com/felixfbecker/cli-highlight" -"cliui@5.0.0","ISC","https://github.com/yargs/cliui" -"clone-response@1.0.2","MIT","https://github.com/lukechilds/clone-response" -"code-point-at@1.1.0","MIT","https://github.com/sindresorhus/code-point-at" -"color-convert@1.9.3","MIT","https://github.com/Qix-/color-convert" -"color-name@1.1.3","MIT","https://github.com/dfcreative/color-name" -"color-name@1.1.4","MIT","https://github.com/colorjs/color-name" -"color-string@1.5.3","MIT","https://github.com/Qix-/color-string" -"color@3.0.0","MIT","https://github.com/Qix-/color" -"colornames@1.1.1","MIT","https://github.com/timoxley/colornames" -"colors@1.4.0","MIT","https://github.com/Marak/colors.js" -"colorspace@1.1.2","MIT","https://github.com/3rd-Eden/colorspace" -"combined-stream@1.0.8","MIT","https://github.com/felixge/node-combined-stream" -"commander@2.20.3","MIT","https://github.com/tj/commander.js" -"concat-map@0.0.1","MIT","https://github.com/substack/node-concat-map" -"console-control-strings@1.1.0","ISC","https://github.com/iarna/console-control-strings" -"content-disposition@0.5.3","MIT","https://github.com/jshttp/content-disposition" -"content-type@1.0.4","MIT","https://github.com/jshttp/content-type" -"cookie-signature@1.0.6","MIT","https://github.com/visionmedia/node-cookie-signature" -"cookie@0.4.0","MIT","https://github.com/jshttp/cookie" -"core-util-is@1.0.2","MIT","https://github.com/isaacs/core-util-is" -"crc-32@1.2.0","Apache-2.0","https://github.com/SheetJS/js-crc32" -"dashdash@1.14.1","MIT","https://github.com/trentm/node-dashdash" -"debug@2.6.9","MIT","https://github.com/visionmedia/debug" -"debug@3.2.6","MIT","https://github.com/visionmedia/debug" -"debug@4.1.1","MIT","https://github.com/visionmedia/debug" -"decamelize@1.2.0","MIT","https://github.com/sindresorhus/decamelize" -"decompress-response@5.0.0","MIT","https://github.com/sindresorhus/decompress-response" -"deep-extend@0.6.0","MIT","https://github.com/unclechu/node-deep-extend" -"defer-to-connect@2.0.0","MIT","https://github.com/szmarczak/defer-to-connect" -"define-properties@1.1.3","MIT","https://github.com/ljharb/define-properties" -"delay@4.3.0","MIT","https://github.com/sindresorhus/delay" -"delayed-stream@1.0.0","MIT","https://github.com/felixge/node-delayed-stream" -"delegates@1.0.0","MIT","https://github.com/visionmedia/node-delegates" -"depd@1.1.2","MIT","https://github.com/dougwilson/nodejs-depd" -"destroy@1.0.4","MIT","https://github.com/stream-utils/destroy" -"detect-libc@1.0.3","Apache-2.0","https://github.com/lovell/detect-libc" -"diagnostics@1.1.1","MIT","https://github.com/bigpipe/diagnostics" -"dotenv@6.2.0","BSD-2-Clause","https://github.com/motdotla/dotenv" -"duplexer3@0.1.4","BSD-3-Clause","https://github.com/floatdrop/duplexer3" -"ecc-jsbn@0.1.2","MIT","https://github.com/quartzjer/ecc-jsbn" -"ee-first@1.1.1","MIT","https://github.com/jonathanong/ee-first" -"emoji-regex@7.0.3","MIT","https://github.com/mathiasbynens/emoji-regex" -"enabled@1.0.2","MIT","https://github.com/bigpipe/enabled" -"encodeurl@1.0.2","MIT","https://github.com/pillarjs/encodeurl" -"end-of-stream@1.4.4","MIT","https://github.com/mafintosh/end-of-stream" -"env-variable@0.0.5","MIT","https://github.com/3rd-Eden/env-variable" -"error@7.0.2","MIT","https://github.com/Raynos/error" -"es-abstract@1.17.0","MIT","https://github.com/ljharb/es-abstract" -"es-to-primitive@1.2.1","MIT","https://github.com/ljharb/es-to-primitive" -"escape-html@1.0.3","MIT","https://github.com/component/escape-html" -"escape-string-regexp@1.0.5","MIT","https://github.com/sindresorhus/escape-string-regexp" -"esprima@4.0.1","BSD-2-Clause","https://github.com/jquery/esprima" -"etag@1.8.1","MIT","https://github.com/jshttp/etag" -"exit-on-epipe@1.0.1","Apache-2.0","https://github.com/SheetJS/node-exit-on-epipe" -"express-opentracing@0.1.1","Apache-2.0","https://github.com/opentracing-contrib/javascript-express" -"express-validator@6.4.0","MIT","https://github.com/express-validator/express-validator" -"express-winston@4.0.3","MIT","https://github.com/bithavoc/express-winston" -"express@4.17.1","MIT","https://github.com/expressjs/express" -"extend@3.0.2","MIT","https://github.com/justmoon/node-extend" -"extsprintf@1.3.0","MIT","https://github.com/davepacheco/node-extsprintf" -"fast-deep-equal@2.0.1","MIT","https://github.com/epoberezkin/fast-deep-equal" -"fast-json-stable-stringify@2.0.0","MIT","https://github.com/epoberezkin/fast-json-stable-stringify" -"fast-safe-stringify@2.0.7","MIT","https://github.com/davidmarkclements/fast-safe-stringify" -"fecha@2.3.3","MIT","git+https://taylorhakes@github.com/taylorhakes/fecha" -"figlet@1.2.4","MIT","https://github.com/patorjk/figlet.js" -"finalhandler@1.1.2","MIT","https://github.com/pillarjs/finalhandler" -"find-up@3.0.0","MIT","https://github.com/sindresorhus/find-up" -"forever-agent@0.6.1","Apache-2.0","https://github.com/mikeal/forever-agent" -"form-data@2.3.3","MIT","https://github.com/form-data/form-data" -"forwarded@0.1.2","MIT","https://github.com/jshttp/forwarded" -"fresh@0.5.2","MIT","https://github.com/jshttp/fresh" -"fs-minipass@1.2.7","ISC","https://github.com/npm/fs-minipass" -"fs.realpath@1.0.0","ISC","https://github.com/isaacs/fs.realpath" -"function-bind@1.1.1","MIT","https://github.com/Raynos/function-bind" -"gauge@2.7.4","ISC","https://github.com/iarna/gauge" -"get-caller-file@2.0.5","ISC","https://github.com/stefanpenner/get-caller-file" -"get-stream@5.1.0","MIT","https://github.com/sindresorhus/get-stream" -"getpass@0.1.7","MIT","https://github.com/arekinath/node-getpass" -"glob-option-error@1.0.0","MIT","https://github.com/shinnn/glob-option-error" -"glob@7.1.6","ISC","https://github.com/isaacs/node-glob" -"got@10.7.0","MIT","https://github.com/sindresorhus/got" -"graceful-fs@4.2.3","ISC","https://github.com/isaacs/node-graceful-fs" -"har-schema@2.0.0","ISC","https://github.com/ahmadnassri/har-schema" -"har-validator@5.1.3","MIT","https://github.com/ahmadnassri/node-har-validator" -"has-ansi@2.0.0","MIT","https://github.com/sindresorhus/has-ansi" -"has-flag@3.0.0","MIT","https://github.com/sindresorhus/has-flag" -"has-symbols@1.0.1","MIT","https://github.com/ljharb/has-symbols" -"has-unicode@2.0.1","ISC","https://github.com/iarna/has-unicode" -"has@1.0.3","MIT","https://github.com/tarruda/has" -"hexer@1.5.0","MIT","https://github.com/jcorbin/hexer" -"highlight.js@9.15.10","BSD-3-Clause","https://github.com/highlightjs/highlight.js" -"http-cache-semantics@4.0.3","BSD-2-Clause","https://github.com/kornelski/http-cache-semantics" -"http-errors@1.7.2","MIT","https://github.com/jshttp/http-errors" -"http-signature@1.2.0","MIT","https://github.com/joyent/node-http-signature" -"iconv-lite@0.4.24","MIT","https://github.com/ashtuchkin/iconv-lite" -"ieee754@1.1.13","BSD-3-Clause","https://github.com/feross/ieee754" -"ignore-walk@3.0.2","ISC","https://github.com/isaacs/ignore-walk" -"indexed-filter@1.0.3","ISC","https://github.com/shinnn/indexed-filter" -"inflight@1.0.6","ISC","https://github.com/npm/inflight" -"inherits@2.0.3","ISC","https://github.com/isaacs/inherits" -"ini@1.3.5","ISC","https://github.com/isaacs/ini" -"inspect-with-kind@1.0.5","ISC","https://github.com/shinnn/inspect-with-kind" -"ipaddr.js@1.9.0","MIT","https://github.com/whitequark/ipaddr.js" -"is-arrayish@0.3.2","MIT","https://github.com/qix-/node-is-arrayish" -"is-callable@1.1.5","MIT","https://github.com/ljharb/is-callable" -"is-date-object@1.0.1","MIT","https://github.com/ljharb/is-date-object" -"is-fullwidth-code-point@1.0.0","MIT","https://github.com/sindresorhus/is-fullwidth-code-point" -"is-fullwidth-code-point@2.0.0","MIT","https://github.com/sindresorhus/is-fullwidth-code-point" -"is-plain-obj@1.1.0","MIT","https://github.com/sindresorhus/is-plain-obj" -"is-regex@1.0.5","MIT","https://github.com/ljharb/is-regex" -"is-stream@1.1.0","MIT","https://github.com/sindresorhus/is-stream" -"is-symbol@1.0.2","MIT","https://github.com/ljharb/is-symbol" -"is-typedarray@1.0.0","MIT","https://github.com/hughsk/is-typedarray" -"isarray@1.0.0","MIT","https://github.com/juliangruber/isarray" -"isstream@0.1.2","MIT","https://github.com/rvagg/isstream" -"jaeger-client@3.17.2","Apache-2.0","https://github.com/jaegertracing/jaeger-client-node" -"js-yaml@3.13.1","MIT","https://github.com/nodeca/js-yaml" -"jsbn@0.1.1","MIT","https://github.com/andyperlitch/jsbn" -"json-buffer@3.0.1","MIT","https://github.com/dominictarr/json-buffer" -"json-schema-traverse@0.4.1","MIT","https://github.com/epoberezkin/json-schema-traverse" -"json-schema@0.2.3","AFLv2.1,BSD","https://github.com/kriszyp/json-schema" -"json-stringify-safe@5.0.1","ISC","https://github.com/isaacs/json-stringify-safe" -"json5@2.1.1","MIT","https://github.com/json5/json5" -"jsprim@1.4.1","MIT","https://github.com/joyent/node-jsprim" -"keyv@4.0.0","MIT","https://github.com/lukechilds/keyv" -"kind-of@6.0.2","MIT","https://github.com/jonschlinkert/kind-of" -"kuler@1.0.1","MIT","https://github.com/3rd-Eden/kuler" -"limiter@1.1.5","MIT","https://github.com/jhurliman/node-rate-limiter" -"locate-path@3.0.0","MIT","https://github.com/sindresorhus/locate-path" -"lodash@4.17.15","MIT","https://github.com/lodash/lodash" -"logform@2.1.2","MIT","https://github.com/winstonjs/logform" -"long@2.4.0","Apache-2.0","https://github.com/dcodeIO/Long.js" -"lowercase-keys@2.0.0","MIT","https://github.com/sindresorhus/lowercase-keys" -"lsif-protocol@0.4.3","MIT","https://github.com/Microsoft/lsif-typescript" -"media-typer@0.3.0","MIT","https://github.com/jshttp/media-typer" -"merge-descriptors@1.0.1","MIT","https://github.com/component/merge-descriptors" -"methods@1.1.2","MIT","https://github.com/jshttp/methods" -"mime-db@1.40.0","MIT","https://github.com/jshttp/mime-db" -"mime-types@2.1.24","MIT","https://github.com/jshttp/mime-types" -"mime@1.6.0","MIT","https://github.com/broofa/node-mime" -"mimic-response@1.0.1","MIT","https://github.com/sindresorhus/mimic-response" -"mimic-response@2.1.0","MIT","https://github.com/sindresorhus/mimic-response" -"minimatch@3.0.4","ISC","https://github.com/isaacs/minimatch" -"minimist@0.0.8","MIT","https://github.com/substack/minimist" -"minimist@1.2.0","MIT","https://github.com/substack/minimist" -"minipass@2.9.0","ISC","https://github.com/isaacs/minipass" -"minizlib@1.3.3","MIT","https://github.com/isaacs/minizlib" -"mkdirp@0.5.1","MIT","https://github.com/substack/node-mkdirp" -"ms@2.0.0","MIT","https://github.com/zeit/ms" -"ms@2.1.1","MIT","https://github.com/zeit/ms" -"mz@2.7.0","MIT","https://github.com/normalize/mz" -"nan@2.14.0","MIT","https://github.com/nodejs/nan" -"needle@2.4.0","MIT","https://github.com/tomas/needle" -"negotiator@0.6.2","MIT","https://github.com/jshttp/negotiator" -"node-int64@0.4.0","MIT","https://github.com/broofa/node-int64" -"node-pre-gyp@0.11.0","BSD-3-Clause","https://github.com/mapbox/node-pre-gyp" -"nopt@4.0.1","ISC","https://github.com/npm/nopt" -"normalize-url@4.5.0","MIT","https://github.com/sindresorhus/normalize-url" -"npm-bundled@1.0.6","ISC","https://github.com/npm/npm-bundled" -"npm-packlist@1.4.4","ISC","https://github.com/npm/npm-packlist" -"npmlog@4.1.2","ISC","https://github.com/npm/npmlog" -"number-is-nan@1.0.1","MIT","https://github.com/sindresorhus/number-is-nan" -"oauth-sign@0.9.0","Apache-2.0","https://github.com/mikeal/oauth-sign" -"object-assign@4.1.1","MIT","https://github.com/sindresorhus/object-assign" -"object-inspect@1.7.0","MIT","https://github.com/substack/object-inspect" -"object-keys@1.1.1","MIT","https://github.com/ljharb/object-keys" -"object.assign@4.1.0","MIT","https://github.com/ljharb/object.assign" -"object.getownpropertydescriptors@2.0.3","MIT","https://github.com/ljharb/object.getownpropertydescriptors" -"on-finished@2.3.0","MIT","https://github.com/jshttp/on-finished" -"once@1.4.0","ISC","https://github.com/isaacs/once" -"one-time@0.0.4","MIT","https://github.com/unshiftio/one-time" -"opentracing@0.13.0","MIT","https://github.com/opentracing/opentracing-javascript" -"opentracing@0.14.4","Apache-2.0","https://github.com/opentracing/opentracing-javascript" -"os-homedir@1.0.2","MIT","https://github.com/sindresorhus/os-homedir" -"os-tmpdir@1.0.2","MIT","https://github.com/sindresorhus/os-tmpdir" -"osenv@0.1.5","ISC","https://github.com/npm/osenv" -"p-cancelable@2.0.0","MIT","https://github.com/sindresorhus/p-cancelable" -"p-event@4.1.0","MIT","https://github.com/sindresorhus/p-event" -"p-finally@1.0.0","MIT","https://github.com/sindresorhus/p-finally" -"p-limit@2.2.2","MIT","https://github.com/sindresorhus/p-limit" -"p-locate@3.0.0","MIT","https://github.com/sindresorhus/p-locate" -"p-retry@4.2.0","MIT","https://github.com/sindresorhus/p-retry" -"p-timeout@2.0.1","MIT","https://github.com/sindresorhus/p-timeout" -"p-try@2.2.0","MIT","https://github.com/sindresorhus/p-try" -"packet-reader@1.0.0","MIT","https://github.com/brianc/node-packet-reader" -"parent-require@1.0.0","MIT","https://github.com/jaredhanson/node-parent-require" -"parse5@4.0.0","MIT","https://github.com/inikulin/parse5" -"parseurl@1.3.3","MIT","https://github.com/pillarjs/parseurl" -"path-exists@3.0.0","MIT","https://github.com/sindresorhus/path-exists" -"path-is-absolute@1.0.1","MIT","https://github.com/sindresorhus/path-is-absolute" -"path-to-regexp@0.1.7","MIT","https://github.com/component/path-to-regexp" -"performance-now@2.1.0","MIT","https://github.com/braveg1rl/performance-now" -"pg-connection-string@0.1.3","MIT","https://github.com/iceddev/pg-connection-string" -"pg-int8@1.0.1","ISC","https://github.com/charmander/pg-int8" -"pg-packet-stream@1.1.0","MIT","" -"pg-pool@2.0.10","MIT","https://github.com/brianc/node-pg-pool" -"pg-types@2.2.0","MIT","https://github.com/brianc/node-pg-types" -"pg@7.18.2","MIT","https://github.com/brianc/node-postgres" -"pgpass@1.0.2","MIT","https://github.com/hoegaarden/pgpass" -"postgres-array@2.0.0","MIT","https://github.com/bendrucker/postgres-array" -"postgres-bytea@1.0.0","MIT","https://github.com/bendrucker/postgres-bytea" -"postgres-date@1.0.4","MIT","https://github.com/bendrucker/postgres-date" -"postgres-interval@1.2.0","MIT","https://github.com/bendrucker/postgres-interval" -"printj@1.1.2","Apache-2.0","https://github.com/SheetJS/printj" -"process-nextick-args@2.0.1","MIT","https://github.com/calvinmetcalf/process-nextick-args" -"process@0.10.1","MIT*","https://github.com/shtylman/node-process" -"prom-client@12.0.0","Apache-2.0","https://github.com/siimon/prom-client" -"proxy-addr@2.0.5","MIT","https://github.com/jshttp/proxy-addr" -"psl@1.7.0","MIT","https://github.com/lupomontero/psl" -"pump@3.0.0","MIT","https://github.com/mafintosh/pump" -"punycode@1.4.1","MIT","https://github.com/bestiejs/punycode.js" -"punycode@2.1.1","MIT","https://github.com/bestiejs/punycode.js" -"qs@6.5.2","BSD-3-Clause","https://github.com/ljharb/qs" -"qs@6.7.0","BSD-3-Clause","https://github.com/ljharb/qs" -"range-parser@1.2.1","MIT","https://github.com/jshttp/range-parser" -"raw-body@2.4.0","MIT","https://github.com/stream-utils/raw-body" -"rc@1.2.8","(BSD-2-Clause OR MIT OR Apache-2.0)","https://github.com/dominictarr/rc" -"readable-stream@2.3.6","MIT","https://github.com/nodejs/readable-stream" -"readable-stream@3.4.0","MIT","https://github.com/nodejs/readable-stream" -"reflect-metadata@0.1.13","Apache-2.0","https://github.com/rbuckton/reflect-metadata" -"relateurl@0.2.7","MIT","https://github.com/stevenvachon/relateurl" -"request@2.88.0","Apache-2.0","https://github.com/request/request" -"require-directory@2.1.1","MIT","https://github.com/troygoode/node-require-directory" -"require-main-filename@2.0.0","ISC","https://github.com/yargs/require-main-filename" -"responselike@2.0.0","MIT","https://github.com/lukechilds/responselike" -"retry@0.12.0","MIT","https://github.com/tim-kos/node-retry" -"rimraf@2.7.1","ISC","https://github.com/isaacs/rimraf" -"rmfr@2.0.0","ISC","https://github.com/shinnn/rmfr" -"safe-buffer@5.1.2","MIT","https://github.com/feross/safe-buffer" -"safer-buffer@2.1.2","MIT","https://github.com/ChALkeR/safer-buffer" -"sax@1.2.4","ISC","https://github.com/isaacs/sax-js" -"semver@4.3.2","BSD*","https://github.com/npm/node-semver" -"semver@5.7.1","ISC","https://github.com/npm/node-semver" -"send@0.17.1","MIT","https://github.com/pillarjs/send" -"serve-static@1.14.1","MIT","https://github.com/expressjs/serve-static" -"set-blocking@2.0.0","ISC","https://github.com/yargs/set-blocking" -"setprototypeof@1.1.1","ISC","https://github.com/wesleytodd/setprototypeof" -"sha.js@2.4.11","(MIT AND BSD-3-Clause)","https://github.com/crypto-browserify/sha.js" -"signal-exit@3.0.2","ISC","https://github.com/tapjs/signal-exit" -"simple-swizzle@0.2.2","MIT","https://github.com/qix-/node-simple-swizzle" -"split@1.0.1","MIT","https://github.com/dominictarr/split" -"sprintf-js@1.0.3","BSD-3-Clause","https://github.com/alexei/sprintf.js" -"sqlite3@4.1.1","BSD-3-Clause","https://github.com/mapbox/node-sqlite3" -"sshpk@1.16.1","MIT","https://github.com/joyent/node-sshpk" -"stack-trace@0.0.10","MIT","https://github.com/felixge/node-stack-trace" -"statuses@1.5.0","MIT","https://github.com/jshttp/statuses" -"stream-throttle@0.1.3","BSD-3-Clause","https://github.com/tjgq/node-stream-throttle" -"string-template@0.2.1","MIT","https://github.com/Matt-Esch/string-template" -"string-width@1.0.2","MIT","https://github.com/sindresorhus/string-width" -"string-width@3.1.0","MIT","https://github.com/sindresorhus/string-width" -"string.prototype.trimleft@2.1.1","MIT","https://github.com/es-shims/String.prototype.trimLeft" -"string.prototype.trimright@2.1.1","MIT","https://github.com/es-shims/String.prototype.trimRight" -"string_decoder@1.1.1","MIT","https://github.com/nodejs/string_decoder" -"strip-ansi@3.0.1","MIT","https://github.com/chalk/strip-ansi" -"strip-ansi@5.2.0","MIT","https://github.com/chalk/strip-ansi" -"strip-json-comments@2.0.1","MIT","https://github.com/sindresorhus/strip-json-comments" -"supports-color@2.0.0","MIT","https://github.com/chalk/supports-color" -"supports-color@5.5.0","MIT","https://github.com/chalk/supports-color" -"tar@4.4.13","ISC","https://github.com/npm/node-tar" -"tdigest@0.1.1","MIT","https://github.com/welch/tdigest" -"text-hex@1.0.0","MIT","https://github.com/3rd-Eden/text-hex" -"thenify-all@1.6.0","MIT","https://github.com/thenables/thenify-all" -"thenify@3.3.0","MIT","https://github.com/thenables/thenify" -"thriftrw@3.11.3","MIT","https://github.com/thriftrw/thriftrw-node" -"through@2.3.8","MIT","https://github.com/dominictarr/through" -"to-readable-stream@2.1.0","MIT","https://github.com/sindresorhus/to-readable-stream" -"toidentifier@1.0.0","MIT","https://github.com/component/toidentifier" -"tough-cookie@2.4.3","BSD-3-Clause","https://github.com/salesforce/tough-cookie" -"triple-beam@1.3.0","MIT","https://github.com/winstonjs/triple-beam" -"tslib@1.10.0","Apache-2.0","https://github.com/Microsoft/tslib" -"tunnel-agent@0.6.0","Apache-2.0","https://github.com/mikeal/tunnel-agent" -"tweetnacl@0.14.5","Unlicense","https://github.com/dchest/tweetnacl-js" -"type-fest@0.10.0","(MIT OR CC0-1.0)","https://github.com/sindresorhus/type-fest" -"type-is@1.6.18","MIT","https://github.com/jshttp/type-is" -"typeorm@0.2.24","MIT","https://github.com/typeorm/typeorm" -"unpipe@1.0.0","MIT","https://github.com/stream-utils/unpipe" -"uri-js@4.2.2","BSD-2-Clause","https://github.com/garycourt/uri-js" -"util-deprecate@1.0.2","MIT","https://github.com/TooTallNate/util-deprecate" -"util.promisify@1.0.0","MIT","https://github.com/ljharb/util.promisify" -"utils-merge@1.0.1","MIT","https://github.com/jaredhanson/utils-merge" -"uuid@3.4.0","MIT","https://github.com/uuidjs/uuid" -"uuid@7.0.3","MIT","https://github.com/uuidjs/uuid" -"validate-glob-opts@1.0.2","ISC","https://github.com/shinnn/validate-glob-opts" -"validator@12.2.0","MIT","https://github.com/chriso/validator.js" -"vary@1.1.2","MIT","https://github.com/jshttp/vary" -"verror@1.10.0","MIT","https://github.com/davepacheco/node-verror" -"vscode-jsonrpc@5.0.1","MIT","https://github.com/Microsoft/vscode-languageserver-node" -"vscode-languageserver-protocol@3.15.3","MIT","https://github.com/Microsoft/vscode-languageserver-node" -"vscode-languageserver-types@3.15.1","MIT","https://github.com/Microsoft/vscode-languageserver-node" -"vscode-languageserver@6.1.1","MIT","https://github.com/Microsoft/vscode-languageserver-node" -"which-module@2.0.0","ISC","https://github.com/nexdrew/which-module" -"wide-align@1.1.3","ISC","https://github.com/iarna/wide-align" -"winston-transport@4.3.0","MIT","https://github.com/winstonjs/winston-transport" -"winston@3.2.1","MIT","https://github.com/winstonjs/winston" -"wrap-ansi@5.1.0","MIT","https://github.com/chalk/wrap-ansi" -"wrappy@1.0.2","ISC","https://github.com/npm/wrappy" -"xml2js@0.4.22","MIT","https://github.com/Leonidas-from-XIV/node-xml2js" -"xmlbuilder@11.0.1","MIT","https://github.com/oozcitak/xmlbuilder-js" -"xorshift@0.2.1","MIT","https://github.com/AndreasMadsen/xorshift" -"xtend@4.0.2","MIT","https://github.com/Raynos/xtend" -"y18n@4.0.0","ISC","https://github.com/yargs/y18n" -"yallist@3.1.1","ISC","https://github.com/isaacs/yallist" -"yallist@4.0.0","ISC","https://github.com/isaacs/yallist" -"yargonaut@1.1.4","Apache-2.0","https://github.com/nexdrew/yargonaut" -"yargs-parser@13.1.1","ISC","https://github.com/yargs/yargs-parser" -"yargs@13.3.0","MIT","https://github.com/yargs/yargs" diff --git a/babel.config.js b/babel.config.js index 3b0c7dcef41..b63bfe740d9 100644 --- a/babel.config.js +++ b/babel.config.js @@ -34,8 +34,6 @@ module.exports = api => { ], plugins: [ 'babel-plugin-lodash', - // Required to support typeorm decorators in ./cmd/precise-code-intel - ['@babel/plugin-proposal-decorators', { legacy: true }], // Node 12 (released 2019 Apr 23) supports these natively, but there seem to be issues when used with TypeScript. ['@babel/plugin-proposal-class-properties', { loose: true }], ], diff --git a/cmd/precise-code-intel/.eslintrc.js b/cmd/precise-code-intel/.eslintrc.js deleted file mode 100644 index d0e41b94f7b..00000000000 --- a/cmd/precise-code-intel/.eslintrc.js +++ /dev/null @@ -1,15 +0,0 @@ -const baseConfig = require('../../.eslintrc') -module.exports = { - extends: '../../.eslintrc.js', - parserOptions: { - ...baseConfig.parserOptions, - project: 'tsconfig.json', - }, - rules: { - 'no-console': ['error'], - 'import/no-cycle': ['error'], - 'no-return-await': ['error'], - 'no-shadow': ['error', { allow: ['ctx'] }], - }, - overrides: baseConfig.overrides, -} diff --git a/cmd/precise-code-intel/README.md b/cmd/precise-code-intel/README.md deleted file mode 100644 index bb380de4870..00000000000 --- a/cmd/precise-code-intel/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Precise code intelligence system - -This project is an early adopter of Microsoft's [LSIF](https://code.visualstudio.com/blogs/2019/02/19/lsif) standard. LSIF (Language Server Index Format) is a format used to store the results of language server queries that are computed ahead-of-time. We uses this format to provide jump-to-definition, find-reference, and hover docstring functionality. - -## LSIF code intelligence - -LSIF dumps are generated by running an LSIF indexer in a build or continuous integration environment. The dump is uploaded to a Sourcegraph instance via [Sourcegraph CLI](https://github.com/sourcegraph/src-cli). An LSIF API server, proxied by the frontend for auth, answers relevant LSP queries to provide fast and precise code intelligence. - -## Architecture - -This project is split into three parts, all currently written in TypeScript. These parts are deployable independently but can also run in the same docker container to simplify deployment with docker-compose. - -- The [API server](./src/api-server/api.ts) receives LSIF uploads and answers LSP queries via HTTP. -- The [bundle-manager](./src/bundle-manager/manager.ts) answers queries about a particular upload by looking at relevant SQLite databases on-disk. -- The [worker](./src/worker/worker.ts) dequeues unconverted LSIF uploads from Postgres and converts them into SQLite databases that can be queried by the server. - -The `api-server`, `bundle-manager`, and `worker` directories contain only instructions to build Docker images for the three entrypoints. - -## Documentation - -- Usage documentation is provided on [Sourcegraph.com](https://docs.sourcegraph.com/user/code_intelligence/lsif). -- API endpoint documentation is provided in [api.md](./docs/api/api.md). -- Database configuration and migrations are described in [database.md](./docs/database/database.md). -- Data models are described in [datamodel.md](./docs/database/datamodel.md) and [datamodel.pg.md](./docs/database/datamodel.pg.md). diff --git a/cmd/precise-code-intel/api-server/Dockerfile b/cmd/precise-code-intel/api-server/Dockerfile deleted file mode 100644 index 8c25c97a1ae..00000000000 --- a/cmd/precise-code-intel/api-server/Dockerfile +++ /dev/null @@ -1,45 +0,0 @@ -FROM alpine:3.10@sha256:e4355b66995c96b4b468159fc5c7e3540fcef961189ca13fee877798649f531a AS precise-code-intel-builder - -RUN apk add --no-cache \ - nodejs-current=12.4.0-r0 \ - nodejs-npm=10.19.0-r0 - -RUN npm install -g yarn@1.17.3 - -COPY precise-code-intel/package.json precise-code-intel/yarn.lock precise-code-intel/tsconfig.json /precise-code-intel/ -RUN yarn --cwd /precise-code-intel --frozen-lockfile -COPY precise-code-intel/src /precise-code-intel/src -RUN yarn --cwd /precise-code-intel run build -# Remove all devDependencies -RUN yarn --cwd /precise-code-intel --frozen-lockfile --production - -FROM sourcegraph/alpine:3.10@sha256:4d05cd5669726fc38823e92320659a6d1ef7879e62268adec5df658a0bacf65c - -ARG COMMIT_SHA="unknown" -ARG DATE="unknown" -ARG VERSION="unknown" - -LABEL org.opencontainers.image.revision=${COMMIT_SHA} -LABEL org.opencontainers.image.created=${DATE} -LABEL org.opencontainers.image.version=${VERSION} -LABEL com.sourcegraph.github.url=https://github.com/sourcegraph/sourcegraph/commit/${COMMIT_SHA} - -# hadolint ignore=DL3018 -RUN apk update && apk add --no-cache \ - nodejs-current=12.4.0-r0 \ - tini - -# Ensures that a directory with the correct permissions exist in the image. Without this, in Docker Compose -# deployments the Docker daemon would first create the volume directory and it would be owned by `root` and -# then one of the precise-code-intel processes would be unable to create the `/lsif-storage` because it -# would be trying to do so in a directory owned by `root` as the user `sourcegraph`. And no, this is not -# dumb, this is just Docker: https://github.com/docker/compose/issues/3270#issuecomment-363478501. -USER root -RUN mkdir -p /lsif-storage && chown -R sourcegraph:sourcegraph /lsif-storage -USER sourcegraph - -COPY --from=precise-code-intel-builder /precise-code-intel /precise-code-intel - -EXPOSE 3186 -ENV LOG_LEVEL=debug -ENTRYPOINT ["/sbin/tini", "--", "node", "/precise-code-intel/out/api-server/api.js"] diff --git a/cmd/precise-code-intel/api-server/build.sh b/cmd/precise-code-intel/api-server/build.sh deleted file mode 100755 index 7219166f7ff..00000000000 --- a/cmd/precise-code-intel/api-server/build.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -cd "$(dirname "${BASH_SOURCE[0]}")/../../.." -set -eux - -OUTPUT=$(mktemp -d -t sgdockerbuild_XXXXXXX) -cleanup() { - rm -rf "$OUTPUT" -} -trap cleanup EXIT - -# Environment for building linux binaries -export GO111MODULE=on -export GOARCH=amd64 -export GOOS=linux -export CGO_ENABLED=0 - -cp -a ./cmd/precise-code-intel "$OUTPUT" - -echo "--- docker build" -docker build -f cmd/precise-code-intel/api-server/Dockerfile -t "$IMAGE" "$OUTPUT" \ - --progress=plain \ - --build-arg COMMIT_SHA \ - --build-arg DATE \ - --build-arg VERSION diff --git a/cmd/precise-code-intel/babel.config.js b/cmd/precise-code-intel/babel.config.js deleted file mode 100644 index 5b5dc53092f..00000000000 --- a/cmd/precise-code-intel/babel.config.js +++ /dev/null @@ -1,8 +0,0 @@ -// @ts-check - -/** @type {import('@babel/core').TransformOptions} */ -const config = { - extends: '../../babel.config.js', -} - -module.exports = config diff --git a/cmd/precise-code-intel/bundle-manager/Dockerfile b/cmd/precise-code-intel/bundle-manager/Dockerfile deleted file mode 100644 index 4ddf1475d13..00000000000 --- a/cmd/precise-code-intel/bundle-manager/Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -FROM alpine:3.10@sha256:e4355b66995c96b4b468159fc5c7e3540fcef961189ca13fee877798649f531a AS precise-code-intel-builder - -RUN apk add --no-cache \ - nodejs-current=12.4.0-r0 \ - nodejs-npm=10.19.0-r0 - -RUN npm install -g yarn@1.17.3 - -COPY precise-code-intel/package.json precise-code-intel/yarn.lock precise-code-intel/tsconfig.json /precise-code-intel/ -RUN yarn --cwd /precise-code-intel --frozen-lockfile -COPY precise-code-intel/src /precise-code-intel/src -RUN yarn --cwd /precise-code-intel run build -# Remove all devDependencies -RUN yarn --cwd /precise-code-intel --frozen-lockfile --production - -FROM sourcegraph/alpine:3.10@sha256:4d05cd5669726fc38823e92320659a6d1ef7879e62268adec5df658a0bacf65c - -ARG COMMIT_SHA="unknown" -ARG DATE="unknown" -ARG VERSION="unknown" - -LABEL org.opencontainers.image.revision=${COMMIT_SHA} -LABEL org.opencontainers.image.created=${DATE} -LABEL org.opencontainers.image.version=${VERSION} -LABEL com.sourcegraph.github.url=https://github.com/sourcegraph/sourcegraph/commit/${COMMIT_SHA} - -# hadolint ignore=DL3018 -RUN apk update && apk add --no-cache \ - nodejs-current=12.4.0-r0 \ - tini - -# Ensures that a directory with the correct permissions exist in the image. Without this, in Docker Compose -# deployments the Docker daemon would first create the volume directory and it would be owned by `root` and -# then one of the precise-code-intel processes would be unable to create the `/lsif-storage` because it -# would be trying to do so in a directory owned by `root` as the user `sourcegraph`. And no, this is not -# dumb, this is just Docker: https://github.com/docker/compose/issues/3270#issuecomment-363478501. -USER root -RUN mkdir -p /lsif-storage && chown -R sourcegraph:sourcegraph /lsif-storage -USER sourcegraph - -COPY --from=precise-code-intel-builder /precise-code-intel /precise-code-intel - -EXPOSE 3187 -VOLUME ["/lsif-storage"] -ENV LOG_LEVEL=debug -ENTRYPOINT ["/sbin/tini", "--", "node", "/precise-code-intel/out/bundle-manager/manager.js"] diff --git a/cmd/precise-code-intel/bundle-manager/build.sh b/cmd/precise-code-intel/bundle-manager/build.sh deleted file mode 100755 index 541ba54dbd4..00000000000 --- a/cmd/precise-code-intel/bundle-manager/build.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -cd "$(dirname "${BASH_SOURCE[0]}")/../../.." -set -eux - -OUTPUT=$(mktemp -d -t sgdockerbuild_XXXXXXX) -cleanup() { - rm -rf "$OUTPUT" -} -trap cleanup EXIT - -# Environment for building linux binaries -export GO111MODULE=on -export GOARCH=amd64 -export GOOS=linux -export CGO_ENABLED=0 - -cp -a ./cmd/precise-code-intel "$OUTPUT" - -echo "--- docker build" -docker build -f cmd/precise-code-intel/bundle-manager/Dockerfile -t "$IMAGE" "$OUTPUT" \ - --progress=plain \ - --build-arg COMMIT_SHA \ - --build-arg DATE \ - --build-arg VERSION diff --git a/cmd/precise-code-intel/docs/api/api.md b/cmd/precise-code-intel/docs/api/api.md deleted file mode 100644 index e290fbe230c..00000000000 --- a/cmd/precise-code-intel/docs/api/api.md +++ /dev/null @@ -1,15 +0,0 @@ -# LSIF API server endpoints - -The LSIF API server endpoints are documented as an [OpenAPI v3](https://swagger.io/docs/specification/about/) document [api.yaml](./api.yaml). This document can be viewed locally via docker by running the following command from this directory (or a parent directory if the host path supplied to `-v` changes accordingly). - -```bash -docker run \ - -e SWAGGER_JSON=/data/api.yaml \ - -p 8080:8080 \ - -v `pwd`:/data \ - swaggerapi/swagger-ui -``` - -The OpenAPI document assumes that the LSIF API server is running locally on port 3186 in order to make sample requests. - -This API should **not** be directly accessible outside of development environments. The endpoints of this API are not authenticated and relies on the Sourcegraph frontend to proxy requests via the HTTP or GraphQL server. diff --git a/cmd/precise-code-intel/docs/api/api.yaml b/cmd/precise-code-intel/docs/api/api.yaml deleted file mode 100644 index a89fb2c7d9a..00000000000 --- a/cmd/precise-code-intel/docs/api/api.yaml +++ /dev/null @@ -1,628 +0,0 @@ -openapi: 3.0.0 -info: - title: LSIF API Server - description: An internal Sourcegraph microservice that serves LSIF-powered code intelligence. - version: 1.0.0 - contact: - name: Eric Fritz - email: eric@sourcegraph.com - url: https://sourcegraph.com -servers: - - url: http://localhost:3186 -tags: - - name: LSIF - description: LSIF operations - - name: Uploads - description: Upload operations - - name: Internal - description: Internal operations -paths: - /upload: - post: - description: Upload LSIF data for a particular commit and directory. Exactly one file must be uploaded, and it is assumed to be the gzipped output of an LSIF indexer. - tags: - - LSIF - requestBody: - content: - application/octet-stream: - schema: - type: string - format: binary - parameters: - - name: repositoryId - in: query - description: The repository identifier. - required: true - schema: - type: number - - name: commit - in: query - description: The 40-character commit hash. - required: true - schema: - type: string - - name: root - in: query - description: The path to the directory associated with the upload, relative to the repository root. - example: cmd/project1 - required: false - schema: - type: string - - name: indexerName - in: query - description: The name of the indexer that generated the payload. This is required only if there is no tool info supplied in the payload's metadata vertex. - required: false - schema: - type: string - responses: - '200': - description: Processed (synchronously) - content: - application/json: - schema: - $ref: '#/components/schemas/EnqueueResponse' - '202': - description: Accepted - content: - application/json: - schema: - $ref: '#/components/schemas/EnqueueResponse' - /exists: - get: - description: Determine if LSIF data exists for a file within a particular commit. This endpoint will return the LSIF upload for which definitions, references, and hover queries will use. - tags: - - LSIF - parameters: - - name: repositoryId - in: query - description: The repository identifier. - required: true - schema: - type: number - - name: commit - in: query - description: The 40-character commit hash. - required: true - schema: - type: string - - name: path - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - - $ref: '#/components/schemas/Uploads' - post: - description: Determine if LSIF data exists for a file within a particular commit. This endpoint will return true if there is a nearby commit (direct ancestor or descendant) with LSIF data for the same file if the exact commit does not have available LSIF data. - tags: - - LSIF - parameters: - - name: repositoryId - in: query - description: The repository identifier. - required: true - schema: - type: number - - name: commit - in: query - description: The 40-character commit hash. - required: true - schema: - type: string - - name: file - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - type: boolean - /definitions: - get: - description: Get definitions for the symbol at a source position. - tags: - - LSIF - parameters: - - name: repositoryId - in: query - description: The repository identifier. - required: true - schema: - type: number - - name: commit - in: query - description: The 40-character commit hash. - required: true - schema: - type: number - - name: path - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - - name: line - in: query - description: The line index (zero-indexed). - required: true - schema: - type: number - - name: character - in: query - description: The character index (zero-indexed). - required: true - schema: - type: number - - name: uploadId - in: query - description: The identifier of the upload to load. If not supplied, the upload nearest to the given commit will be loaded. - required: true - schema: - type: number - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/Locations' - '404': - description: Not found - /references: - get: - description: Get references for the symbol at a source position. - tags: - - LSIF - parameters: - - name: repositoryId - in: query - description: The repository identifier. - required: true - schema: - type: number - - name: commit - in: query - description: The 40-character commit hash. - required: true - schema: - type: number - - name: path - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - - name: line - in: query - description: The line index (zero-indexed). - required: true - schema: - type: number - - name: character - in: query - description: The character index (zero-indexed). - required: true - schema: - type: number - - name: uploadId - in: query - description: The identifier of the upload to load. If not supplied, the upload nearest to the given commit will be loaded. - required: true - schema: - type: number - - name: limit - in: query - description: The maximum number of locations to return in one page. - required: false - schema: - type: number - default: 10 - - name: cursor - in: query - description: The end cursor given in the response of a previous page. - required: false - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/Locations' - headers: - Link: - description: If there are more results, this header includes the URL of the next page with relation type *next*. See [RFC 5988](https://tools.ietf.org/html/rfc5988). - schema: - type: string - '404': - description: Not found - /hover: - get: - description: Get hover data for the symbol at a source position. - tags: - - LSIF - parameters: - - name: repositoryId - in: query - description: The repository identifier. - required: true - schema: - type: number - - name: commit - in: query - description: The 40-character commit hash. - required: true - schema: - type: number - - name: path - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - - name: line - in: query - description: The line index (zero-indexed). - required: true - schema: - type: number - - name: character - in: query - description: The character index (zero-indexed). - required: true - schema: - type: number - - name: uploadId - in: query - description: The identifier of the upload to load. If not supplied, the upload nearest to the given commit will be loaded. - required: true - schema: - type: number - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/Hover' - '404': - description: Not found - /uploads/repositories/{repositoryId}: - get: - description: Get LSIF uploads for a repository. - tags: - - Uploads - parameters: - - name: repositoryId - in: query - description: The repository identifier. - required: true - schema: - type: number - - name: query - in: query - description: A search query applied over commit, root, failure reason, and failure stacktrace properties. - required: false - schema: - type: string - - name: state - in: query - description: The target upload state. - required: true - schema: - type: string - enum: - - processing - - errored - - completed - - queued - - name: visibleAtTip - in: query - description: If true, only show uploads visible at tip. - required: false - schema: - type: boolean - - name: limit - in: query - description: The maximum number of uploads to return in one page. - required: false - schema: - type: number - default: 50 - - name: offset - in: query - description: The number of uploads seen on previous pages. - required: false - schema: - type: number - default: 0 - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedUploads' - headers: - Link: - description: If there are more results, this header includes the URL of the next page with relation type *next*. See [RFC 5988](https://tools.ietf.org/html/rfc5988). - schema: - type: string - /uploads/{id}: - get: - description: Get an LSIF upload by its identifier. - tags: - - Uploads - parameters: - - name: id - in: path - description: The upload identifier. - required: true - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/Upload' - '404': - description: Not Found - delete: - description: Delete an LSIF upload by its identifier. - tags: - - Uploads - parameters: - - name: id - in: path - description: The upload identifier. - required: true - schema: - type: string - responses: - '200': - description: No Content - '404': - description: Not Found - /states: - get: - description: Retrieve the state of a set of uploads by identifier. - tags: - - Internal - requestBody: - content: - application/json: - schema: - type: object - properties: - ids: - description: The upload identifier list. - type: array - items: - type: number - additionalProperties: false - required: - - ids - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - type: - type: string - const: 'map' - values: - description: A list of key/value pairs. - type: array - items: - description: A key/value pair. - type: array - items: - oneOf: - - type: number - - type: string - additionalProperties: false - required: - - type - - values - /prune: - post: - description: Remove the oldest prunable dump. - tags: - - Internal - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - id: - description: The identifier of the pruned dump. - type: number - additionalProperties: false - required: - - id - nullable: true -components: - schemas: - Position: - type: object - description: A cursor position in a source file. - properties: - line: - type: number - description: The (zero-index) line index. - character: - type: number - description: The (zero-index) character index. - required: - - line - - character - additionalProperties: false - Range: - type: object - description: A half-open range of positions in a source file. - properties: - start: - $ref: '#/components/schemas/Position' - end: - $ref: '#/components/schemas/Position' - required: - - start - - end - additionalProperties: false - Location: - type: object - description: A position in a file of a code base. - properties: - repositoryId: - type: number - description: The identifier of the repository in which the location occurs. - commit: - type: string - description: The commit in which the location occurs. - path: - type: string - description: The root-relative path to the file. - range: - $ref: '#/components/schemas/Range' - required: - - repositoryId - - commit - - path - - range - additionalProperties: false - Locations: - type: array - description: A list of definition or reference locations. - items: - $ref: '#/components/schemas/Location' - Hover: - type: object - description: The text associated with a position in a source file. - properties: - text: - type: string - description: The raw hover text. - required: - - text - additionalProperties: false - EnqueueResponse: - type: object - description: A payload indicating the enqueued upload. - properties: - id: - type: number - description: The upload identifier. - required: - - id - additionalProperties: false - Uploads: - type: object - description: A wrapper for a list of uploads. - properties: - uploads: - type: array - description: A list of uploads. - items: - $ref: '#/components/schemas/Upload' - PaginatedUploads: - type: object - description: A paginated wrapper for a list of uploads. - properties: - uploads: - type: array - description: A list of uploads with a particular state. - items: - $ref: '#/components/schemas/Upload' - totalCount: - type: number - description: The total number of uploads in this set of results. - required: - - uploads - additionalProperties: false - Upload: - type: object - description: An LSIF upload. - properties: - id: - type: number - description: A unique identifier. - repositoryId: - type: number - description: The repository identifier argument given on upload. - commit: - type: string - description: The commit argument given on upload. - root: - type: string - description: The root argument given on upload. - indexer: - type: string - description: The name of the indexer given on upload. - filename: - type: string - description: The filename where the upload was stored before conversion. - state: - type: string - description: The upload's current state. - enum: - - processing - - errored - - completed - - queued - failureSummary: - type: string - description: A brief description of why the upload conversion failed. - failureStacktrace: - type: string - description: The stacktrace of the upload error. - uploadedAt: - type: string - description: An RFC3339-formatted time that the upload was uploaded. - startedAt: - type: string - description: An RFC3339-formatted time that the conversion started. - nullable: true - finishedAt: - type: string - description: An RFC3339-formatted time that the conversion completed or errored. - nullable: true - visibleAtTip: - type: boolean - description: Whether or not this upload can provide global reference code intelligence. - placeInQueue: - type: number - description: The rank of this upload in the queue. The value of this field is null if the upload has been processed. - nullable: true - required: - - id - - repositoryId - - commit - - root - - filename - - state - - failureSummary - - failureStacktrace - - uploadedAt - - startedAt - - finishedAt - additionalProperties: false diff --git a/cmd/precise-code-intel/docs/api/manager.yaml b/cmd/precise-code-intel/docs/api/manager.yaml deleted file mode 100644 index 5110cfedb04..00000000000 --- a/cmd/precise-code-intel/docs/api/manager.yaml +++ /dev/null @@ -1,462 +0,0 @@ -openapi: 3.0.0 -info: - title: LSIF Bundle Manager - description: An internal Sourcegraph microservice that serves LSIF-powered code intelligence for a single processed dump. - version: 1.0.0 - contact: - name: Eric Fritz - email: eric@sourcegraph.com - url: https://sourcegraph.com -servers: - - url: http://localhost:3187 -tags: - - name: Uploads - description: Upload operations - - name: Query - description: Query operations -paths: - /uploads/{id}: - get: - description: Retrieve raw LSIF content. - tags: - - Uploads - parameters: - - name: id - in: query - description: The upload identifier. - required: true - schema: - type: number - responses: - '200': - description: OK - content: - application/octet-stream: - schema: - type: string - format: binary - post: - description: Upload raw LSIF content. - tags: - - Uploads - parameters: - - name: id - in: query - description: The upload identifier. - required: true - schema: - type: number - requestBody: - content: - application/octet-stream: - schema: - type: string - format: binary - responses: - '200': - description: OK - /dbs/{id}: - post: - description: Upload a processed LSIF database. - tags: - - Uploads - parameters: - - name: id - in: query - description: The database identifier. - required: true - schema: - type: number - requestBody: - content: - application/octet-stream: - schema: - type: string - format: binary - responses: - '200': - description: OK - /dbs/{id}/exists: - get: - description: Determine if a file path exists in the given database. - tags: - - Query - parameters: - - name: id - in: query - description: The database identifier. - required: true - schema: - type: number - - name: path - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ExistsResponse' - /dbs/{id}/definitions: - get: - description: Retrieve a list of definition locations for a position in the given database. - tags: - - Query - parameters: - - name: id - in: query - description: The database identifier. - required: true - schema: - type: number - - name: path - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - - name: line - in: query - description: The line index (zero-indexed). - required: true - schema: - type: number - - name: character - in: query - description: The character index (zero-indexed). - required: true - schema: - type: number - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/DefinitionsResponse' - /dbs/{id}/references: - get: - description: Retrieve a list of reference locations for a position in the given database. - tags: - - Query - parameters: - - name: id - in: query - description: The database identifier. - required: true - schema: - type: number - - name: path - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - - name: line - in: query - description: The line index (zero-indexed). - required: true - schema: - type: number - - name: character - in: query - description: The character index (zero-indexed). - required: true - schema: - type: number - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ReferencesResponse' - /dbs/{id}/hover: - get: - description: Retrieve hover data for a position in the given database. - tags: - - Query - parameters: - - name: id - in: query - description: The database identifier. - required: true - schema: - type: number - - name: path - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - - name: line - in: query - description: The line index (zero-indexed). - required: true - schema: - type: number - - name: character - in: query - description: The character index (zero-indexed). - required: true - schema: - type: number - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/HoverResponse' - /dbs/{id}/monikersByPosition: - get: - description: Retrieve a list of monikers for a position in the given database. - tags: - - Query - parameters: - - name: id - in: query - description: The database identifier. - required: true - schema: - type: number - - name: path - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - - name: line - in: query - description: The line index (zero-indexed). - required: true - schema: - type: number - - name: character - in: query - description: The character index (zero-indexed). - required: true - schema: - type: number - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/MonikersByPositionResponse' - /dbs/{id}/monikerResults: - get: - description: Retrieve a list of locations associated with the given moniker in the given database. - tags: - - Query - parameters: - - name: id - in: query - description: The database identifier. - required: true - schema: - type: number - - name: modelType - in: query - description: The type of query. - required: true - schema: - type: string - enum: - - definition - - reference - - name: scheme - in: query - description: The moniker scheme. - required: true - schema: - type: string - - name: identifier - in: query - description: The moniker identifier. - required: true - schema: - type: string - - name: skip - in: query - description: The number of results to skip. - required: false - schema: - type: number - - name: take - in: query - description: The maximum number of results to return. - required: false - schema: - type: number - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/MonikerResultsResponse' - /dbs/{id}/packageInformation: - get: - description: Retrieve package information data by identifier. - tags: - - Query - parameters: - - name: id - in: query - description: The database identifier. - required: true - schema: - type: number - - name: path - in: query - description: The file path within the repository (relative to the repository root). - required: true - schema: - type: string - - name: packageInformationId - in: query - description: The identifier of the target package information data. - required: true - schema: - oneOf: - - type: number - - type: string - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/PackageInformationResponse' -components: - schemas: - Position: - type: object - description: A cursor position in a source file. - properties: - line: - type: number - description: The (zero-index) line index. - character: - type: number - description: The (zero-index) character index. - required: - - line - - character - additionalProperties: false - Range: - type: object - description: A half-open range of positions in a source file. - properties: - start: - $ref: '#/components/schemas/Position' - end: - $ref: '#/components/schemas/Position' - required: - - start - - end - additionalProperties: false - Location: - type: object - description: A position in a file of a code base. - properties: - path: - type: string - description: The root-relative path to the file. - range: - $ref: '#/components/schemas/Range' - required: - - path - - range - additionalProperties: false - ExistsResponse: - type: boolean - DefinitionsResponse: - type: array - items: - $ref: '#/components/schemas/Location' - ReferencesResponse: - type: array - items: - $ref: '#/components/schemas/Location' - HoverResponse: - type: object - properties: - text: - type: string - description: The hover text. - range: - $ref: '#/components/schemas/Range' - description: The range that the hover text describes. - additionalProperties: false - required: - - text - - range - nullable: true - MonikersByPositionResponse: - type: array - description: A list of monikers grouped by matching ranges. - items: - type: array - description: A list of monikers for a single range. - items: - type: object - properties: - kind: - type: string - description: The kind of moniker. - enum: - - import - - export - - local - scheme: - type: string - description: The moniker scheme. - identifier: - type: string - description: The moniker identifier. - packageInformationId: - type: number - description: The identifier of any associated package information. - nullable: true - additionalProperties: false - required: - - kind - - scheme - - identifier - - packageInformationId - MonikerResultsResponse: - type: object - properties: - locations: - type: array - items: - $ref: '#/components/schemas/Location' - count: - type: number - description: The total number of matching locations for this moniker. - additionalProperties: false - required: - - locations - - count - PackageInformationResponse: - type: object - properties: - name: - type: string - description: The name of the package. - version: - type: string - description: The package version. - nullable: true - additionalProperties: false - required: - - name - - version - nullable: true diff --git a/cmd/precise-code-intel/docs/database/database.md b/cmd/precise-code-intel/docs/database/database.md deleted file mode 100644 index 42e17f2d1af..00000000000 --- a/cmd/precise-code-intel/docs/database/database.md +++ /dev/null @@ -1,12 +0,0 @@ -# Configuration - -The LSIF processes store most of their data in SQLite repositories on a shared disk that are written once by a worker on LSIF dump upload, and read many times by the APIs to answer LSIF/LSP queries. Cross-repository and commit graph data is stored in Postgres, as this database requires many concurrent writers (which is an unsafe operation for SQLite in a networked application). The LSIF processes retrieve PostgreSQL connection configuration from the frontend process on startup. - -We rely on the Sourcegraph frontend to apply our DB migrations. These live in the `/migrations` folder. This means: - -- The server, bundle manager, and worker wait for the frontend to apply the migration version it cares about before starting. -- We (and more importantly, site admins) only have to care about a single set of DB schema migrations. This is the primary property we benefit from by doing this. - -## Migrations - -To add a new migration for the tables used by the LSIF processes, create a new migration in the frontend according to the instructions in [the migration documentation](../../../../migrations/README.md). Then, update the value of `MINIMUM_MIGRATION_VERSION` in [postgres.ts](../src/shared/database/postgres.ts) to be the timestamp from the generated filename. diff --git a/cmd/precise-code-intel/docs/database/datamodel.md b/cmd/precise-code-intel/docs/database/datamodel.md deleted file mode 100644 index b114ea80fb3..00000000000 --- a/cmd/precise-code-intel/docs/database/datamodel.md +++ /dev/null @@ -1,234 +0,0 @@ -# LSIF data model - -This document outlines the data model for a single LSIF dump. The definition of the database tables and the entities encoded within it can be found in [sqlite.ts](../src/shared/models/sqlite.ts). - -In the following document, we collapse ranges to keep the document readable, where `a:b-c:d` is shorthand for the following: - -``` -{ - "startLine": a, - "startCharacter": b, - "endLine": c, - "endCharacter": d -} -``` - -This applies to JSON payloads, and a similar shorthand is used for the columns of the `definitions` and `references` tables. - -## Running example - -The following source files compose the package `sample`, which is used as the running example for this document. - -**foo.ts** - -```typescript -export function foo(value: string): string { - return value.substring(1, value.length - 1) -} -``` - -**bar.ts** - -```typescript -import { foo } from './foo' - -export function bar(input: string): string { - return foo(foo(input)) -} -``` - -## Database tables - -**`meta` table** - -This table is populated with **exactly** one row containing the version of the LSIF input, the version of the software that converted it into a SQLite database, and the number used to determine in which result chunk a result identifier belongs (via hash and modulus over the number of chunks). Generally, this number will be the number of rows in the `resultChunks` table, but this number may be higher as we won't insert empty chunks (in the case that no identifier happened to hash to it). - -The last value is used in order to achieve a consistent hash of identifiers that map to the correct result chunk row identifier. This will be explained in more detail later in this document. - -| id | lsifVersion | sourcegraphVersion | numResultChunks | -| --- | ----------- | ------------------ | --------------- | -| 0 | 0.4.3 | 0.1.0 | 1 | - -**`documents` table** - -This table is populated with a gzipped JSON payload that represents the ranges as well as each range's definition, reference, and hover result identifiers. The table is indexed on the path of the document relative to the project root. - -| path | data | -| ------ | ---------------------------- | -| foo.ts | _gzipped_ and _json-encoded_ | -| bar.ts | _gzipped_ and _json-encoded_ | - -Each payload has the following form. As the documents are large, we show only the decoded version for `foo.ts`. - -**encoded `foo.ts` payload** - -````json -{ - "ranges": { - "9": { - "range": "0:0-0:0", - "definitionResultId": "49", - "referenceResultId": "52", - "monikerIds": ["9007199254740990"] - }, - "14": { - "range": "0:16-0:19", - "definitionResultId": "55", - "referenceResultId": "58", - "hoverResultId": "16", - "monikerIds": ["9007199254740987"] - }, - "21": { - "range": "0:20-0:25", - "definitionResultId": "61", - "referenceResultId": "64", - "hoverResultId": "23", - "monikerIds": [] - }, - "25": { - "range": "1:9-1:14", - "definitionResultId": "61", - "referenceResultId": "64", - "hoverResultId": "23", - "monikerIds": [] - }, - "36": { - "range": "1:15-1:24", - "definitionResultId": "144", - "referenceResultId": "68", - "hoverResultId": "34", - "monikerIds": ["30"] - }, - "38": { - "range": "1:28-1:33", - "definitionResultId": "61", - "referenceResultId": "64", - "hoverResultId": "23", - "monikerIds": [] - }, - "47": { - "range": "1:34-1:40", - "definitionResultId": "148", - "referenceResultId": "71", - "hoverResultId": "45", - "monikerIds": [] - } - }, - "hoverResults": { - "16": "```typescript\nfunction foo(value: string): string\n```", - "23": "```typescript\n(parameter) value: string\n```", - "34": "```typescript\n(method) String.substring(start: number, end?: number): string\n```\n\n---\n\nReturns the substring at the specified location within a String object.", - "45": "```typescript\n(property) String.length: number\n```\n\n---\n\nReturns the length of a String object." - }, - "monikers": { - "9007199254740987": { - "kind": "export", - "scheme": "npm", - "identifier": "sample:foo:foo", - "packageInformationId": "9007199254740991" - }, - "9007199254740990": { - "kind": "export", - "scheme": "npm", - "identifier": "sample:foo:", - "packageInformationId": "9007199254740991" - } - }, - "packageInformation": { - "9007199254740991": { - "name": "sample", - "version": "0.1.0" - } - } -} -```` - -The `ranges` field holds a map from range identifier range data including the extents within the source code and optional fields for a definition result, a reference result, and a hover result. Each range also has a possibly empty list of moniker ids. The hover result and moniker identifiers index into the `hoverResults` and `monikers` field of the document. The definition and reference result identifiers index into a result chunk payload, as described below. - -**`resultChunks` table** - -Originally, definition and reference results were stored inline in the document payload. However, this caused document payloads to be come massive in some circumstances (for instance, where the reference result of a frequently used symbol includes multiple ranges in every document of the project). In order to keep each row to a manageable and cacheable size, the definition and reference results were moved into a separate table. The size of each result chunk can then be controlled by varying _how many_ result chunks there are available in a database. It may also be worth noting here that hover result and monikers are best left inlined, as normalizing the former would require another SQL lookup on hover queries, and normalizing the latter would require a SQL lookup per moniker attached to a range; normalizing either does not have a large effect on the size of the document payload. - -This table is populated with gzipped JSON payloads that contains a mapping from definition result or reference result identifiers to the set of ranges that compose that result. A definition or reference result may be referred to by many documents, which is why it is encoded separately. The table is indexed on the common hash of each definition and reference result id inserted in this chunk. - -| id | data | -| --- | ---------------------------- | -| 0 | _gzipped_ and _json-encoded_ | - -Each payload has the following form. - -**encoded result chunk #0 payload** - -```json -{ - "documentPaths": { - "4": "foo.ts", - "80": "bar.ts" - }, - "documentIdRangeIds": { - "49": [{ "documentId": "4", "rangeId": "9" }], - "55": [{ "documentId": "4", "rangeId": "4" }], - "61": [{ "documentId": "4", "rangeId": "21" }], - "71": [{ "documentId": "4", "rangeId": "47" }], - "52": [ - { "documentId": "4", "rangeId": "9" }, - { "documentId": "80", "rangeId": "95" } - ], - "58": [ - { "documentId": "4", "rangeId": "14" }, - { "documentId": "80", "rangeId": "91" }, - { "documentId": "80", "rangeId": "111" }, - { "documentId": "80", "rangeId": "113" } - ], - "64": [ - { "documentId": "4", "rangeId": "21" }, - { "documentId": "4", "rangeId": "25" }, - { "documentId": "4", "rangeId": "38" } - ], - "68": [{ "documentId": "4", "rangeId": "36" }], - "117": [{ "documentId": "80", "rangeId": "85" }], - "120": [{ "documentId": "80", "rangeId": "85" }], - "125": [{ "documentId": "80", "rangeId": "100" }], - "128": [{ "documentId": "80", "rangeId": "100" }], - "131": [{ "documentId": "80", "rangeId": "107" }], - "134": [ - { "documentId": "80", "rangeId": "107" }, - { "documentId": "80", "rangeId": "115" } - ] - } -} -``` - -The `documentIdRangeIds` field store a list of _pairs_ of document identifiers and range identifiers. To look up a range in this format, the `documentId` must be translated into a document path via the `documentPaths` field. This gives the primary key of the document containing the range in the `documents` table, and the range identifier can be looked up in the decoded payload. - -To retrieve a definition or reference result by its identifier, we must first determine in which result chunk it is defined. This requires that we take the hash of the identifier (modulo the `numResultChunks` field of the `meta` table). This gives us the unique identifier into the `resultChunks` table. In the running example of this document, there is only one result chunk. Larger dumps will have a greater number of result chunks to keep the amount of data encoded in a single database row reasonable. - -**definitions table** - -This table is populated with the monikers of a range and that range's definition result. The table is indexed on the `(scheme, identifier)` pair to allow quick lookup by moniker. - -| id | scheme | identifier | documentPath | range | -| --- | ------ | -------------- | ------------ | ------------ | -| 1 | npm | sample:foo: | foo.ts | 0:0 to 0:0 | -| 2 | npm | sample:foo:foo | foo.ts | 0:16 to 0:19 | -| 3 | npm | sample:bar: | bar.ts | 0:0 to 0:0 | -| 4 | npm | sample:bar:bar | bar.ts | 2:16 to 2:19 | - -The row with id `2` correlates the `npm` moniker for the `foo` function with the range where it is defined in `foo.ts`. Similarly, the row with id `4` correlates the exported `npm` moniker for the `bar` function with the range where it is defined in `bar.ts`. - -**references table** - -This table is populated with the monikers of a range and that range's reference result. The table is indexed on the `(scheme, identifier)` pair to allow quick lookup by moniker. - -| id | scheme | identifier | documentPath | range | -| --- | ------ | -------------- | ------------ | ------------ | -| 1 | npm | sample:foo | foo.ts | 0:0 to 0:0 | -| 2 | npm | sample:foo | bar.ts | 0:20 to 0:27 | -| 3 | npm | sample:bar | bar.ts | 0:0 to 0:0 | -| 4 | npm | sample:foo:foo | foo.ts | 0:16 to 0:19 | -| 5 | npm | sample:foo:foo | bar.ts | 0:9 to 0:12 | -| 6 | npm | sample:foo:foo | bar.ts | 3:9 to 3:12 | -| 7 | npm | sample:foo:foo | bar.ts | 3:13 to 3:16 | -| 8 | npm | sample:bar:bar | bar.ts | 2:16 to 2:19 | - -The row with ids `4` through `7` correlate the `npm` moniker for the `foo` function with its references: the definition in `foo.ts`, its import in `bar.ts`, and its two uses in `bar.ts`, respectively. diff --git a/cmd/precise-code-intel/docs/database/datamodel.pg.md b/cmd/precise-code-intel/docs/database/datamodel.pg.md deleted file mode 100644 index a0fe1fb65f0..00000000000 --- a/cmd/precise-code-intel/docs/database/datamodel.pg.md +++ /dev/null @@ -1,68 +0,0 @@ -# Cross-repo data model - -This document outlines the data model used to correlate multiple LSIF dumps. The definition of the cross-repo database tables can be found in [pg.ts](../src/shared/models/pg.ts). - -In the following document, commits have been abbreviated to 7 characters for readability. - -## Database tables - -**`lsif_commits` table** - -This table contains all commits known for a repository for which LSIF data has been uploaded. Each commit consists of one or more rows indicating their parent. If a commit has no parent, then the parentCommit field is an empty string. - -| id | repository_id | commit | parent_commit | -| --- | ------------- | --------- | ------------- | -| 1 | 6 | `a360643` | `f4fb066` | -| 2 | 6 | `f4fb066` | `4c8d9dc` | -| 3 | 6 | `313082b` | `4c8d9dc` | -| 4 | 6 | `4c8d9dc` | `d67b8de` | -| 5 | 6 | `d67b8de` | `323e23f` | -| 6 | 6 | `323e23f` | | - -This table allows us to ues recursive CTEs to find ancestor and descendant commits with a particular property (as indicated by the existence of an entry in the `lsif_dumps` table) and enables closest commit functionality. - -**`lsif_uploads` table** - -This table contains an entry for each LSIF upload. An upload is inserted with the state `queued` and is processed asynchronously by a worker process. The `root` field indicates the directory for which this upload provides code intelligence. The `indexer` field indicates the tool that generated the input. The `visible_at_tip` field indicates whether this a (completed) upload that is closest to the tip of the default branch. - -| id | repository_id | commit | root | indexer | state | visible_at_tip | -| --- | ------------- | --------- | ---- | ------- | --------- | -------------- | -| 1 | 6 | `a360643` | | lsif-go | completed | true | -| 2 | 6 | `f4fb066` | | lsif-go | completed | false | -| 3 | 6 | `4c8d9dc` | cmd | lsif-go | completed | true | -| 4 | 6 | `323e23f` | | lsif-go | completed | false | - -The view `lsif_dumps` selects all uploads with a state of `completed`. - -Additional fields are not shown in the table above which do not affect code intelligence queries in a meaningful way. - -- `filename`: The filename of the raw upload. -- `uploaded_at`: The time the record was inserted. -- `started_at`: The time the conversion was started. -- `finished_at`: The time the conversion was finished. -- `failure_summary`: The message of the error that occurred during conversion. -- `failure_stacktrace`: The stacktrace of the error that occurred during conversion. -- `tracing_context`: The tracing context from the `/upload` endpoint. Used to trace the entire span of work from the upload to the end of conversion. - -**`lsif_packages` table** - -This table links a package manager-specific identifier and version to the LSIF upload _provides_ the package. The scheme, name, and version values are correlated with a moniker and its package information from an LSIF dump. - -| id | scheme | name | version | dump_id | -| --- | ------ | ------ | ------- | ------- | -| 1 | npm | sample | 0.1.0 | 6 | - -This table enables cross-repository jump-to-definition. When a range has no definition result but does have an _import_ moniker, the scheme, name, and version of the moniker can be queried in this table to get the repository and commit of the package that should contain that moniker's definition. - -**`lsif_references` table** - -This table links an LSIF upload to the set of packages on which it depends. This table shares common columns with the `lsif_packages` table, which are documented above. In addition, this table also has a `filter` column, which encodes a [bloom filter](https://en.wikipedia.org/wiki/Bloom_filter) populated with the set of identifiers that the commit imports from the dependent package. - -| id | scheme | name | version | filter | dump_id | -| --- | ------ | --------- | ------- | ---------------------------- | ------- | -| 1 | npm | left-pad | 0.1.0 | _gzipped_ and _json-encoded_ | 6 | -| 2 | npm | right-pad | 1.2.3 | _gzipped_ and _json-encoded_ | 6 | -| 2 | npm | left-pad | 0.1.0 | _gzipped_ and _json-encoded_ | 7 | -| 2 | npm | right-pad | 1.2.4 | _gzipped_ and _json-encoded_ | 7 | - -This table enables global find-references. When finding all references of a definition that has an _export_ moniker, the set of repositories and commits that depend on the package of that moniker are queried. We want to open only the databases that import this particular symbol (not all projects depending on this package import the identifier under query). To do this, the bloom filter is deserialized and queried for the identifier under query. A positive response from a bloom filter indicates that the identifier may be present in the set; a negative response from the bloom filter indicates that the identifier is _definitely_ not in the set. We only open the set of databases for which the bloom filter query responds positively. diff --git a/cmd/precise-code-intel/jest.config.js b/cmd/precise-code-intel/jest.config.js deleted file mode 100644 index 40f366edd87..00000000000 --- a/cmd/precise-code-intel/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -// @ts-check - -/** @type {jest.InitialOptions} */ -const config = require('../../jest.config.base') - -/** @type {jest.InitialOptions} */ -module.exports = { ...config, setupFilesAfterEnv: ['./jest.setup.js'], displayName: 'lsif', rootDir: __dirname } diff --git a/cmd/precise-code-intel/jest.setup.js b/cmd/precise-code-intel/jest.setup.js deleted file mode 100644 index e0588650946..00000000000 --- a/cmd/precise-code-intel/jest.setup.js +++ /dev/null @@ -1,5 +0,0 @@ -// LSIF tests create and migrate Postgres databases, which can take more -// time than the default test timeout. Increase it here for all tests in -// this project. - -jest.setTimeout(15000) diff --git a/cmd/precise-code-intel/package.json b/cmd/precise-code-intel/package.json deleted file mode 100644 index ff44ede9f1a..00000000000 --- a/cmd/precise-code-intel/package.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "private": true, - "name": "precise-code-intel", - "description": "Precise code intelligence services for Sourcegraph", - "author": "Sourcegraph", - "license": "MIT", - "scripts": { - "build": "tsc -b .", - "test": "jest", - "eslint": "../../node_modules/.bin/eslint --cache 'src/**/*.ts?(x)'", - "run:api-server": "tsc-watch --onSuccess \"node -r source-map-support/register out/api-server/api.js\" --noClear", - "run:bundle-manager": "tsc-watch --onSuccess \"node -r source-map-support/register out/bundle-manager/manager.js\" --noClear", - "run:worker": "tsc-watch --onSuccess \"node -r source-map-support/register out/worker/worker.js\" --noClear" - }, - "dependencies": { - "async-middleware": "^1.2.1", - "async-polling": "^0.2.1", - "bloomfilter": "^0.0.18", - "body-parser": "^1.19.0", - "crc-32": "^1.2.0", - "delay": "^4.3.0", - "express": "^4.17.1", - "express-opentracing": "^0.1.1", - "express-validator": "^6.4.0", - "express-winston": "^4.0.3", - "got": "^10.7.0", - "jaeger-client": "^3.17.2", - "json5": "^2.1.1", - "lodash": "^4.17.15", - "logform": "^2.1.2", - "lsif-protocol": "0.4.3", - "mz": "^2.7.0", - "on-finished": "^2.3.0", - "opentracing": "^0.14.4", - "p-retry": "^4.2.0", - "pg": "^7.18.2", - "prom-client": "^12.0.0", - "relateurl": "^0.2.7", - "rmfr": "^2.0.0", - "sqlite3": "^4.1.1", - "stream-throttle": "^0.1.3", - "triple-beam": "^1.3.0", - "typeorm": "^0.2.24", - "uuid": "^7.0.3", - "vscode-languageserver": "^6.1.1", - "winston": "^3.2.1", - "yallist": "^4.0.0" - }, - "devDependencies": { - "@sourcegraph/tsconfig": "^4.0.1", - "@types/async-polling": "0.0.3", - "@types/bloomfilter": "0.0.0", - "@types/body-parser": "1.19.0", - "@types/express": "4.17.4", - "@types/express-winston": "3.0.4", - "@types/got": "9.6.10", - "@types/jaeger-client": "3.15.3", - "@types/jest": "25.2.1", - "@types/json5": "0.0.30", - "@types/lodash": "4.14.150", - "@types/logform": "1.2.0", - "@types/mz": "2.7.0", - "@types/on-finished": "2.3.1", - "@types/relateurl": "0.2.28", - "@types/rmfr": "2.0.0", - "@types/sinon": "9.0.0", - "@types/stream-throttle": "0.1.0", - "@types/triple-beam": "1.3.0", - "@types/uuid": "7.0.3", - "@types/yallist": "3.0.1", - "babel-jest": "^25.2.6", - "copyfiles": "^2.2.0", - "eslint-plugin-import": "^2.20.2", - "jest": "^25.2.7", - "nock": "^12.0.2", - "sinon": "^9.0.1", - "source-map-support": "^0.5.16", - "tsc-watch": "^4.2.3", - "typescript": "^3.7.2", - "typescript-json-schema": "^0.42.0" - } -} diff --git a/cmd/precise-code-intel/src/@types/express-opentracing/index.d.ts b/cmd/precise-code-intel/src/@types/express-opentracing/index.d.ts deleted file mode 100644 index 26a722feabd..00000000000 --- a/cmd/precise-code-intel/src/@types/express-opentracing/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module 'express-opentracing' - -declare function middleware(options?: { tracer?: Tracer }): Handler diff --git a/cmd/precise-code-intel/src/api-server/api.ts b/cmd/precise-code-intel/src/api-server/api.ts deleted file mode 100644 index 379cc74c2c3..00000000000 --- a/cmd/precise-code-intel/src/api-server/api.ts +++ /dev/null @@ -1,80 +0,0 @@ -import * as metrics from './metrics' -import * as settings from './settings' -import promClient from 'prom-client' -import { Backend } from './backend/backend' -import { createLogger } from '../shared/logging' -import { createLsifRouter } from './routes/lsif' -import { createPostgresConnection } from '../shared/database/postgres' -import { createTracer } from '../shared/tracing' -import { createUploadRouter } from './routes/uploads' -import { ensureDirectory } from '../shared/paths' -import { Logger } from 'winston' -import { startTasks } from './tasks' -import { UploadManager } from '../shared/store/uploads' -import { waitForConfiguration } from '../shared/config/config' -import { DumpManager } from '../shared/store/dumps' -import { DependencyManager } from '../shared/store/dependencies' -import { SRC_FRONTEND_INTERNAL } from '../shared/config/settings' -import { startExpressApp } from '../shared/api/init' -import { createInternalRouter } from './routes/internal' - -/** - * Runs the HTTP server that accepts LSIF dump uploads and responds to LSIF requests. - * - * @param logger The logger instance. - */ -async function main(logger: Logger): Promise { - // Collect process metrics - promClient.collectDefaultMetrics({ prefix: 'lsif_' }) - - // Read configuration from frontend - const fetchConfiguration = await waitForConfiguration(logger) - - // Configure distributed tracing - const tracer = createTracer('precise-code-intel-api-server', fetchConfiguration()) - - // Ensure storage roots exist - await ensureDirectory(settings.STORAGE_ROOT) - - // Create database connection and entity wrapper classes - const connection = await createPostgresConnection(fetchConfiguration(), logger) - const dumpManager = new DumpManager(connection) - const uploadManager = new UploadManager(connection) - const dependencyManager = new DependencyManager(connection) - const backend = new Backend(dumpManager, dependencyManager, SRC_FRONTEND_INTERNAL) - - // Start background tasks - startTasks(connection, uploadManager, logger) - - const routers = [ - createUploadRouter(dumpManager, uploadManager, logger), - createLsifRouter(connection, backend, uploadManager, logger, tracer), - createInternalRouter(dumpManager, uploadManager, logger), - ] - - // Start server - startExpressApp({ port: settings.HTTP_PORT, routers, logger, tracer, selectHistogram }) -} - -function selectHistogram(route: string): promClient.Histogram | undefined { - switch (route) { - case '/upload': - return metrics.httpUploadDurationHistogram - - case '/exists': - case '/request': - return metrics.httpQueryDurationHistogram - } - - return undefined -} - -// Initialize logger -const appLogger = createLogger('precise-code-intel-api-server') - -// Run app! -main(appLogger).catch(error => { - appLogger.error('failed to start process', { error }) - appLogger.on('finish', () => process.exit(1)) - appLogger.end() -}) diff --git a/cmd/precise-code-intel/src/api-server/backend/backend.test.ts b/cmd/precise-code-intel/src/api-server/backend/backend.test.ts deleted file mode 100644 index 75819b89280..00000000000 --- a/cmd/precise-code-intel/src/api-server/backend/backend.test.ts +++ /dev/null @@ -1,633 +0,0 @@ -import * as sinon from 'sinon' -import * as lsif from 'lsif-protocol' -import * as pgModels from '../../shared/models/pg' -import { Backend, sortMonikers } from './backend' -import { DependencyManager } from '../../shared/store/dependencies' -import { DumpManager } from '../../shared/store/dumps' -import { Database } from './database' -import { createCleanPostgresDatabase } from '../../shared/test-util' -import { Connection } from 'typeorm' -import { OrderedLocationSet, ResolvedInternalLocation } from './location' -import { ReferencePaginationCursor } from './cursor' -import { range } from 'lodash' - -const zeroUpload: pgModels.LsifUpload = { - id: 0, - repositoryId: 0, - commit: '', - root: '', - indexer: '', - state: 'queued', - numParts: 1, - uploadedParts: [0], - uploadedAt: new Date(), - startedAt: null, - finishedAt: null, - failureSummary: null, - failureStacktrace: null, - visibleAtTip: false, -} - -const zeroDump: pgModels.LsifDump = { - ...zeroUpload, - state: 'completed', - processedAt: new Date(), -} - -const zeroPackage = { - id: 0, - scheme: '', - name: '', - version: '', - dump: null, - dump_id: 0, - filter: Buffer.from(''), -} - -const monikersWithPackageInformation = [ - { kind: lsif.MonikerKind.local, scheme: 'test', identifier: 'm1' }, - { kind: lsif.MonikerKind.import, scheme: 'test', identifier: 'm2', packageInformationId: 71 }, - { kind: lsif.MonikerKind.import, scheme: 'test', identifier: 'm3' }, -] - -const makeRange = (i: number) => ({ - start: { line: i + 1, character: (i + 1) * 10 }, - end: { line: i + 1, character: (i + 1) * 10 + 5 }, -}) - -const createTestDatabase = (dbs: Map) => (dumpId: pgModels.DumpId) => { - const db = dbs.get(dumpId) - if (!db) { - throw new Error(`Unexpected database construction (dumpId=${dumpId})`) - } - - return db -} - -describe('Backend', () => { - let connection!: Connection - let cleanup!: () => Promise - let dumpManager!: DumpManager - let dependencyManager!: DependencyManager - - beforeAll(async () => { - ;({ connection, cleanup } = await createCleanPostgresDatabase()) - }) - - afterAll(async () => { - if (cleanup) { - await cleanup() - } - }) - - beforeEach(() => { - dumpManager = new DumpManager(connection) - dependencyManager = new DependencyManager(connection) - }) - - describe('exists', () => { - it('should return closest dumps with file', async () => { - const database1 = new Database(1) - const database2 = new Database(2) - const database3 = new Database(3) - const database4 = new Database(4) - - // Commit graph traversal - sinon.stub(dumpManager, 'findClosestDumps').resolves([ - { ...zeroDump, id: 1 }, - { ...zeroDump, id: 2 }, - { ...zeroDump, id: 3 }, - { ...zeroDump, id: 4 }, - ]) - - // Path existence check - const spy1 = sinon.stub(database1, 'exists').resolves(true) - const spy2 = sinon.stub(database2, 'exists').resolves(false) - const spy3 = sinon.stub(database3, 'exists').resolves(false) - const spy4 = sinon.stub(database4, 'exists').resolves(true) - - const dumps = await new Backend( - dumpManager, - dependencyManager, - '', - createTestDatabase( - new Map([ - [1, database1], - [2, database2], - [3, database3], - [4, database4], - ]) - ) - ).exists(42, 'deadbeef', '/foo/bar/baz.ts') - - expect(dumps).toEqual([ - { ...zeroDump, id: 1 }, - { ...zeroDump, id: 4 }, - ]) - expect(spy1.args[0][0]).toEqual('/foo/bar/baz.ts') - expect(spy2.args[0][0]).toEqual('/foo/bar/baz.ts') - expect(spy3.args[0][0]).toEqual('/foo/bar/baz.ts') - expect(spy4.args[0][0]).toEqual('/foo/bar/baz.ts') - }) - }) - - describe('definitions', () => { - it('should return definitions from database', async () => { - const database1 = new Database(1) - - // Loading source dump - sinon.stub(dumpManager, 'getDumpById').resolves({ ...zeroDump, id: 1 }) - - // Resolving target dumps - sinon.stub(dumpManager, 'getDumpsByIds').resolves( - new Map([ - [1, { ...zeroDump, id: 1 }], - [2, { ...zeroDump, id: 2 }], - [3, { ...zeroDump, id: 3 }], - [4, { ...zeroDump, id: 4 }], - ]) - ) - - // In-database definitions - sinon.stub(database1, 'definitions').resolves([ - { dumpId: 1, path: '1.ts', range: makeRange(1) }, - { dumpId: 2, path: '2.ts', range: makeRange(2) }, - { dumpId: 3, path: '3.ts', range: makeRange(3) }, - { dumpId: 4, path: '4.ts', range: makeRange(4) }, - ]) - - const locations = await new Backend( - dumpManager, - dependencyManager, - '', - createTestDatabase(new Map([[1, database1]])) - ).definitions(42, 'deadbeef', '/foo/bar/baz.ts', { line: 5, character: 10 }, 1) - - expect(locations).toEqual([ - { dump: { ...zeroDump, id: 1 }, path: '1.ts', range: makeRange(1) }, - { dump: { ...zeroDump, id: 2 }, path: '2.ts', range: makeRange(2) }, - { dump: { ...zeroDump, id: 3 }, path: '3.ts', range: makeRange(3) }, - { dump: { ...zeroDump, id: 4 }, path: '4.ts', range: makeRange(4) }, - ]) - }) - - it('should return definitions from local moniker search', async () => { - const database1 = new Database(1) - - // Loading source dump - sinon.stub(dumpManager, 'getDumpById').resolves({ ...zeroDump, id: 1 }) - - // Resolving target dumps - sinon.stub(dumpManager, 'getDumpsByIds').resolves( - new Map([ - [1, { ...zeroDump, id: 1 }], - [2, { ...zeroDump, id: 2 }], - [3, { ...zeroDump, id: 3 }], - [4, { ...zeroDump, id: 4 }], - ]) - ) - - // In-database definitions - sinon.stub(database1, 'definitions').resolves([]) - - // Moniker resolution - sinon.stub(database1, 'monikersByPosition').resolves([monikersWithPackageInformation]) - - // Moniker search - sinon.stub(database1, 'monikerResults').resolves({ - locations: [ - { dumpId: 1, path: '1.ts', range: makeRange(1) }, - { dumpId: 2, path: '2.ts', range: makeRange(2) }, - { dumpId: 3, path: '3.ts', range: makeRange(3) }, - { dumpId: 4, path: '4.ts', range: makeRange(4) }, - ], - count: 4, - }) - - const locations = await new Backend( - dumpManager, - dependencyManager, - '', - createTestDatabase(new Map([[1, database1]])) - ).definitions(42, 'deadbeef', '/foo/bar/baz.ts', { line: 5, character: 10 }, 1) - - expect(locations).toEqual([ - { dump: { ...zeroDump, id: 1 }, path: '1.ts', range: makeRange(1) }, - { dump: { ...zeroDump, id: 2 }, path: '2.ts', range: makeRange(2) }, - { dump: { ...zeroDump, id: 3 }, path: '3.ts', range: makeRange(3) }, - { dump: { ...zeroDump, id: 4 }, path: '4.ts', range: makeRange(4) }, - ]) - }) - - it('should return definitions from remote moniker search', async () => { - const database1 = new Database(1) - const database2 = new Database(2) - - // Loading source dump - sinon.stub(dumpManager, 'getDumpById').resolves({ ...zeroDump, id: 1 }) - - // Resolving target dumps - sinon.stub(dumpManager, 'getDumpsByIds').resolves( - new Map([ - [1, { ...zeroDump, id: 1 }], - [2, { ...zeroDump, id: 2 }], - [3, { ...zeroDump, id: 3 }], - [4, { ...zeroDump, id: 4 }], - ]) - ) - - // In-database definitions - sinon.stub(database1, 'definitions').resolves([]) - - // Moniker resolution - sinon.stub(database1, 'monikersByPosition').resolves([monikersWithPackageInformation]) - - // Package resolution - sinon.stub(database1, 'packageInformation').resolves({ name: 'pkg2', version: '0.0.1' }) - - // Package resolution - sinon.stub(dependencyManager, 'getPackage').resolves({ - id: 71, - scheme: 'test', - name: 'pkg2', - version: '0.0.1', - dump: { ...zeroDump, id: 2 }, - dump_id: 2, - }) - - // Moniker search (local database) - sinon.stub(database1, 'monikerResults').resolves({ locations: [], count: 0 }) - - // Moniker search (remote database) - sinon.stub(database2, 'monikerResults').resolves({ - locations: [ - { dumpId: 1, path: '1.ts', range: makeRange(1) }, - { dumpId: 2, path: '2.ts', range: makeRange(2) }, - { dumpId: 3, path: '3.ts', range: makeRange(3) }, - { dumpId: 4, path: '4.ts', range: makeRange(4) }, - ], - count: 4, - }) - - const locations = await new Backend( - dumpManager, - dependencyManager, - '', - createTestDatabase( - new Map([ - [1, database1], - [2, database2], - ]) - ) - ).definitions(42, 'deadbeef', '/foo/bar/baz.ts', { line: 5, character: 10 }, 1) - - expect(locations).toEqual([ - { dump: { ...zeroDump, id: 1 }, path: '1.ts', range: makeRange(1) }, - { dump: { ...zeroDump, id: 2 }, path: '2.ts', range: makeRange(2) }, - { dump: { ...zeroDump, id: 3 }, path: '3.ts', range: makeRange(3) }, - { dump: { ...zeroDump, id: 4 }, path: '4.ts', range: makeRange(4) }, - ]) - }) - }) - - describe('references', () => { - const queryAllReferences = async ( - backend: Backend, - repositoryId: number, - commit: string, - path: string, - position: lsif.lsp.Position, - dumpId: number, - limit: number, - remoteDumpLimit?: number - ): Promise<{ locations: ResolvedInternalLocation[]; pageSizes: number[]; numPages: number }> => { - let locations: ResolvedInternalLocation[] = [] - const pageSizes: number[] = [] - let cursor: ReferencePaginationCursor | undefined - - while (true) { - const result = await backend.references( - repositoryId, - commit, - path, - position, - { limit, cursor }, - remoteDumpLimit, - dumpId - ) - if (!result) { - break - } - - locations = locations.concat(result.locations) - pageSizes.push(result.locations.length) - - if (!result.newCursor) { - break - } - cursor = result.newCursor - } - - return { locations, pageSizes, numPages: pageSizes.length } - } - - const assertPagedReferences = async ( - numSameRepoDumps: number, - numRemoteRepoDumps: number, - locationsPerDump: number, - pageLimit: number, - remoteDumpLimit: number - ): Promise => { - const numDatabases = 2 + numSameRepoDumps + numRemoteRepoDumps - const numLocations = numDatabases * locationsPerDump - - const databases = range(0, numDatabases).map(i => new Database(i + 1)) - const dumps = range(0, numLocations).map(i => ({ ...zeroDump, id: i + 1 })) - const locations = range(0, numLocations).map(i => ({ - dumpId: i + 1, - path: `${i + 1}.ts`, - range: makeRange(i), - })) - - const getChunk = (index: number) => - locations.slice(index * locationsPerDump, (index + 1) * locationsPerDump) - - const sameRepoDumps = range(0, numSameRepoDumps).map(i => ({ - ...zeroPackage, - dump: dumps[i + 2], - dump_id: i + 3, - })) - - const remoteRepoDumps = range(0, numRemoteRepoDumps).map(i => ({ - ...zeroPackage, - dump: dumps[i + numSameRepoDumps + 2], - dump_id: i + numSameRepoDumps + 3, - })) - - const expectedLocations = locations.map((location, i) => ({ - dump: dumps[i], - path: location.path, - range: location.range, - })) - - const dumpMap = new Map(dumps.map(dump => [dump.id, dump])) - const databaseMap = new Map(databases.map((db, i) => [i + 1, db])) - const definitionPackage = { - id: 71, - scheme: 'test', - name: 'pkg2', - version: '0.0.1', - dump: dumps[1], - dump_id: 2, - } - - // Loading source dump - sinon.stub(dumpManager, 'getDumpById').callsFake(id => { - if (id <= dumps.length) { - return Promise.resolve(dumps[id - 1]) - } - - throw new Error(`Unexpected getDumpById invocation (id=${id}`) - }) - - // Resolving target dumps - sinon.stub(dumpManager, 'getDumpsByIds').resolves(dumpMap) - - // Package resolution - sinon.stub(dependencyManager, 'getPackage').resolves(definitionPackage) - - // Same-repo package references - const sameRepoStub = sinon - .stub(dependencyManager, 'getSameRepoRemotePackageReferences') - .callsFake(({ limit, offset }) => - Promise.resolve({ - packageReferences: sameRepoDumps.slice(offset, offset + limit), - totalCount: numSameRepoDumps, - newOffset: offset + limit, - }) - ) - - // Remote repo package references - const remoteRepoStub = sinon - .stub(dependencyManager, 'getPackageReferences') - .callsFake(({ limit, offset }) => - Promise.resolve({ - packageReferences: remoteRepoDumps.slice(offset, offset + limit), - totalCount: numRemoteRepoDumps, - newOffset: offset + limit, - }) - ) - - // Moniker resolution - sinon.stub(databases[0], 'monikersByPosition').resolves([monikersWithPackageInformation]) - - // Package resolution - sinon.stub(databases[0], 'packageInformation').resolves({ name: 'pkg2', version: '0.0.1' }) - - // Same dump results - const referenceStub = sinon.stub(databases[0], 'references').resolves(new OrderedLocationSet(getChunk(0))) - - const monikerStubs: sinon.SinonStub< - Parameters, - ReturnType - >[] = [] - - // Local moniker results - sinon.stub(databases[0], 'monikerResults').resolves({ locations: [], count: 0 }) - - // Remote dump results - for (let i = 1; i < numDatabases; i++) { - monikerStubs.push( - sinon.stub(databases[i], 'monikerResults').callsFake((model, moniker, { skip = 0, take = 10 }) => - Promise.resolve({ - locations: getChunk(i).slice(skip, skip + take), - count: locationsPerDump, - }) - ) - ) - } - - // Read all reference pages - const { locations: resolvedLocations, pageSizes } = await queryAllReferences( - new Backend(dumpManager, dependencyManager, '', createTestDatabase(databaseMap)), - 42, - 'deadbeef', - '/foo/bar/baz.ts', - { line: 5, character: 10 }, - 1, - pageLimit, - remoteDumpLimit - ) - - // Ensure we get all locations - expect(resolvedLocations).toEqual(expectedLocations) - - // Ensure all pages (except for last) are full - const copy = Array.from(pageSizes) - expect(copy.pop()).toBeLessThanOrEqual(pageLimit) - expect(copy.every(v => v === pageLimit)).toBeTruthy() - - // Ensure pagination limits are respected - const expectedCalls = (results: number, limit: number) => Math.max(1, Math.ceil(results / limit)) - expect(sameRepoStub.callCount).toEqual(expectedCalls(numSameRepoDumps, remoteDumpLimit)) - expect(remoteRepoStub.callCount).toEqual(expectedCalls(numRemoteRepoDumps, remoteDumpLimit)) - expect(referenceStub.callCount).toEqual(expectedCalls(locationsPerDump, pageLimit)) - for (const stub of monikerStubs) { - expect(stub.callCount).toEqual(expectedCalls(locationsPerDump, pageLimit)) - } - } - - it('should return references in source and definition dumps', () => assertPagedReferences(0, 0, 1, 10, 5)) - it('should return references in remote dumps', () => assertPagedReferences(1, 0, 1, 10, 5)) - it('should return references in remote repositories', () => assertPagedReferences(0, 1, 1, 10, 5)) - it('should page large results sets', () => assertPagedReferences(0, 0, 25, 10, 5)) - it('should page a large number of remote dumps', () => assertPagedReferences(25, 25, 25, 10, 5)) - it('should respect small page size', () => assertPagedReferences(25, 25, 25, 1, 5)) - it('should respect large page size', () => assertPagedReferences(25, 25, 25, 1000, 5)) - it('should respect small remote dumps page size', () => assertPagedReferences(25, 25, 25, 10, 1)) - it('should respect large remote dumps page size', () => assertPagedReferences(25, 25, 25, 10, 25)) - }) - - describe('hover', () => { - it('should return hover content from database', async () => { - const database1 = new Database(1) - - // Loading source dump - sinon.stub(dumpManager, 'getDumpById').resolves({ ...zeroDump, id: 1 }) - - // In-database hover - sinon.stub(database1, 'hover').resolves({ - text: 'hover text', - range: makeRange(1), - }) - - const hover = await new Backend( - dumpManager, - dependencyManager, - '', - createTestDatabase(new Map([[1, database1]])) - ).hover(42, 'deadbeef', '/foo/bar/baz.ts', { line: 5, character: 10 }, 1) - - expect(hover).toEqual({ - text: 'hover text', - range: makeRange(1), - }) - }) - - it('should return hover content from unique definition', async () => { - const database1 = new Database(1) - const database2 = new Database(2) - - // Loading source dump - sinon.stub(dumpManager, 'getDumpById').resolves({ ...zeroDump, id: 1 }) - - // Resolving target dumps - sinon.stub(dumpManager, 'getDumpsByIds').resolves(new Map([[2, { ...zeroDump, id: 2 }]])) - - // In-database hover - sinon.stub(database1, 'hover').resolves(null) - - // In-database definitions - sinon.stub(database1, 'definitions').resolves([ - { - dumpId: 2, - path: '2.ts', - range: makeRange(2), - }, - ]) - - // Remote-database hover - sinon.stub(database2, 'hover').resolves({ - text: 'hover text', - range: makeRange(1), - }) - - const hover = await new Backend( - dumpManager, - dependencyManager, - '', - createTestDatabase( - new Map([ - [1, database1], - [2, database2], - ]) - ) - ).hover(42, 'deadbeef', '/foo/bar/baz.ts', { line: 5, character: 10 }, 1) - - expect(hover).toEqual({ text: 'hover text', range: makeRange(1) }) - }) - }) -}) - -describe('sortMonikers', () => { - it('should order monikers by kind', () => { - const monikers = [ - { - kind: lsif.MonikerKind.local, - scheme: 'npm', - identifier: 'foo', - }, - { - kind: lsif.MonikerKind.export, - scheme: 'npm', - identifier: 'bar', - }, - { - kind: lsif.MonikerKind.local, - scheme: 'npm', - identifier: 'baz', - }, - { - kind: lsif.MonikerKind.import, - scheme: 'npm', - identifier: 'bonk', - }, - ] - - expect(sortMonikers(monikers)).toEqual([monikers[3], monikers[0], monikers[2], monikers[1]]) - }) - - it('should remove subsumed monikers', () => { - const monikers = [ - { - kind: lsif.MonikerKind.local, - scheme: 'go', - identifier: 'foo', - }, - { - kind: lsif.MonikerKind.local, - scheme: 'tsc', - identifier: 'bar', - }, - { - kind: lsif.MonikerKind.local, - scheme: 'gomod', - identifier: 'baz', - }, - { - kind: lsif.MonikerKind.local, - scheme: 'npm', - identifier: 'baz', - }, - ] - - expect(sortMonikers(monikers)).toEqual([monikers[2], monikers[3]]) - }) - - it('should not remove subsumable (but non-subsumed) monikers', () => { - const monikers = [ - { - kind: lsif.MonikerKind.local, - scheme: 'go', - identifier: 'foo', - }, - { - kind: lsif.MonikerKind.local, - scheme: 'tsc', - identifier: 'bar', - }, - ] - - expect(sortMonikers(monikers)).toEqual(monikers) - }) -}) diff --git a/cmd/precise-code-intel/src/api-server/backend/cursor.ts b/cmd/precise-code-intel/src/api-server/backend/cursor.ts deleted file mode 100644 index f1544f4b917..00000000000 --- a/cmd/precise-code-intel/src/api-server/backend/cursor.ts +++ /dev/null @@ -1,92 +0,0 @@ -import * as sqliteModels from '../../shared/models/sqlite' -import * as lsp from 'vscode-languageserver-protocol' - -/** Context describing the current request for paginated results. */ -export interface ReferencePaginationContext { - /** The maximum number of locations to return on this page. */ - limit: number - - /** Context describing the next page of results. */ - cursor?: ReferencePaginationCursor -} - -/** Context describing the next page of results. */ -export type ReferencePaginationCursor = - | SameDumpReferenceCursor - | DefinitionMonikersReferenceCursor - | RemoteDumpReferenceCursor - -/** A label that indicates which pagination phase is being expanded. */ -export type ReferencePaginationPhase = 'same-dump' | 'definition-monikers' | 'same-repo' | 'remote-repo' - -/** Fields common to all reference pagination cursors. */ -interface ReferencePaginationCursorCommon { - /** The identifier of the dump that contains the target range. */ - dumpId: number - - /** The phase of the pagination. */ - phase: ReferencePaginationPhase -} - -/** Bookkeeping data for the reference results that come from the initial dump. */ -export interface SameDumpReferenceCursor extends ReferencePaginationCursorCommon { - phase: 'same-dump' - - /** The (database-relative) document path containing the symbol ranges. */ - path: string - - /** The current hover position. */ - position: lsp.Position - - /** A normalized list of monikers attached to the symbol ranges. */ - monikers: sqliteModels.MonikerData[] - - /** The number of reference results to skip. */ - skipResults: number -} - -/** Bookkeeping data for the reference results that come from dumps defining a moniker. */ -export interface DefinitionMonikersReferenceCursor extends ReferencePaginationCursorCommon { - phase: 'definition-monikers' - - /** The (database-relative) document path containing the symbol ranges. */ - path: string - - /** A normalized list of monikers attached to the symbol ranges. */ - monikers: sqliteModels.MonikerData[] - - /** The number of location results to skip for the current moniker. */ - skipResults: number -} - -/** Bookkeeping data for the reference results that come from additional (remote) dumps. */ -export interface RemoteDumpReferenceCursor extends ReferencePaginationCursorCommon { - phase: 'same-repo' | 'remote-repo' - - /** The identifier of the moniker that has remote results. */ - identifier: string - - /** The scheme of the moniker that has remote results. */ - scheme: string - - /** The name of the package that has remote results. */ - name: string - - /** The version of the package that has remote results. */ - version: string | null - - /** The current batch of dumps to open. */ - dumpIds: number[] - - /** The total count of candidate dumps that can be opened. */ - totalDumpsWhenBatching: number - - /** The number of dumps we have already processed or bloom filtered. */ - skipDumpsWhenBatching: number - - /** The number of dumps we have already completed in the current batch. */ - skipDumpsInBatch: number - - /** The number of location results to skip for the current dump. */ - skipResultsInDump: number -} diff --git a/cmd/precise-code-intel/src/api-server/backend/database.ts b/cmd/precise-code-intel/src/api-server/backend/database.ts deleted file mode 100644 index 507bd66219f..00000000000 --- a/cmd/precise-code-intel/src/api-server/backend/database.ts +++ /dev/null @@ -1,175 +0,0 @@ -import * as sqliteModels from '../../shared/models/sqlite' -import * as lsp from 'vscode-languageserver-protocol' -import * as pgModels from '../../shared/models/pg' -import { TracingContext } from '../../shared/tracing' -import { parseJSON } from '../../shared/encoding/json' -import * as settings from '../settings' -import got from 'got' -import { InternalLocation, OrderedLocationSet } from './location' - -/** A wrapper around operations related to a single SQLite dump. */ -export class Database { - constructor(private dumpId: pgModels.DumpId) {} - - /** - * Determine if data exists for a particular document in this database. - * - * @param path The path of the document. - * @param ctx The tracing context. - */ - public exists(path: string, ctx: TracingContext = {}): Promise { - return this.request('exists', new URLSearchParams({ path }), ctx) - } - - /** - * Return a list of locations that define the symbol at the given position. - * - * @param path The path of the document to which the position belongs. - * @param position The current hover position. - * @param ctx The tracing context. - */ - public async definitions( - path: string, - position: lsp.Position, - ctx: TracingContext = {} - ): Promise { - const locations = await this.request<{ path: string; range: lsp.Range }[]>( - 'definitions', - new URLSearchParams({ path, line: String(position.line), character: String(position.character) }), - ctx - ) - - return locations.map(location => ({ ...location, dumpId: this.dumpId })) - } - - /** - * Return a list of unique locations that reference the symbol at the given position. - * - * @param path The path of the document to which the position belongs. - * @param position The current hover position. - * @param ctx The tracing context. - */ - public async references( - path: string, - position: lsp.Position, - ctx: TracingContext = {} - ): Promise { - const locations = await this.request<{ path: string; range: lsp.Range }[]>( - 'references', - new URLSearchParams({ path, line: String(position.line), character: String(position.character) }), - ctx - ) - - return new OrderedLocationSet(locations.map(location => ({ ...location, dumpId: this.dumpId }))) - } - - /** - * Return the hover content for the symbol at the given position. - * - * @param path The path of the document to which the position belongs. - * @param position The current hover position. - * @param ctx The tracing context. - */ - public hover( - path: string, - position: lsp.Position, - ctx: TracingContext = {} - ): Promise<{ text: string; range: lsp.Range } | null> { - return this.request( - 'hover', - new URLSearchParams({ path, line: String(position.line), character: String(position.character) }), - ctx - ) - } - - /** - * Return all of the monikers attached to all ranges that contain the given position. The - * resulting list is grouped by range. If multiple ranges contain this position, then the - * list monikers for the inner-most ranges will occur before the outer-most ranges. - * - * @param path The path of the document. - * @param position The user's hover position. - * @param ctx The tracing context. - */ - public monikersByPosition( - path: string, - position: lsp.Position, - ctx: TracingContext = {} - ): Promise { - return this.request( - 'monikersByPosition', - new URLSearchParams({ path, line: String(position.line), character: String(position.character) }), - ctx - ) - } - - /** - * Query the definitions or references table of `db` for items that match the given moniker. - * Convert each result into an `InternalLocation`. The `pathTransformer` function is invoked - * on each result item to modify the resulting locations. - * - * @param model The constructor for the model type. - * @param moniker The target moniker. - * @param pagination A limit and offset to use for the query. - * @param ctx The tracing context. - */ - public async monikerResults( - model: typeof sqliteModels.DefinitionModel | typeof sqliteModels.ReferenceModel, - moniker: Pick, - pagination: { skip?: number; take?: number }, - ctx: TracingContext = {} - ): Promise<{ locations: InternalLocation[]; count: number }> { - let p: {} | { skip: string } | { take: string } | { skip: string; take: string } = {} - if (pagination.skip !== undefined) { - p = { ...p, skip: String(pagination.skip) } - } - if (pagination.take !== undefined) { - p = { ...p, take: String(pagination.take) } - } - - const { locations, count } = await this.request<{ - locations: { path: string; range: lsp.Range }[] - count: number - }>( - 'monikerResults', - new URLSearchParams({ - modelType: model === sqliteModels.DefinitionModel ? 'definition' : 'reference', - scheme: moniker.scheme, - identifier: moniker.identifier, - ...p, - }), - ctx - ) - - return { locations: locations.map(location => ({ ...location, dumpId: this.dumpId })), count } - } - - /** - * Return the package information data with the given identifier. - * - * @param path The path of the document. - * @param packageInformationId The identifier of the package information data. - * @param ctx The tracing context. - */ - public packageInformation( - path: string, - packageInformationId: sqliteModels.PackageInformationId, - ctx: TracingContext = {} - ): Promise { - return this.request( - 'packageInformation', - new URLSearchParams({ path, packageInformationId: String(packageInformationId) }), - ctx - ) - } - - // - // - - private async request(method: string, searchParams: URLSearchParams, ctx: TracingContext): Promise { - const url = new URL(`/dbs/${this.dumpId}/${method}`, settings.PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL) - url.search = searchParams.toString() - const resp = await got.get(url.href) - return parseJSON(resp.body) - } -} diff --git a/cmd/precise-code-intel/src/api-server/backend/location.ts b/cmd/precise-code-intel/src/api-server/backend/location.ts deleted file mode 100644 index 42a1637d246..00000000000 --- a/cmd/precise-code-intel/src/api-server/backend/location.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as lsp from 'vscode-languageserver-protocol' -import * as pgModels from '../../shared/models/pg' -import { OrderedSet } from '../../shared/datastructures/orderedset' - -export interface InternalLocation { - /** The identifier of the dump that contains the location. */ - dumpId: pgModels.DumpId - /** The path relative to the dump root. */ - path: string - range: lsp.Range -} - -export interface ResolvedInternalLocation { - /** The dump that contains the location. */ - dump: pgModels.LsifDump - /** The path relative to the dump root. */ - path: string - range: lsp.Range -} - -/** A duplicate-free list of locations ordered by time of insertion. */ -export class OrderedLocationSet extends OrderedSet { - /** - * Create a new ordered locations set. - * - * @param values A set of values used to seed the set. - */ - constructor(values?: InternalLocation[]) { - super( - (value: InternalLocation): string => - [ - value.dumpId, - value.path, - value.range.start.line, - value.range.start.character, - value.range.end.line, - value.range.end.character, - ].join(':'), - values - ) - } -} diff --git a/cmd/precise-code-intel/src/api-server/metrics.ts b/cmd/precise-code-intel/src/api-server/metrics.ts deleted file mode 100644 index c02bb710ea5..00000000000 --- a/cmd/precise-code-intel/src/api-server/metrics.ts +++ /dev/null @@ -1,40 +0,0 @@ -import promClient from 'prom-client' - -// -// HTTP Metrics - -export const httpUploadDurationHistogram = new promClient.Histogram({ - name: 'lsif_http_upload_request_duration_seconds', - help: 'Total time spent on upload requests.', - labelNames: ['code'], - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -export const httpQueryDurationHistogram = new promClient.Histogram({ - name: 'lsif_http_query_request_duration_seconds', - help: 'Total time spent on query requests.', - labelNames: ['code'], - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -// -// Database Metrics - -export const databaseQueryDurationHistogram = new promClient.Histogram({ - name: 'lsif_database_query_duration_seconds', - help: 'Total time spent on database queries.', - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -export const databaseQueryErrorsCounter = new promClient.Counter({ - name: 'lsif_database_query_errors_total', - help: 'The number of errors that occurred during a database query.', -}) - -// -// Unconverted Upload Metrics - -export const unconvertedUploadSizeGauge = new promClient.Gauge({ - name: 'lsif_unconverted_upload_size', - help: 'The current number of uploads that have are pending conversion.', -}) diff --git a/cmd/precise-code-intel/src/api-server/routes/internal.ts b/cmd/precise-code-intel/src/api-server/routes/internal.ts deleted file mode 100644 index 643e1d6bd53..00000000000 --- a/cmd/precise-code-intel/src/api-server/routes/internal.ts +++ /dev/null @@ -1,95 +0,0 @@ -import express from 'express' -import { wrap } from 'async-middleware' -import { UploadManager } from '../../shared/store/uploads' -import { DumpManager } from '../../shared/store/dumps' -import { EntityManager } from 'typeorm' -import { SRC_FRONTEND_INTERNAL } from '../../shared/config/settings' -import { TracingContext, addTags } from '../../shared/tracing' -import { Span } from 'opentracing' -import { Logger } from 'winston' -import { updateCommitsAndDumpsVisibleFromTip } from '../../shared/visibility' -import { json } from 'body-parser' - -/** - * Create a router containing the endpoints used by the bundle manager. - * - * @param dumpManager The dumps manager instance. - * @param uploadManager The uploads manager instance. - * @param logger The logger instance. - */ -export function createInternalRouter( - dumpManager: DumpManager, - uploadManager: UploadManager, - logger: Logger -): express.Router { - const router = express.Router() - - /** - * Create a tracing context from the request logger and tracing span - * tagged with the given values. - * - * @param req The express request. - * @param tags The tags to apply to the logger and span. - */ - const createTracingContext = ( - req: express.Request & { span?: Span }, - tags: { [K: string]: unknown } - ): TracingContext => addTags({ logger, span: req.span }, tags) - - interface StatesBody { - ids: number[] - } - - type StatesResponse = Map - - router.post( - '/uploads', - json(), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { ids }: StatesBody = req.body - res.json(await dumpManager.getUploadStates(ids)) - } - ) - ) - - type PruneResponse = { id: number } | null - - router.post( - '/prune', - wrap( - async (req: express.Request, res: express.Response): Promise => { - const ctx = createTracingContext(req, {}) - - const dump = await dumpManager.getOldestPrunableDump() - if (!dump) { - res.json(null) - return - } - - logger.info('Pruning dump', { - repository: dump.repositoryId, - commit: dump.commit, - root: dump.root, - }) - - // This delete cascades to the packages and references tables as well - await uploadManager.deleteUpload( - dump.id, - (entityManager: EntityManager, repositoryId: number): Promise => - updateCommitsAndDumpsVisibleFromTip({ - entityManager, - dumpManager, - frontendUrl: SRC_FRONTEND_INTERNAL, - repositoryId, - ctx, - }) - ) - - res.json({ id: dump.id }) - } - ) - ) - - return router -} diff --git a/cmd/precise-code-intel/src/api-server/routes/lsif.ts b/cmd/precise-code-intel/src/api-server/routes/lsif.ts deleted file mode 100644 index 695ffd4a90b..00000000000 --- a/cmd/precise-code-intel/src/api-server/routes/lsif.ts +++ /dev/null @@ -1,321 +0,0 @@ -import * as constants from '../../shared/constants' -import * as fs from 'mz/fs' -import * as lsp from 'vscode-languageserver-protocol' -import * as nodepath from 'path' -import * as settings from '../settings' -import * as validation from '../../shared/api/middleware/validation' -import express from 'express' -import * as uuid from 'uuid' -import { addTags, logAndTraceCall, TracingContext } from '../../shared/tracing' -import { Backend } from '../backend/backend' -import { encodeCursor } from '../../shared/api/pagination/cursor' -import { Logger } from 'winston' -import { nextLink } from '../../shared/api/pagination/link' -import { pipeline as _pipeline } from 'stream' -import { promisify } from 'util' -import { Span, Tracer } from 'opentracing' -import { wrap } from 'async-middleware' -import { extractLimitOffset } from '../../shared/api/pagination/limit-offset' -import { UploadManager } from '../../shared/store/uploads' -import { readGzippedJsonElementsFromFile } from '../../shared/input' -import * as lsif from 'lsif-protocol' -import { ReferencePaginationCursor } from '../backend/cursor' -import { LsifUpload } from '../../shared/models/pg' -import got from 'got' -import { Connection } from 'typeorm' - -const pipeline = promisify(_pipeline) - -/** - * Create a router containing the LSIF upload and query endpoints. - * - * @param connection The Postgres connection. - * @param backend The backend instance. - * @param uploadManager The uploads manager instance. - * @param logger The logger instance. - * @param tracer The tracer instance. - */ -export function createLsifRouter( - connection: Connection, - backend: Backend, - uploadManager: UploadManager, - logger: Logger, - tracer: Tracer | undefined -): express.Router { - const router = express.Router() - - // Used to validate commit hashes are 40 hex digits - const commitPattern = /^[a-f0-9]{40}$/ - - /** - * Ensure roots end with a slash, unless it refers to the top-level directory. - * - * @param root The input root. - */ - const sanitizeRoot = (root: string | undefined): string => { - if (root === undefined || root === '/' || root === '') { - return '' - } - - return root.endsWith('/') ? root : root + '/' - } - - /** - * Create a tracing context from the request logger and tracing span - * tagged with the given values. - * - * @param req The express request. - * @param tags The tags to apply to the logger and span. - */ - const createTracingContext = ( - req: express.Request & { span?: Span }, - tags: { [K: string]: unknown } - ): TracingContext => addTags({ logger, span: req.span }, tags) - - interface UploadQueryArgs { - repositoryId: number - commit: string - root?: string - indexerName?: string - } - - interface UploadResponse { - id: string - } - - router.post( - '/upload', - validation.validationMiddleware([ - validation.validateInt('repositoryId'), - validation.validateNonEmptyString('commit').matches(commitPattern), - validation.validateOptionalString('root'), - validation.validateOptionalString('indexerName'), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { repositoryId, commit, root: rootRaw, indexerName }: UploadQueryArgs = req.query - - const root = sanitizeRoot(rootRaw) - const ctx = createTracingContext(req, { repositoryId, commit, root }) - const filename = nodepath.join(settings.STORAGE_ROOT, uuid.v4()) - const output = fs.createWriteStream(filename) - await logAndTraceCall(ctx, 'Receiving dump', () => pipeline(req, output)) - - try { - const indexer = indexerName || (await findIndexer(filename)) - if (!indexer) { - throw new Error('Could not find tool type in metadata vertex at the start of the dump.') - } - - const id = await connection.transaction(async entityManager => { - // Add upload record - const uploadId = await uploadManager.enqueue( - { repositoryId, commit, root, indexer }, - entityManager - ) - - // Upload the payload file where it can be found by the worker - await logAndTraceCall(ctx, 'Uploading payload to bundle manager', () => - pipeline( - fs.createReadStream(filename), - got.stream.post( - new URL(`/uploads/${uploadId}`, settings.PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL).href - ) - ) - ) - - return uploadId - }) - - // Upload conversion will complete asynchronously, send an accepted response - // with the upload id so that the client can continue to track the progress - // asynchronously. - res.status(202).send({ id: `${id}` }) - } finally { - // Remove local file - await fs.unlink(filename) - } - } - ) - ) - - interface ExistsQueryArgs { - repositoryId: number - commit: string - path: string - } - - interface ExistsResponse { - uploads: LsifUpload[] - } - - router.get( - '/exists', - validation.validationMiddleware([ - validation.validateInt('repositoryId'), - validation.validateNonEmptyString('commit').matches(commitPattern), - validation.validateNonEmptyString('path'), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { repositoryId, commit, path }: ExistsQueryArgs = req.query - const ctx = createTracingContext(req, { repositoryId, commit }) - const uploads = await backend.exists(repositoryId, commit, path, ctx) - res.json({ uploads }) - } - ) - ) - - interface FilePositionArgs { - repositoryId: number - commit: string - path: string - line: number - character: number - uploadId: number - } - - interface LocationsResponse { - locations: { repositoryId: number; commit: string; path: string; range: lsp.Range }[] - } - - router.get( - '/definitions', - validation.validationMiddleware([ - validation.validateInt('repositoryId'), - validation.validateNonEmptyString('commit'), - validation.validateNonEmptyString('path'), - validation.validateInt('line'), - validation.validateInt('character'), - validation.validateInt('uploadId'), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { repositoryId, commit, path, line, character, uploadId }: FilePositionArgs = req.query - const ctx = createTracingContext(req, { repositoryId, commit, path }) - - const locations = await backend.definitions( - repositoryId, - commit, - path, - { line, character }, - uploadId, - ctx - ) - if (locations === undefined) { - throw Object.assign(new Error('LSIF upload not found'), { status: 404 }) - } - - res.send({ - locations: locations.map(l => ({ - repositoryId: l.dump.repositoryId, - commit: l.dump.commit, - path: l.path, - range: l.range, - })), - }) - } - ) - ) - - interface ReferencesQueryArgs extends FilePositionArgs { - commit: string - cursor: ReferencePaginationCursor | undefined - } - - router.get( - '/references', - validation.validationMiddleware([ - validation.validateInt('repositoryId'), - validation.validateNonEmptyString('commit'), - validation.validateNonEmptyString('path'), - validation.validateInt('line'), - validation.validateInt('character'), - validation.validateInt('uploadId'), - validation.validateLimit, - validation.validateCursor(), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { repositoryId, commit, path, line, character, uploadId, cursor }: ReferencesQueryArgs = req.query - const { limit } = extractLimitOffset(req.query, settings.DEFAULT_REFERENCES_PAGE_SIZE) - const ctx = createTracingContext(req, { repositoryId, commit, path }) - - const result = await backend.references( - repositoryId, - commit, - path, - { line, character }, - { limit, cursor }, - constants.DEFAULT_REFERENCES_REMOTE_DUMP_LIMIT, - uploadId, - ctx - ) - if (result === undefined) { - throw Object.assign(new Error('LSIF upload not found'), { status: 404 }) - } - - const { locations, newCursor } = result - const encodedCursor = encodeCursor(newCursor) - if (encodedCursor) { - res.set('Link', nextLink(req, { limit, cursor: encodedCursor })) - } - - res.json({ - locations: locations.map(l => ({ - repositoryId: l.dump.repositoryId, - commit: l.dump.commit, - path: l.path, - range: l.range, - })), - }) - } - ) - ) - - type HoverResponse = { text: string; range: lsp.Range } | null - - router.get( - '/hover', - validation.validationMiddleware([ - validation.validateInt('repositoryId'), - validation.validateNonEmptyString('commit'), - validation.validateNonEmptyString('path'), - validation.validateInt('line'), - validation.validateInt('character'), - validation.validateInt('uploadId'), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { repositoryId, commit, path, line, character, uploadId }: FilePositionArgs = req.query - const ctx = createTracingContext(req, { repositoryId, commit, path }) - - const result = await backend.hover(repositoryId, commit, path, { line, character }, uploadId, ctx) - if (result === undefined) { - throw Object.assign(new Error('LSIF upload not found'), { status: 404 }) - } - - res.json(result) - } - ) - ) - - return router -} - -/** - * Read and decode the first entry of the dump. If the entry exists, encodes a metadata vertex, - * and contains a tool info name field, return the contents of that field; otherwise undefined. - * - * @param filename The filename to read. - */ -async function findIndexer(filename: string): Promise { - for await (const element of readGzippedJsonElementsFromFile(filename) as AsyncIterable) { - if (element.type === lsif.ElementTypes.vertex && element.label === lsif.VertexLabels.metaData) { - return element.toolInfo?.name - } - break - } - - return undefined -} diff --git a/cmd/precise-code-intel/src/api-server/routes/uploads.ts b/cmd/precise-code-intel/src/api-server/routes/uploads.ts deleted file mode 100644 index 14303f701ce..00000000000 --- a/cmd/precise-code-intel/src/api-server/routes/uploads.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as pgModels from '../../shared/models/pg' -import * as settings from '../settings' -import * as validation from '../../shared/api/middleware/validation' -import express from 'express' -import { nextLink } from '../../shared/api/pagination/link' -import { wrap } from 'async-middleware' -import { extractLimitOffset } from '../../shared/api/pagination/limit-offset' -import { UploadManager, LsifUploadWithPlaceInQueue } from '../../shared/store/uploads' -import { DumpManager } from '../../shared/store/dumps' -import { EntityManager } from 'typeorm' -import { SRC_FRONTEND_INTERNAL } from '../../shared/config/settings' -import { TracingContext, addTags } from '../../shared/tracing' -import { Span } from 'opentracing' -import { Logger } from 'winston' -import { updateCommitsAndDumpsVisibleFromTip } from '../../shared/visibility' - -/** - * Create a router containing the upload endpoints. - * - * @param dumpManager The dumps manager instance. - * @param uploadManager The uploads manager instance. - * @param logger The logger instance. - */ -export function createUploadRouter( - dumpManager: DumpManager, - uploadManager: UploadManager, - logger: Logger -): express.Router { - const router = express.Router() - - /** - * Create a tracing context from the request logger and tracing span - * tagged with the given values. - * - * @param req The express request. - * @param tags The tags to apply to the logger and span. - */ - const createTracingContext = ( - req: express.Request & { span?: Span }, - tags: { [K: string]: unknown } - ): TracingContext => addTags({ logger, span: req.span }, tags) - - interface UploadsQueryArgs { - query: string - state?: pgModels.LsifUploadState - visibleAtTip?: boolean - } - - type UploadResponse = LsifUploadWithPlaceInQueue - - router.get( - '/uploads/:id([0-9]+)', - wrap( - async (req: express.Request, res: express.Response): Promise => { - const upload = await uploadManager.getUpload(parseInt(req.params.id, 10)) - if (upload) { - res.send(upload) - return - } - - throw Object.assign(new Error('Upload not found'), { - status: 404, - }) - } - ) - ) - - router.delete( - '/uploads/:id([0-9]+)', - wrap( - async (req: express.Request, res: express.Response): Promise => { - const id = parseInt(req.params.id, 10) - const ctx = createTracingContext(req, { id }) - - const updateVisibility = (entityManager: EntityManager, repositoryId: number): Promise => - updateCommitsAndDumpsVisibleFromTip({ - entityManager, - dumpManager, - frontendUrl: SRC_FRONTEND_INTERNAL, - repositoryId, - ctx, - }) - - if (await uploadManager.deleteUpload(id, updateVisibility)) { - res.status(204).send() - return - } - - throw Object.assign(new Error('Upload not found'), { - status: 404, - }) - } - ) - ) - - interface UploadsResponse { - uploads: LsifUploadWithPlaceInQueue[] - totalCount: number - } - - router.get( - '/uploads/repository/:id([0-9]+)', - validation.validationMiddleware([ - validation.validateQuery, - validation.validateLsifUploadState, - validation.validateOptionalBoolean('visibleAtTip'), - validation.validateLimit, - validation.validateOffset, - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { query, state, visibleAtTip }: UploadsQueryArgs = req.query - const { limit, offset } = extractLimitOffset(req.query, settings.DEFAULT_UPLOAD_PAGE_SIZE) - const { uploads, totalCount } = await uploadManager.getUploads( - parseInt(req.params.id, 10), - state, - query, - !!visibleAtTip, - limit, - offset - ) - - if (offset + uploads.length < totalCount) { - res.set('Link', nextLink(req, { limit, offset: offset + uploads.length })) - } - - res.json({ uploads, totalCount }) - } - ) - ) - - return router -} diff --git a/cmd/precise-code-intel/src/api-server/settings.ts b/cmd/precise-code-intel/src/api-server/settings.ts deleted file mode 100644 index 4cf4933d95c..00000000000 --- a/cmd/precise-code-intel/src/api-server/settings.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { readEnvInt } from '../shared/settings' - -/** Which port to run the LSIF API server on. Defaults to 3186. */ -export const HTTP_PORT = readEnvInt('HTTP_PORT', 3186) - -/** HTTP address for internal LSIF bundle manager server. */ -export const PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL = - process.env.PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL || 'http://localhost:3187' - -/** Where on the file system to temporarily store LSIF uploads. This need not be a persistent volume. */ -export const STORAGE_ROOT = process.env.LSIF_STORAGE_ROOT || 'lsif-storage' - -/** The default number of results to return from the upload endpoints. */ -export const DEFAULT_UPLOAD_PAGE_SIZE = readEnvInt('DEFAULT_UPLOAD_PAGE_SIZE', 50) - -/** The default number of results to return from the dumps endpoint. */ -export const DEFAULT_DUMP_PAGE_SIZE = readEnvInt('DEFAULT_DUMP_PAGE_SIZE', 50) - -/** The default number of location results to return when performing a find-references operation. */ -export const DEFAULT_REFERENCES_PAGE_SIZE = readEnvInt('DEFAULT_REFERENCES_PAGE_SIZE', 100) - -/** The interval (in seconds) to invoke the updateQueueSizeGaugeInterval task. */ -export const UPDATE_QUEUE_SIZE_GAUGE_INTERVAL = readEnvInt('UPDATE_QUEUE_SIZE_GAUGE_INTERVAL', 5) - -/** The interval (in seconds) to run the resetStalledUploads task. */ -export const RESET_STALLED_UPLOADS_INTERVAL = readEnvInt('RESET_STALLED_UPLOADS_INTERVAL', 60) - -/** The maximum age (in seconds) that an upload can be unlocked and in the `processing` state. */ -export const STALLED_UPLOAD_MAX_AGE = readEnvInt('STALLED_UPLOAD_MAX_AGE', 5) - -/** The interval (in seconds) to invoke the cleanOldUploads task. */ -export const CLEAN_OLD_UPLOADS_INTERVAL = readEnvInt('CLEAN_OLD_UPLOADS_INTERVAL', 60 * 60 * 8) // 8 hours - -/** The maximum age (in seconds) that an upload (completed or queued) will remain in Postgres. */ -export const UPLOAD_MAX_AGE = readEnvInt('UPLOAD_UPLOAD_AGE', 60 * 60 * 24 * 7) // 1 week diff --git a/cmd/precise-code-intel/src/api-server/tasks.ts b/cmd/precise-code-intel/src/api-server/tasks.ts deleted file mode 100644 index 92e01178a81..00000000000 --- a/cmd/precise-code-intel/src/api-server/tasks.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as settings from './settings' -import { Connection } from 'typeorm' -import { Logger } from 'winston' -import { UploadManager } from '../shared/store/uploads' -import { ExclusivePeriodicTaskRunner } from '../shared/tasks' -import * as metrics from './metrics' -import { createSilentLogger } from '../shared/logging' -import { TracingContext } from '../shared/tracing' - -/** - * Begin running cleanup tasks on a schedule in the background. - * - * @param connection The Postgres connection. - * @param uploadManager The uploads manager instance. - * @param logger The logger instance. - */ -export function startTasks(connection: Connection, uploadManager: UploadManager, logger: Logger): void { - const runner = new ExclusivePeriodicTaskRunner(connection, logger) - - runner.register({ - name: 'Updating metrics', - intervalMs: settings.UPDATE_QUEUE_SIZE_GAUGE_INTERVAL, - task: () => updateQueueSizeGauge(uploadManager), - silent: true, - }) - - runner.register({ - name: 'Resetting stalled uploads', - intervalMs: settings.RESET_STALLED_UPLOADS_INTERVAL, - task: ({ ctx }) => resetStalledUploads(uploadManager, ctx), - }) - - runner.register({ - name: 'Cleaning old uploads', - intervalMs: settings.CLEAN_OLD_UPLOADS_INTERVAL, - task: ({ ctx }) => cleanOldUploads(uploadManager, ctx), - }) - - runner.run() -} - -/** - * Update the value of the unconverted uploads gauge. - * - * @param uploadManager The uploads manager instance. - */ -async function updateQueueSizeGauge(uploadManager: UploadManager): Promise { - metrics.unconvertedUploadSizeGauge.set(await uploadManager.getCount('queued')) -} - -/** - * Move all unlocked uploads that have been in `processing` state for longer than - * `STALLED_UPLOAD_MAX_AGE` back to the `queued` state. - * - * @param uploadManager The uploads manager instance. - * @param ctx The tracing context. - */ -async function resetStalledUploads( - uploadManager: UploadManager, - { logger = createSilentLogger() }: TracingContext -): Promise { - for (const id of await uploadManager.resetStalled(settings.STALLED_UPLOAD_MAX_AGE)) { - logger.debug('Reset stalled upload conversion', { id }) - } -} - -/** - * Remove all upload data older than `UPLOAD_MAX_AGE`. - * - * @param uploadManager The uploads manager instance. - * @param ctx The tracing context. - */ -async function cleanOldUploads( - uploadManager: UploadManager, - { logger = createSilentLogger() }: TracingContext -): Promise { - const count = await uploadManager.clean(settings.UPLOAD_MAX_AGE) - if (count > 0) { - logger.debug('Cleaned old uploads', { count }) - } -} diff --git a/cmd/precise-code-intel/src/bundle-manager/backend/cache.test.ts b/cmd/precise-code-intel/src/bundle-manager/backend/cache.test.ts deleted file mode 100644 index 7bfeb8687de..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/backend/cache.test.ts +++ /dev/null @@ -1,238 +0,0 @@ -import * as sinon from 'sinon' -import promClient from 'prom-client' -import { createBarrierPromise, GenericCache } from './cache' - -describe('GenericCache', () => { - const testCacheSizeGauge = new promClient.Gauge({ - name: 'test_cache_size', - help: 'test_cache_size', - }) - - const testCacheEventsCounter = new promClient.Counter({ - name: 'test_cache_events_total', - help: 'test_cache_events_total', - labelNames: ['type'], - }) - - const testMetrics = { - sizeGauge: testCacheSizeGauge, - eventsCounter: testCacheEventsCounter, - } - - it('should evict items based by reverse recency', async () => { - const values = [ - 'foo', // foo* - 'bar', // bar* foo - 'baz', // baz* bar foo - 'bonk', // bonk* baz bar foo - 'quux', // quux* bonk baz bar foo - 'bar', // bar quux bonk baz foo - 'foo', // foo bar quux bonk baz - 'honk', // honk* foo bar quux bonk - 'foo', // foo honk bar quux bonk - 'baz', // baz* foo honk bar quux - ] - - // These are the cache values that need to be created, in-order - const expectedInstantiations = ['foo', 'bar', 'baz', 'bonk', 'quux', 'honk', 'baz'] - - const factory = sinon.stub>() - for (const [i, value] of expectedInstantiations.entries()) { - // Log the value arg and resolve the cache data immediately - factory.onCall(i).resolves(value) - } - - const cache = new GenericCache( - 5, - () => 1, - () => { - /* noop */ - }, - testMetrics - ) - for (const value of values) { - const returnValue = await cache.withValue( - value, - () => factory(value), - v => Promise.resolve(v) - ) - expect(returnValue).toBe(value) - } - - // Expect the args of the factory to equal the resolved values - expect(factory.args).toEqual(expectedInstantiations.map(v => [v])) - }) - - it('should asynchronously resolve cache values', async () => { - const factory = sinon.stub>() - const { wait, done } = createBarrierPromise() - factory.returns(wait.then(() => 'bar')) - - const cache = new GenericCache( - 5, - () => 1, - () => { - /* noop */ - }, - testMetrics - ) - const p1 = cache.withValue('foo', factory, v => Promise.resolve(v)) - const p2 = cache.withValue('foo', factory, v => Promise.resolve(v)) - const p3 = cache.withValue('foo', factory, v => Promise.resolve(v)) - done() - - expect(await Promise.all([p1, p2, p3])).toEqual(['bar', 'bar', 'bar']) - expect(factory.callCount).toEqual(1) - }) - - it('should call dispose function on eviction', async () => { - const values = [ - 'foo', // foo - 'bar', // bar foo - 'baz', // baz bar (drops foo) - 'foo', // foo baz (drops bar) - ] - - const { wait, done } = createBarrierPromise() - const disposer = sinon.spy(done) - const cache = new GenericCache(2, () => 1, disposer, testMetrics) - - for (const value of values) { - await cache.withValue( - value, - () => Promise.resolve(value), - v => Promise.resolve(v) - ) - } - - await wait - expect(disposer.args).toEqual([['foo'], ['bar']]) - }) - - it('should calculate size by resolved value', async () => { - const values = [ - 2, // 2, size = 2 - 3, // 3 2, size = 5 - 1, // 1 3, size = 4 - 2, // 1 2, size = 3 - ] - - const expectedInstantiations = [2, 3, 1, 2] - - const factory = sinon.stub>() - for (const [i, value] of expectedInstantiations.entries()) { - factory.onCall(i).resolves(value) - } - - const cache = new GenericCache( - 5, - v => v, - () => { - /* noop */ - }, - testMetrics - ) - for (const value of values) { - await cache.withValue( - value, - () => factory(value), - v => Promise.resolve(v) - ) - } - - expect(factory.args).toEqual(expectedInstantiations.map(v => [v])) - }) - - it('should not evict referenced cache entries', async () => { - const { wait, done } = createBarrierPromise() - const disposer = sinon.spy(done) - const cache = new GenericCache(5, () => 1, disposer, testMetrics) - - const fooResolver = () => Promise.resolve('foo') - const barResolver = () => Promise.resolve('bar') - const bazResolver = () => Promise.resolve('baz') - const bonkResolver = () => Promise.resolve('bonk') - const quuxResolver = () => Promise.resolve('quux') - const honkResolver = () => Promise.resolve('honk') - const ronkResolver = () => Promise.resolve('ronk') - - await cache.withValue('foo', fooResolver, async () => { - await cache.withValue('bar', barResolver, async () => { - await cache.withValue('baz', bazResolver, async () => { - await cache.withValue('bonk', bonkResolver, async () => { - await cache.withValue('quux', quuxResolver, async () => { - // Sixth entry, but nothing to evict (all held) - await cache.withValue('honk', honkResolver, () => Promise.resolve()) - - // Seventh entry, honk can now be removed as it's the least - // recently used value that's not currently under a read lock. - await cache.withValue('ronk', ronkResolver, () => Promise.resolve()) - }) - }) - }) - }) - }) - - // Release and remove the least recently used - - await cache.withValue( - 'honk', - () => Promise.resolve('honk'), - async () => { - await wait - expect(disposer.args).toEqual([['honk'], ['foo'], ['bar']]) - } - ) - }) - - it('should dispose busted keys', async () => { - const { wait, done } = createBarrierPromise() - const disposer = sinon.spy(done) - const cache = new GenericCache(5, () => 1, disposer, testMetrics) - - const factory = sinon.stub>() - factory.resolves('foo') - - // Construct then bust a same key - await cache.withValue('foo', factory, () => Promise.resolve()) - await cache.bustKey('foo') - await wait - - // Ensure value was disposed - expect(disposer.args).toEqual([['foo']]) - - // Ensure entry was removed - expect(cache.withValue('foo', factory, () => Promise.resolve())) - expect(factory.args).toHaveLength(2) - }) - - it('should wait to dispose busted keys that are in use', async () => { - const { wait: wait1, done: done1 } = createBarrierPromise() - const { wait: wait2, done: done2 } = createBarrierPromise() - - const resolver = () => Promise.resolve('foo') - const disposer = sinon.spy(done1) - const cache = new GenericCache(5, () => 1, disposer, testMetrics) - - // Create a cache entry for 'foo' that blocks on done2 - const p1 = cache.withValue('foo', resolver, () => wait2) - - // Attempt to bust the cache key that's used in the blocking promise above - const p2 = cache.bustKey('foo') - - // Ensure that p1 and p2 are blocked on each other - const timedResolver = new Promise(resolve => setTimeout(() => resolve('$'), 10)) - const winner = await Promise.race([p1, p2, timedResolver]) - expect(winner).toEqual('$') - - // Ensure dispose hasn't been called - expect(disposer.args).toHaveLength(0) - - // Unblock p1 - done2() - - // Show that all promises are unblocked and dispose was called - await Promise.all([p1, p2, wait1]) - expect(disposer.args).toEqual([['foo']]) - }) -}) diff --git a/cmd/precise-code-intel/src/bundle-manager/backend/cache.ts b/cmd/precise-code-intel/src/bundle-manager/backend/cache.ts deleted file mode 100644 index 8793d5ff297..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/backend/cache.ts +++ /dev/null @@ -1,433 +0,0 @@ -import * as sqliteModels from '../../shared/models/sqlite' -import * as metrics from '../metrics' -import promClient from 'prom-client' -import Yallist from 'yallist' -import { Connection, EntityManager } from 'typeorm' -import { createSqliteConnection } from '../../shared/database/sqlite' -import { Logger } from 'winston' - -/** A wrapper around a cache value promise. */ -interface CacheEntry { - /** The key that can retrieve this cache entry. */ - key: K - - /** The promise that will resolve the cache value. */ - promise: Promise - - /** - * The size of the promise value, once resolved. This value is - * initially zero and is updated once an appropriate can be - * determined from the result of `promise`. - */ - size: number - - /** - * The number of active withValue calls referencing this entry. If - * this value is non-zero, it is not evict-able from the cache. - */ - readers: number - - /** - * A function reference that should be called, if present, when - * the reader count for an entry goes to zero. This will unblock a - * a promise created in `bustKey` to wait for all readers to finish - * using the cache value. - */ - waiter: (() => void) | undefined -} - -/** - * A bag of prometheus metric objects that apply to a particular - * instance of `GenericCache`. - */ -interface CacheMetrics { - /** - * A metric incremented on each cache insertion and decremented on - * each cache eviction. - */ - sizeGauge: promClient.Gauge - - /** - * A metric incremented on each cache hit, miss, and eviction. A `type` - * label is applied to differentiate the events. - */ - eventsCounter: promClient.Counter -} - -/** - * A generic LRU cache. We use this instead of the `lru-cache` package - * available in NPM so that we can handle async payloads in a more - * first-class way as well as shedding some of the cruft around evictions. - * We need to ensure database handles are closed when they are no longer - * accessible, and we also do not want to evict any database handle while - * it is actively being used. - */ -export class GenericCache { - /** A map from from keys to nodes in `lruList`. */ - private cache = new Map>>() - - /** A linked list of cache entires ordered by last-touch. */ - private lruList = new Yallist>() - - /** The additive size of the items currently in the cache. */ - private size = 0 - - /** - * Create a new `GenericCache` with the given maximum (soft) size for - * all items in the cache, a function that determine the size of a - * cache item from its resolved value, and a function that is called - * when an item falls out of the cache. - * - * @param max The maximum size of the cache before an eviction. - * @param sizeFunction A function that determines the size of a cache item. - * @param disposeFunction A function that disposes of evicted cache items. - * @param cacheMetrics The bag of metrics to use for this instance of the cache. - */ - constructor( - private max: number, - private sizeFunction: (value: V) => number, - private disposeFunction: (value: V) => Promise | void, - private cacheMetrics: CacheMetrics - ) {} - - /** Remove all values from the cache. */ - public async flush(): Promise { - await Promise.all(Array.from(this.cache.keys()).map(key => this.bustKey(key))) - } - - /** - * Check if `key` exists in the cache. If it does not, create a value - * from `factory`. Once the cache value resolves, invoke `callback` and - * return its value. This method acts as a lock around the cache entry - * so that it may not be removed while the factory or callback functions - * are running. - * - * @param key The cache key. - * @param factory The function used to create a new value. - * @param callback The function to invoke with the resolved cache value. - */ - public async withValue(key: K, factory: () => Promise, callback: (value: V) => Promise): Promise { - // Find or create the entry - const entry = await this.getEntry(key, factory) - - try { - // Re-resolve the promise. If this is already resolved it's a fast - // no-op. Otherwise, we got a cache entry that was under-construction - // and will resolve shortly. - - return await callback(await entry.promise) - } finally { - // Unlock the cache entry - entry.readers-- - - // If we were the last reader and there's a bustKey call waiting on - // us to finish, inform it that we're done using it. Bust away! - - if (entry.readers === 0 && entry.waiter !== undefined) { - entry.waiter() - } - } - } - - /** - * Remove a key from the cache. This blocks until all current readers - * of the cached value have completed, then calls the dispose function. - * - * Do NOT call this function while holding the same: you will deadlock. - * - * @param key The cache key. - */ - public async bustKey(key: K): Promise { - const node = this.cache.get(key) - if (!node) { - return - } - - const { - value: { promise, size, readers }, - } = node - - // Immediately remove from cache so that another reader cannot get - // ahold of the value, and so that another bust attempt cannot call - // dispose twice on the same value. - - this.removeNode(node, size) - - // Wait for the value to resolve. We do this first in case the value - // was still under construction. This simplifies the rest of the logic - // below, as readers can never be negative once the promise value has - // resolved. - - const value = await promise - - if (readers > 0) { - // There's someone holding the cache value. Create a barrier promise - // and stash the function that can unlock it. When the reader count - // for an entry is decremented, the waiter function, if present, is - // invoked. This basically forms a condition variable. - - const { wait, done } = createBarrierPromise() - node.value.waiter = done - await wait - } - - // We have the resolved value, removed from the cache, which is no longer - // used by any reader. It's safe to dispose now. - await this.disposeFunction(value) - } - - /** - * Check if `key` exists in the cache. If it does not, create a value - * from `factory` and add it to the cache. In either case, update the - * cache entry's place in `lruCache` and return the entry. If a new - * value was created, then it may trigger a cache eviction once its - * value resolves. - * - * @param key The cache key. - * @param factory The function used to create a new value. - */ - private async getEntry(key: K, factory: () => Promise): Promise> { - const node = this.cache.get(key) - if (node) { - // Move to head of list - this.lruList.unshiftNode(node) - - // Log cache event - this.cacheMetrics.eventsCounter.labels('hit').inc() - - // Ensure entry is locked before returning - const entry = node.value - entry.readers++ - return entry - } - - // Log cache event - this.cacheMetrics.eventsCounter.labels('miss').inc() - - // Create promise and the entry that wraps it. We don't know the effective - // size of the value until the promise resolves, so we put zero. We have a - // reader count of 1, in order to lock the entry until after the user that - // requested the entry is done using it. We don't want to block here while - // waiting for the promise value to resolve, otherwise a second request for - // the same key will create a duplicate cache entry. - - const promise = factory() - const newEntry = { key, promise, size: 0, readers: 1, waiter: undefined } - - // Add to head of list - this.lruList.unshift(newEntry) - - // Grab the head of the list we just pushed and store it - // in the map. We need the node that the unshift method - // creates so we can unlink it in constant time. - const head = this.lruList.head - if (head) { - this.cache.set(key, head) - } - - // Now that another call to getEntry will find the cache entry - // and early-out, we can block here and wait to resolve the - // value, then update the entry and cache sizes. - - const value = await promise - await this.resolved(newEntry, value) - return newEntry - } - - /** - * Determine the size of the resolved value and update the size of the - * entry as well as `size`. While the total cache size exceeds `max`, - * try to evict the least recently used cache entries that do not have - * a non-zero `readers` count. - * - * @param entry The cache entry. - * @param value The cache entry's resolved value. - */ - private async resolved(entry: CacheEntry, value: V): Promise { - entry.size = this.sizeFunction(value) - this.size += entry.size - this.cacheMetrics.sizeGauge.set(this.size) - - let node = this.lruList.tail - while (this.size > this.max && node) { - const { - prev, - value: { promise, size, readers }, - } = node - - if (readers === 0) { - // If readers > 0, then it may be actively used by another - // part of the code that hit a portion of their critical - // section that returned control to the event loop. We don't - // want to mess with those if we can help it. - - this.removeNode(node, size) - await this.disposeFunction(await promise) - - // Log cache event - this.cacheMetrics.eventsCounter.labels('eviction').inc() - } else { - // Log cache event - this.cacheMetrics.eventsCounter.labels('locked-eviction').inc() - } - - node = prev - } - } - - /** - * Remove the given node from the list and update the cache size. - * - * @param node The node to remove. - * @param size The size of the promise value. - */ - private removeNode(node: Yallist.Node>, size: number): void { - this.size -= size - this.cacheMetrics.sizeGauge.set(this.size) - this.lruList.removeNode(node) - this.cache.delete(node.value.key) - } -} - -/** A cache of SQLite database connections indexed by database filenames. */ -export class ConnectionCache extends GenericCache { - /** - * Create a new `ConnectionCache` with the given maximum (soft) size for - * all items in the cache. - */ - constructor(max: number) { - super( - max, - // Each handle is roughly the same size. - () => 1, - // Close the underlying file handle on cache eviction. - connection => connection.close(), - { - sizeGauge: metrics.connectionCacheSizeGauge, - eventsCounter: metrics.connectionCacheEventsCounter, - } - ) - } - - /** - * Invoke `callback` with a SQLite connection object obtained from the - * cache or created on cache miss. This connection is guaranteed not to - * be disposed by cache eviction while the callback is active. - * - * @param database The database filename. - * @param entities The set of entities to create on a new connection. - * @param logger The logger instance. - * @param callback The function invoke with the SQLite connection. - */ - public withConnection( - database: string, - // Decorators are not possible type check - // eslint-disable-next-line @typescript-eslint/ban-types - entities: Function[], - logger: Logger, - callback: (connection: Connection) => Promise - ): Promise { - return this.withValue(database, () => createSqliteConnection(database, entities, logger), callback) - } - - /** - * Like `withConnection`, but will open a transaction on the connection - * before invoking the callback. - * - * @param database The database filename. - * @param entities The set of entities to create on a new connection. - * @param logger The logger instance. - * @param callback The function invoke with a SQLite transaction connection. - */ - public withTransactionalEntityManager( - database: string, - // Decorators are not possible type check - // eslint-disable-next-line @typescript-eslint/ban-types - entities: Function[], - logger: Logger, - callback: (entityManager: EntityManager) => Promise - ): Promise { - return this.withConnection(database, entities, logger, connection => connection.transaction(callback)) - } -} - -/** - * A wrapper around a cache value that retains its encoded size. In order to keep - * the in-memory limit of these decoded items, we use this value as the cache entry - * size. This assumes that the size of the encoded text is a good proxy for the size - * of the in-memory representation. - */ -export interface EncodedJsonCacheValue { - /** The size of the encoded value. */ - size: number - - /** The decoded value. */ - data: T -} - -/** A cache of decoded values encoded as JSON and gzipped in a SQLite database. */ -class EncodedJsonCache extends GenericCache> { - /** - * Create a new `EncodedJsonCache` with the given maximum (soft) size for - * all items in the cache. - * - * @param max The maximum size of the cache before an eviction. - * @param cacheMetrics The bag of metrics to use for this instance of the cache. - */ - constructor(max: number, cacheMetrics: CacheMetrics) { - super( - max, - v => v.size, - // Let GC handle the cleanup of the object on cache eviction. - () => { - /* noop */ - }, - cacheMetrics - ) - } -} - -/** - * A cache of deserialized `DocumentData` values indexed by a string containing - * the database path and the path of the document. - */ -export class DocumentCache extends EncodedJsonCache { - /** - * Create a new `DocumentCache` with the given maximum (soft) size for - * all items in the cache. - * - * @param max The maximum size of the cache before an eviction. - */ - constructor(max: number) { - super(max, { - sizeGauge: metrics.documentCacheSizeGauge, - eventsCounter: metrics.documentCacheEventsCounter, - }) - } -} - -/** - * A cache of deserialized `ResultChunkData` values indexed by a string containing - * the database path and the chunk index. - */ -export class ResultChunkCache extends EncodedJsonCache { - /** - * Create a new `ResultChunkCache` with the given maximum (soft) size for - * all items in the cache. - * - * @param max The maximum size of the cache before an eviction. - */ - constructor(max: number) { - super(max, { - sizeGauge: metrics.resultChunkCacheSizeGauge, - eventsCounter: metrics.resultChunkCacheEventsCounter, - }) - } -} - -/** Return a promise and a function pair. The promise resolves once the function is called. */ -export function createBarrierPromise(): { wait: Promise; done: () => void } { - let done!: () => void - const wait = new Promise(resolve => (done = resolve)) - return { wait, done } -} diff --git a/cmd/precise-code-intel/src/bundle-manager/backend/database.test.ts b/cmd/precise-code-intel/src/bundle-manager/backend/database.test.ts deleted file mode 100644 index 27ae44b8c07..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/backend/database.test.ts +++ /dev/null @@ -1,378 +0,0 @@ -import * as sqliteModels from '../../shared/models/sqlite' -import { comparePosition, findRanges, mapRangesToInternalLocations, Database } from './database' -import * as fs from 'mz/fs' -import * as nodepath from 'path' -import { convertLsif } from '../../worker/conversion/importer' -import { PathExistenceChecker } from '../../worker/conversion/existence' -import rmfr from 'rmfr' -import * as uuid from 'uuid' - -describe('Database', () => { - let storageRoot!: string - let database!: Database - - const makeDatabase = async (filename: string): Promise => { - // Create a filesystem read stream for the given test file. This will cover - // the cases where `yarn test` is run from the root or from the lsif directory. - const sourceFile = nodepath.join( - (await fs.exists('cmd')) ? 'cmd/precise-code-intel' : '', - 'test-data', - filename - ) - const databaseFile = nodepath.join(storageRoot, uuid.v4()) - - await convertLsif({ - path: sourceFile, - root: '', - database: databaseFile, - pathExistenceChecker: new PathExistenceChecker({ - repositoryId: 42, - commit: 'ad3507cbeb18d1ed2b8a0f6354dea88a101197f3', - root: '', - }), - }) - - return new Database(1, databaseFile) - } - - beforeAll(async () => { - storageRoot = await fs.mkdtemp('test-', { encoding: 'utf8' }) - database = await makeDatabase('lsif-go@ad3507cb.lsif.gz') - }) - - afterAll(async () => { - if (storageRoot) { - await rmfr(storageRoot) - } - }) - - describe('exists', () => { - it('should check document path', async () => { - expect(await database.exists('cmd/lsif-go/main.go')).toEqual(true) - expect(await database.exists('internal/index/indexer.go')).toEqual(true) - expect(await database.exists('missing.go')).toEqual(false) - }) - }) - - describe('definitions', () => { - it('should correlate definitions', async () => { - // `\ts, err := indexer.Index()` -> `\t Index() (*Stats, error)` - // ^^^^^ ^^^^^ - - expect(await database.definitions('cmd/lsif-go/main.go', { line: 110, character: 22 })).toEqual([ - { - path: 'internal/index/indexer.go', - range: { start: { line: 20, character: 1 }, end: { line: 20, character: 6 } }, - }, - ]) - }) - }) - - describe('references', () => { - it('should correlate references', async () => { - // `func (w *Writer) EmitRange(start, end Pos) (string, error) {` - // ^^^^^^^^^ - // - // -> `\t\trangeID, err := i.w.EmitRange(lspRange(ipos, ident.Name, isQuotedPkgName))` - // ^^^^^^^^^ - // - // -> `\t\t\trangeID, err = i.w.EmitRange(lspRange(ipos, ident.Name, false))` - // ^^^^^^^^^ - - expect((await database.references('protocol/writer.go', { line: 85, character: 20 })).values).toEqual([ - { - path: 'protocol/writer.go', - range: { start: { line: 85, character: 17 }, end: { line: 85, character: 26 } }, - }, - { - path: 'internal/index/indexer.go', - range: { start: { line: 529, character: 22 }, end: { line: 529, character: 31 } }, - }, - { - path: 'internal/index/indexer.go', - range: { start: { line: 380, character: 22 }, end: { line: 380, character: 31 } }, - }, - ]) - }) - }) - - describe('hover', () => { - it('should correlate hover text', async () => { - // `\tcontents, err := findContents(pkgs, p, f, obj)` - // ^^^^^^^^^^^^ - - const ticks = '```' - const docstring = 'findContents returns contents used as hover info for given object.' - const signature = - 'func findContents(pkgs []*Package, p *Package, f *File, obj Object) ([]MarkedString, error)' - - expect(await database.hover('internal/index/indexer.go', { line: 628, character: 20 })).toEqual({ - text: `${ticks}go\n${signature}\n${ticks}\n\n---\n\n${docstring}`, - range: { start: { line: 628, character: 18 }, end: { line: 628, character: 30 } }, - }) - }) - }) - - describe('monikersByPosition', () => { - it('should return correct range and document with monikers', async () => { - // `func NewMetaData(id, root string, info ToolInfo) *MetaData {` - // ^^^^^^^^^^^ - - const monikers = await database.monikersByPosition('protocol/protocol.go', { - line: 92, - character: 10, - }) - - expect(monikers).toHaveLength(1) - expect(monikers[0]).toHaveLength(1) - expect(monikers[0][0]?.kind).toEqual('export') - expect(monikers[0][0]?.scheme).toEqual('gomod') - expect(monikers[0][0]?.identifier).toEqual('github.com/sourcegraph/lsif-go/protocol:NewMetaData') - }) - }) - - describe('monikerResults', () => { - const edgeLocations = [ - { - path: 'protocol/protocol.go', - range: { start: { line: 600, character: 1 }, end: { line: 600, character: 5 } }, - }, - { - path: 'protocol/protocol.go', - range: { start: { line: 644, character: 1 }, end: { line: 644, character: 5 } }, - }, - { - path: 'protocol/protocol.go', - range: { start: { line: 507, character: 1 }, end: { line: 507, character: 5 } }, - }, - { - path: 'protocol/protocol.go', - range: { start: { line: 553, character: 1 }, end: { line: 553, character: 5 } }, - }, - { - path: 'protocol/protocol.go', - range: { start: { line: 462, character: 1 }, end: { line: 462, character: 5 } }, - }, - { - path: 'protocol/protocol.go', - range: { start: { line: 484, character: 1 }, end: { line: 484, character: 5 } }, - }, - { - path: 'protocol/protocol.go', - range: { start: { line: 410, character: 5 }, end: { line: 410, character: 9 } }, - }, - { - path: 'protocol/protocol.go', - range: { start: { line: 622, character: 1 }, end: { line: 622, character: 5 } }, - }, - { - path: 'protocol/protocol.go', - range: { start: { line: 440, character: 1 }, end: { line: 440, character: 5 } }, - }, - { - path: 'protocol/protocol.go', - range: { start: { line: 530, character: 1 }, end: { line: 530, character: 5 } }, - }, - ] - - it('should query definitions table', async () => { - const { locations, count } = await database.monikerResults( - sqliteModels.DefinitionModel, - { - scheme: 'gomod', - identifier: 'github.com/sourcegraph/lsif-go/protocol:Edge', - }, - {} - ) - - expect(locations).toEqual(edgeLocations) - expect(count).toEqual(10) - }) - - it('should respect pagination', async () => { - const { locations, count } = await database.monikerResults( - sqliteModels.DefinitionModel, - { - scheme: 'gomod', - identifier: 'github.com/sourcegraph/lsif-go/protocol:Edge', - }, - { skip: 3, take: 4 } - ) - - expect(locations).toEqual(edgeLocations.slice(3, 7)) - expect(count).toEqual(10) - }) - - it('should query references table', async () => { - const { locations, count } = await database.monikerResults( - sqliteModels.ReferenceModel, - { - scheme: 'gomod', - identifier: 'github.com/slimsag/godocmd:ToMarkdown', - }, - {} - ) - - expect(locations).toEqual([ - { - path: 'internal/index/helper.go', - range: { start: { line: 78, character: 6 }, end: { line: 78, character: 16 } }, - }, - ]) - expect(count).toEqual(1) - }) - }) -}) - -describe('findRanges', () => { - it('should return ranges containing position', () => { - const range1 = { - startLine: 0, - startCharacter: 3, - endLine: 0, - endCharacter: 5, - monikerIds: new Set(), - } - const range2 = { - startLine: 1, - startCharacter: 3, - endLine: 1, - endCharacter: 5, - monikerIds: new Set(), - } - const range3 = { - startLine: 2, - startCharacter: 3, - endLine: 2, - endCharacter: 5, - monikerIds: new Set(), - } - const range4 = { - startLine: 3, - startCharacter: 3, - endLine: 3, - endCharacter: 5, - monikerIds: new Set(), - } - const range5 = { - startLine: 4, - startCharacter: 3, - endLine: 4, - endCharacter: 5, - monikerIds: new Set(), - } - - expect(findRanges([range1, range2, range3, range4, range5], { line: 0, character: 4 })).toEqual([range1]) - expect(findRanges([range1, range2, range3, range4, range5], { line: 1, character: 4 })).toEqual([range2]) - expect(findRanges([range1, range2, range3, range4, range5], { line: 2, character: 4 })).toEqual([range3]) - expect(findRanges([range1, range2, range3, range4, range5], { line: 3, character: 4 })).toEqual([range4]) - expect(findRanges([range1, range2, range3, range4, range5], { line: 4, character: 4 })).toEqual([range5]) - }) - - it('should order inner-most ranges first', () => { - const range1 = { - startLine: 0, - startCharacter: 3, - endLine: 4, - endCharacter: 5, - monikerIds: new Set(), - } - const range2 = { - startLine: 1, - startCharacter: 3, - endLine: 3, - endCharacter: 5, - monikerIds: new Set(), - } - const range3 = { - startLine: 2, - startCharacter: 3, - endLine: 2, - endCharacter: 5, - monikerIds: new Set(), - } - const range4 = { - startLine: 5, - startCharacter: 3, - endLine: 5, - endCharacter: 5, - monikerIds: new Set(), - } - const range5 = { - startLine: 6, - startCharacter: 3, - endLine: 6, - endCharacter: 5, - monikerIds: new Set(), - } - - expect(findRanges([range1, range2, range3, range4, range5], { line: 2, character: 4 })).toEqual([ - range3, - range2, - range1, - ]) - }) -}) - -describe('comparePosition', () => { - it('should return the relative order to a range', () => { - const range = { - startLine: 5, - startCharacter: 11, - endLine: 5, - endCharacter: 13, - monikerIds: new Set(), - } - - expect(comparePosition(range, { line: 5, character: 11 })).toEqual(0) - expect(comparePosition(range, { line: 5, character: 12 })).toEqual(0) - expect(comparePosition(range, { line: 5, character: 13 })).toEqual(0) - expect(comparePosition(range, { line: 4, character: 12 })).toEqual(+1) - expect(comparePosition(range, { line: 5, character: 10 })).toEqual(+1) - expect(comparePosition(range, { line: 5, character: 14 })).toEqual(-1) - expect(comparePosition(range, { line: 6, character: 12 })).toEqual(-1) - }) -}) - -describe('mapRangesToInternalLocations', () => { - it('should map ranges to locations', () => { - const ranges = new Map() - ranges.set(1, { - startLine: 1, - startCharacter: 1, - endLine: 1, - endCharacter: 2, - monikerIds: new Set(), - }) - ranges.set(2, { - startLine: 3, - startCharacter: 1, - endLine: 3, - endCharacter: 2, - monikerIds: new Set(), - }) - ranges.set(4, { - startLine: 2, - startCharacter: 1, - endLine: 2, - endCharacter: 2, - monikerIds: new Set(), - }) - - const path = 'src/position.ts' - const locations = mapRangesToInternalLocations(ranges, path, new Set([1, 2, 4])) - expect(locations).toContainEqual({ - path, - range: { start: { line: 1, character: 1 }, end: { line: 1, character: 2 } }, - }) - expect(locations).toContainEqual({ - path, - range: { start: { line: 3, character: 1 }, end: { line: 3, character: 2 } }, - }) - expect(locations).toContainEqual({ - path, - range: { start: { line: 2, character: 1 }, end: { line: 2, character: 2 } }, - }) - expect(locations).toHaveLength(3) - }) -}) diff --git a/cmd/precise-code-intel/src/bundle-manager/backend/database.ts b/cmd/precise-code-intel/src/bundle-manager/backend/database.ts deleted file mode 100644 index be6a7aa3234..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/backend/database.ts +++ /dev/null @@ -1,588 +0,0 @@ -import * as cache from './cache' -import * as sqliteModels from '../../shared/models/sqlite' -import * as lsp from 'vscode-languageserver-protocol' -import * as metrics from '../metrics' -import * as pgModels from '../../shared/models/pg' -import { Connection } from 'typeorm' -import { DefaultMap } from '../../shared/datastructures/default-map' -import { gunzipJSON } from '../../shared/encoding/json' -import { hashKey } from '../../shared/models/hash' -import { instrument } from '../../shared/metrics' -import { logSpan, TracingContext, logAndTraceCall, addTags } from '../../shared/tracing' -import { mustGet } from '../../shared/maps' -import { Logger } from 'winston' -import { createSilentLogger } from '../../shared/logging' -import { InternalLocation, OrderedLocationSet } from './location' -import * as settings from '../settings' - -/** The maximum number of results in a logSpan value. */ -const MAX_SPAN_ARRAY_LENGTH = 20 - -/** A wrapper around operations related to a single SQLite dump. */ -export class Database { - /** - * A static map of database paths to the `numResultChunks` value of their - * metadata row. This map is populated lazily as the values are needed. - */ - private static numResultChunks = new Map() - private static connectionCache = new cache.ConnectionCache(settings.CONNECTION_CACHE_CAPACITY) - private static documentCache = new cache.DocumentCache(settings.DOCUMENT_CACHE_CAPACITY) - private static resultChunkCache = new cache.ResultChunkCache(settings.RESULT_CHUNK_CACHE_CAPACITY) - - /** - * Create a new `Database` with the given dump record, and the SQLite file - * on disk that contains data for a particular repository and commit. - * - * @param dumpId The identifier of the dump for which this database answers queries. - * @param databasePath The path to the database file. - */ - constructor(private dumpId: pgModels.DumpId, private databasePath: string) {} - - /** - * Determine if data exists for a particular document in this database. - * - * @param path The path of the document. - * @param ctx The tracing context. - */ - public exists(path: string, ctx: TracingContext = {}): Promise { - return this.logAndTraceCall( - ctx, - 'Checking if path exists', - async () => (await this.getDocumentByPath(path)) !== undefined - ) - } - - /** - * Return a list of locations that define the symbol at the given position. - * - * @param path The path of the document to which the position belongs. - * @param position The current hover position. - * @param ctx The tracing context. - */ - public async definitions( - path: string, - position: lsp.Position, - ctx: TracingContext = {} - ): Promise { - return this.logAndTraceCall(ctx, 'Fetching definitions', async ctx => { - const { document, ranges } = await this.getRangeByPosition(path, position, ctx) - if (!document || ranges.length === 0) { - return [] - } - - for (const range of ranges) { - if (!range.definitionResultId) { - continue - } - - const definitionResults = await this.getResultById(range.definitionResultId) - this.logSpan(ctx, 'definition_results', { - definitionResultId: range.definitionResultId, - definitionResults: definitionResults.slice(0, MAX_SPAN_ARRAY_LENGTH), - numDefinitionResults: definitionResults.length, - }) - - return this.convertRangesToInternalLocations(path, document, definitionResults) - } - - return [] - }) - } - - /** - * Return a list of unique locations that reference the symbol at the given position. - * - * @param path The path of the document to which the position belongs. - * @param position The current hover position. - * @param ctx The tracing context. - */ - public async references( - path: string, - position: lsp.Position, - ctx: TracingContext = {} - ): Promise { - return this.logAndTraceCall(ctx, 'Fetching references', async ctx => { - const { document, ranges } = await this.getRangeByPosition(path, position, ctx) - if (!document || ranges.length === 0) { - return new OrderedLocationSet() - } - - const locationSet = new OrderedLocationSet() - for (const range of ranges) { - if (range.referenceResultId) { - const referenceResults = await this.getResultById(range.referenceResultId) - this.logSpan(ctx, 'reference_results', { - referenceResultId: range.referenceResultId, - referenceResults: referenceResults.slice(0, MAX_SPAN_ARRAY_LENGTH), - numReferenceResults: referenceResults.length, - }) - - if (referenceResults.length > 0) { - for (const location of await this.convertRangesToInternalLocations( - path, - document, - referenceResults - )) { - locationSet.push(location) - } - } - } - } - - return locationSet - }) - } - - /** - * Return the hover content for the symbol at the given position. - * - * @param path The path of the document to which the position belongs. - * @param position The current hover position. - * @param ctx The tracing context. - */ - public async hover( - path: string, - position: lsp.Position, - ctx: TracingContext = {} - ): Promise<{ text: string; range: lsp.Range } | null> { - return this.logAndTraceCall(ctx, 'Fetching hover', async ctx => { - const { document, ranges } = await this.getRangeByPosition(path, position, ctx) - if (!document || ranges.length === 0) { - return null - } - - for (const range of ranges) { - if (!range.hoverResultId) { - continue - } - - this.logSpan(ctx, 'hover_result', { hoverResultId: range.hoverResultId }) - - // Extract text - const text = mustGet(document.hoverResults, range.hoverResultId, 'hoverResult') - - // Return first defined hover result for the inner-most range. This response - // includes the entire range so that the highlighted portion in the UI can be - // accurate (rather than approximated by the tokenizer). - return { text, range: createRange(range) } - } - - return null - }) - } - - /** - * Return all of the monikers attached to all ranges that contain the given position. The - * resulting list is grouped by range. If multiple ranges contain this position, then the - * list monikers for the inner-most ranges will occur before the outer-most ranges. - * - * @param path The path of the document. - * @param position The user's hover position. - * @param ctx The tracing context. - */ - public async monikersByPosition( - path: string, - position: lsp.Position, - ctx: TracingContext = {} - ): Promise { - const { document, ranges } = await this.getRangeByPosition(path, position, ctx) - if (!document) { - return [] - } - - return ranges.map(range => - Array.from(range.monikerIds).map(monikerId => mustGet(document.monikers, monikerId, 'moniker')) - ) - } - - /** - * Query the definitions or references table of `db` for items that match the given moniker. - * Convert each result into an `InternalLocation`. The `pathTransformer` function is invoked - * on each result item to modify the resulting locations. - * - * @param model The constructor for the model type. - * @param moniker The target moniker. - * @param pagination A limit and offset to use for the query. - * @param ctx The tracing context. - */ - public monikerResults( - model: typeof sqliteModels.DefinitionModel | typeof sqliteModels.ReferenceModel, - moniker: Pick, - pagination: { skip?: number; take?: number }, - ctx: TracingContext = {} - ): Promise<{ locations: InternalLocation[]; count: number }> { - return this.logAndTraceCall(ctx, 'Fetching moniker results', async ctx => { - const [results, count] = await this.withConnection( - connection => - connection - .getRepository(model) - .findAndCount({ - where: { - scheme: moniker.scheme, - identifier: moniker.identifier, - }, - ...pagination, - }), - ctx.logger - ) - - this.logSpan(ctx, 'symbol_results', { - moniker, - results: results.slice(0, MAX_SPAN_ARRAY_LENGTH), - numResults: results.length, - }) - - const locations = results.map(result => ({ - path: result.documentPath, - range: createRange(result), - })) - - return { locations, count } - }) - } - - /** - * Return the package information data with the given identifier. - * - * @param path The path of the document. - * @param packageInformationId The identifier of the package information data. - * @param ctx The tracing context. - */ - public async packageInformation( - path: string, - packageInformationId: string, - ctx: TracingContext = {} - ): Promise { - const document = await this.getDocumentByPath(path, ctx) - if (!document) { - return undefined - } - - return ( - // TODO - normalize ids before we serialize them in the database - document.packageInformation.get(parseInt(packageInformationId, 10)) || - document.packageInformation.get(packageInformationId) - ) - } - - // - // Helper Functions - - /** - * Return a parsed document that describes the given path. The result of this - * method is cached across all database instances. If the document is not found - * it returns undefined; other errors will throw. - * - * @param path The path of the document. - * @param ctx The tracing context. - */ - private async getDocumentByPath( - path: string, - ctx: TracingContext = {} - ): Promise { - const factory = async (): Promise> => { - const document = await this.withConnection( - connection => connection.getRepository(sqliteModels.DocumentModel).findOneOrFail(path), - ctx.logger - ) - - return { - size: document.data.length, - data: await gunzipJSON(document.data), - } - } - - try { - return await Database.documentCache.withValue(`${this.databasePath}::${path}`, factory, document => - Promise.resolve(document.data) - ) - } catch (error) { - if (error.name === 'EntityNotFound') { - return undefined - } - - throw error - } - } - - /** - * Return a parsed document that describes the given path as well as the ranges - * from that document that contains the given position. If multiple ranges are - * returned, then the inner-most ranges will occur before the outer-most ranges. - * - * @param path The path of the document. - * @param position The user's hover position. - * @param ctx The tracing context. - */ - private getRangeByPosition( - path: string, - position: lsp.Position, - ctx: TracingContext = {} - ): Promise<{ document: sqliteModels.DocumentData | undefined; ranges: sqliteModels.RangeData[] }> { - return this.logAndTraceCall(ctx, 'Fetching range by position', async ctx => { - const document = await this.getDocumentByPath(path) - if (!document) { - return { document: undefined, ranges: [] } - } - - const ranges = findRanges(document.ranges.values(), position) - this.logSpan(ctx, 'matching_ranges', { ranges: cleanRanges(ranges) }) - return { document, ranges } - }) - } - - /** - * Convert a set of range-document pairs (from a definition or reference query) into - * a set of `InternalLocation` object. Each pair holds the range identifier as well as - * the document path. For document paths matching the loaded document, find the range - * data locally. For all other paths, find the document in this database and find the - * range in that document. - * - * @param path The path of the document for this query. - * @param document The document object for this query. - * @param resultData A list of range ids and the document they belong to. - */ - private async convertRangesToInternalLocations( - path: string, - document: sqliteModels.DocumentData, - resultData: sqliteModels.DocumentPathRangeId[] - ): Promise { - // Group by document path so we only have to load each document once - const groupedResults = new DefaultMap>(() => new Set()) - - for (const { documentPath, rangeId } of resultData) { - groupedResults.getOrDefault(documentPath).add(rangeId) - } - - let results: InternalLocation[] = [] - for (const [documentPath, rangeIds] of groupedResults) { - if (documentPath === path) { - // If the document path is this document, convert the locations directly - results = results.concat(mapRangesToInternalLocations(document.ranges, path, rangeIds)) - continue - } - - // Otherwise, we need to get the correct document - const sibling = await this.getDocumentByPath(documentPath) - if (!sibling) { - continue - } - - // Then finally convert the locations in the sibling document - results = results.concat(mapRangesToInternalLocations(sibling.ranges, documentPath, rangeIds)) - } - - return results - } - - /** - * Convert a list of ranges with document ids into a list of ranges with - * document paths by looking into the result chunks table and parsing the - * data associated with the given identifier. - * - * @param id The identifier of the definition or reference result. - */ - private async getResultById( - id: sqliteModels.DefinitionReferenceResultId - ): Promise { - const { documentPaths, documentIdRangeIds } = await this.getResultChunkByResultId(id) - const ranges = mustGet(documentIdRangeIds, id, 'documentIdRangeId') - - return ranges.map(range => ({ - documentPath: mustGet(documentPaths, range.documentId, 'documentPath'), - rangeId: range.rangeId, - })) - } - - /** - * Return a parsed result chunk that contains the given identifier. - * - * @param id An identifier contained in the result chunk. - * @param ctx The tracing context. - */ - private async getResultChunkByResultId( - id: sqliteModels.DefinitionReferenceResultId, - ctx: TracingContext = {} - ): Promise { - // Find the result chunk index this id belongs to - const index = hashKey(id, await this.getNumResultChunks()) - - const factory = async (): Promise> => { - const resultChunk = await this.withConnection( - connection => connection.getRepository(sqliteModels.ResultChunkModel).findOneOrFail(index), - ctx.logger - ) - - return { - size: resultChunk.data.length, - data: await gunzipJSON(resultChunk.data), - } - } - - return Database.resultChunkCache.withValue(`${this.databasePath}::${index}`, factory, resultChunk => - Promise.resolve(resultChunk.data) - ) - } - - /** - * Get the `numResultChunks` value from this database's metadata row. - * - * @param ctx The tracing context. - */ - private async getNumResultChunks(ctx: TracingContext = {}): Promise { - const numResultChunks = Database.numResultChunks.get(this.databasePath) - if (numResultChunks !== undefined) { - return numResultChunks - } - - // Not in the shared map, need to query it - const meta = await this.withConnection( - connection => connection.getRepository(sqliteModels.MetaModel).findOneOrFail(1), - ctx.logger - ) - Database.numResultChunks.set(this.databasePath, meta.numResultChunks) - return meta.numResultChunks - } - - /** - * Invoke `callback` with a SQLite connection object obtained from the - * cache or created on cache miss. - * - * @param callback The function invoke with the SQLite connection. - * @param logger The logger instance. - */ - private withConnection( - callback: (connection: Connection) => Promise, - logger: Logger = createSilentLogger() - ): Promise { - return Database.connectionCache.withConnection(this.databasePath, sqliteModels.entities, logger, connection => - instrument(metrics.databaseQueryDurationHistogram, metrics.databaseQueryErrorsCounter, () => - callback(connection) - ) - ) - } - - /** - * Log and trace the execution of a function. - * - * @param ctx The tracing context. - * @param name The name of the span and text of the log message. - * @param f The function to invoke. - */ - private logAndTraceCall(ctx: TracingContext, name: string, f: (ctx: TracingContext) => Promise): Promise { - return logAndTraceCall(addTags(ctx, { dbID: this.dumpId }), name, f) - } - - /** - * Logs an event to the span of the tracing context, if its defined. - * - * @param ctx The tracing context. - * @param event The name of the event. - * @param pairs The values to log. - */ - private logSpan(ctx: TracingContext, event: string, pairs: { [name: string]: unknown }): void { - logSpan(ctx, event, { ...pairs, dbID: this.dumpId }) - } -} - -/** - * Return the set of ranges that contain the given position. If multiple ranges - * are returned, then the inner-most ranges will occur before the outer-most - * ranges. - * - * @param ranges The set of possible ranges. - * @param position The user's hover position. - */ -export function findRanges(ranges: Iterable, position: lsp.Position): sqliteModels.RangeData[] { - const filtered = [] - for (const range of ranges) { - if (comparePosition(range, position) === 0) { - filtered.push(range) - } - } - - return filtered.sort((a, b) => { - if (comparePosition(a, { line: b.startLine, character: b.startCharacter }) === 0) { - return +1 - } - - return -1 - }) -} - -/** - * Compare a position against a range. Returns 0 if the position occurs - * within the range (inclusive bounds), -1 if the position occurs after - * it, and +1 if the position occurs before it. - * - * @param range The range. - * @param position The position. - */ -export function comparePosition(range: sqliteModels.RangeData, position: lsp.Position): number { - if (position.line < range.startLine) { - return +1 - } - - if (position.line > range.endLine) { - return -1 - } - - if (position.line === range.startLine && position.character < range.startCharacter) { - return +1 - } - - if (position.line === range.endLine && position.character > range.endCharacter) { - return -1 - } - - return 0 -} - -/** - * Construct an LSP range from a flat range. - * - * @param result The start/end line/character of the range. - */ -function createRange(result: { - startLine: number - startCharacter: number - endLine: number - endCharacter: number -}): lsp.Range { - return lsp.Range.create(result.startLine, result.startCharacter, result.endLine, result.endCharacter) -} - -/** - * Convert the given range identifiers into an `InternalLocation` objects. - * - * @param ranges The map of ranges of the document. - * @param uri The location URI. - * @param ids The set of range identifiers for each resulting location. - */ -export function mapRangesToInternalLocations( - ranges: Map, - uri: string, - ids: Set -): InternalLocation[] { - const locations = [] - for (const id of ids) { - locations.push({ - path: uri, - range: createRange(mustGet(ranges, id, 'range')), - }) - } - - return locations -} - -/** - * Format ranges to be serialized in opentracing logs. - * - * @param ranges The ranges to clean. - */ -function cleanRanges( - ranges: sqliteModels.RangeData[] -): (Omit & { monikerIds: sqliteModels.MonikerId[] })[] { - // We need to array-ize sets otherwise we get a "0 key" object - return ranges.map(r => ({ ...r, monikerIds: Array.from(r.monikerIds) })) -} diff --git a/cmd/precise-code-intel/src/bundle-manager/backend/location.ts b/cmd/precise-code-intel/src/bundle-manager/backend/location.ts deleted file mode 100644 index 2ed171f5efa..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/backend/location.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as lsp from 'vscode-languageserver-protocol' -import { OrderedSet } from '../../shared/datastructures/orderedset' - -export interface InternalLocation { - /** The path relative to the dump root. */ - path: string - range: lsp.Range -} - -/** A duplicate-free list of locations ordered by time of insertion. */ -export class OrderedLocationSet extends OrderedSet { - /** - * Create a new ordered locations set. - * - * @param values A set of values used to seed the set. - */ - constructor(values?: InternalLocation[]) { - super( - (value: InternalLocation): string => - [ - value.path, - value.range.start.line, - value.range.start.character, - value.range.end.line, - value.range.end.character, - ].join(':'), - values - ) - } -} diff --git a/cmd/precise-code-intel/src/bundle-manager/manager.ts b/cmd/precise-code-intel/src/bundle-manager/manager.ts deleted file mode 100644 index 508d8910165..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/manager.ts +++ /dev/null @@ -1,58 +0,0 @@ -import * as constants from '../shared/constants' -import * as path from 'path' -import * as settings from './settings' -import * as metrics from './metrics' -import promClient from 'prom-client' -import { createLogger } from '../shared/logging' -import { ensureDirectory } from '../shared/paths' -import { Logger } from 'winston' -import { startExpressApp } from '../shared/api/init' -import { createDatabaseRouter } from './routes/database' -import { createUploadRouter } from './routes/uploads' -import { startTasks } from './tasks' -import { createPostgresConnection } from '../shared/database/postgres' -import { waitForConfiguration } from '../shared/config/config' - -/** - * Runs the HTTP server that stores and queries individual SQLite files. - * - * @param logger The logger instance. - */ -async function main(logger: Logger): Promise { - // Collect process metrics - promClient.collectDefaultMetrics({ prefix: 'lsif_' }) - - // Read configuration from frontend - const fetchConfiguration = await waitForConfiguration(logger) - - // Update cache capacities on startup - metrics.connectionCacheCapacityGauge.set(settings.CONNECTION_CACHE_CAPACITY) - metrics.documentCacheCapacityGauge.set(settings.DOCUMENT_CACHE_CAPACITY) - metrics.resultChunkCacheCapacityGauge.set(settings.RESULT_CHUNK_CACHE_CAPACITY) - - // Ensure storage roots exist - await ensureDirectory(settings.STORAGE_ROOT) - await ensureDirectory(path.join(settings.STORAGE_ROOT, constants.DBS_DIR)) - await ensureDirectory(path.join(settings.STORAGE_ROOT, constants.UPLOADS_DIR)) - - // Create database connection - const connection = await createPostgresConnection(fetchConfiguration(), logger) - - // Start background tasks - startTasks(connection, logger) - - const routers = [createDatabaseRouter(logger), createUploadRouter(logger)] - - // Start server - startExpressApp({ port: settings.HTTP_PORT, routers, logger }) -} - -// Initialize logger -const appLogger = createLogger('precise-code-intel-bundle-manager') - -// Launch! -main(appLogger).catch(error => { - appLogger.error('Failed to start process', { error }) - appLogger.on('finish', () => process.exit(1)) - appLogger.end() -}) diff --git a/cmd/precise-code-intel/src/bundle-manager/metrics.ts b/cmd/precise-code-intel/src/bundle-manager/metrics.ts deleted file mode 100644 index 521058c8757..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/metrics.ts +++ /dev/null @@ -1,83 +0,0 @@ -import promClient from 'prom-client' - -// -// HTTP Metrics - -export const httpUploadDurationHistogram = new promClient.Histogram({ - name: 'lsif_http_upload_request_duration_seconds', - help: 'Total time spent on upload requests.', - labelNames: ['code'], - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -export const httpQueryDurationHistogram = new promClient.Histogram({ - name: 'lsif_http_query_request_duration_seconds', - help: 'Total time spent on query requests.', - labelNames: ['code'], - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -// -// Database Metrics - -export const databaseQueryDurationHistogram = new promClient.Histogram({ - name: 'lsif_database_query_duration_seconds', - help: 'Total time spent on database queries.', - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -export const databaseQueryErrorsCounter = new promClient.Counter({ - name: 'lsif_database_query_errors_total', - help: 'The number of errors that occurred during a database query.', -}) - -// -// Cache Metrics - -export const connectionCacheCapacityGauge = new promClient.Gauge({ - name: 'lsif_connection_cache_capacity', - help: 'The maximum number of open SQLite handles.', -}) - -export const connectionCacheSizeGauge = new promClient.Gauge({ - name: 'lsif_connection_cache_size', - help: 'The current number of open SQLite handles.', -}) - -export const connectionCacheEventsCounter = new promClient.Counter({ - name: 'lsif_connection_cache_events_total', - help: 'The number of connection cache hits, misses, and evictions.', - labelNames: ['type'], -}) - -export const documentCacheCapacityGauge = new promClient.Gauge({ - name: 'lsif_document_cache_capacity', - help: 'The maximum number of documents loaded in memory.', -}) - -export const documentCacheSizeGauge = new promClient.Gauge({ - name: 'lsif_document_cache_size', - help: 'The current number of documents loaded in memory.', -}) - -export const documentCacheEventsCounter = new promClient.Counter({ - name: 'lsif_document_cache_events_total', - help: 'The number of document cache hits, misses, and evictions.', - labelNames: ['type'], -}) - -export const resultChunkCacheCapacityGauge = new promClient.Gauge({ - name: 'lsif_results_chunk_cache_capacity', - help: 'The maximum number of result chunks loaded in memory.', -}) - -export const resultChunkCacheSizeGauge = new promClient.Gauge({ - name: 'lsif_results_chunk_cache_size', - help: 'The current number of result chunks loaded in memory.', -}) - -export const resultChunkCacheEventsCounter = new promClient.Counter({ - name: 'lsif_results_chunk_cache_events_total', - help: 'The number of result chunk cache hits, misses, and evictions.', - labelNames: ['type'], -}) diff --git a/cmd/precise-code-intel/src/bundle-manager/routes/database.ts b/cmd/precise-code-intel/src/bundle-manager/routes/database.ts deleted file mode 100644 index 5e79161bde1..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/routes/database.ts +++ /dev/null @@ -1,227 +0,0 @@ -import * as settings from '../settings' -import express from 'express' -import { addTags, TracingContext } from '../../shared/tracing' -import { Logger } from 'winston' -import { pipeline as _pipeline } from 'stream' -import { Span } from 'opentracing' -import { wrap } from 'async-middleware' -import { Database } from '../backend/database' -import * as sqliteModels from '../../shared/models/sqlite' -import { InternalLocation } from '../backend/location' -import { dbFilename } from '../../shared/paths' -import * as lsp from 'vscode-languageserver-protocol' -import * as validation from '../../shared/api/middleware/validation' - -/** - * Create a router containing the SQLite query endpoints. - * - * For now, each public method of Database (see sif/src/bundle-manager/backend/database.ts) is - * exposed at `//`. This interface is likely to change soon. - * - * @param logger The logger instance. - */ -export function createDatabaseRouter(logger: Logger): express.Router { - const router = express.Router() - - /** - * Create a tracing context from the request logger and tracing span - * tagged with the given values. - * - * @param req The express request. - * @param tags The tags to apply to the logger and span. - */ - const createTracingContext = ( - req: express.Request & { span?: Span }, - tags: { [K: string]: unknown } - ): TracingContext => addTags({ logger, span: req.span }, tags) - - const withDatabase = async ( - req: express.Request, - res: express.Response, - handler: (database: Database, ctx?: TracingContext) => Promise - ): Promise => { - const id = parseInt(req.params.id, 10) - const ctx = createTracingContext(req, { id }) - const database = new Database(id, dbFilename(settings.STORAGE_ROOT, id)) - - const payload = await handler(database, ctx) - res.json(payload) - } - - interface ExistsQueryArgs { - path: string - } - - type ExistsResponse = boolean - - router.get( - '/dbs/:id([0-9]+)/exists', - validation.validationMiddleware([validation.validateNonEmptyString('path')]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { path }: ExistsQueryArgs = req.query - await withDatabase(req, res, (database, ctx) => database.exists(path, ctx)) - } - ) - ) - - interface DefinitionsQueryArgs { - path: string - line: number - character: number - } - - type DefinitionsResponse = InternalLocation[] - - router.get( - '/dbs/:id([0-9]+)/definitions', - validation.validationMiddleware([ - validation.validateNonEmptyString('path'), - validation.validateInt('line'), - validation.validateInt('character'), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { path, line, character }: DefinitionsQueryArgs = req.query - await withDatabase(req, res, (database, ctx) => database.definitions(path, { line, character }, ctx)) - } - ) - ) - - interface ReferencesQueryArgs { - path: string - line: number - character: number - } - - type ReferencesResponse = InternalLocation[] - - router.get( - '/dbs/:id([0-9]+)/references', - validation.validationMiddleware([ - validation.validateNonEmptyString('path'), - validation.validateInt('line'), - validation.validateInt('character'), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { path, line, character }: ReferencesQueryArgs = req.query - await withDatabase( - req, - res, - async (database, ctx) => (await database.references(path, { line, character }, ctx)).values - ) - } - ) - ) - - interface HoverQueryArgs { - path: string - line: number - character: number - } - - type HoverResponse = { text: string; range: lsp.Range } | null - - router.get( - '/dbs/:id([0-9]+)/hover', - validation.validationMiddleware([ - validation.validateNonEmptyString('path'), - validation.validateInt('line'), - validation.validateInt('character'), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { path, line, character }: HoverQueryArgs = req.query - await withDatabase(req, res, (database, ctx) => database.hover(path, { line, character }, ctx)) - } - ) - ) - - interface MonikersByPositionQueryArgs { - path: string - line: number - character: number - } - - type MonikersByPositionResponse = sqliteModels.MonikerData[][] - - router.get( - '/dbs/:id([0-9]+)/monikersByPosition', - validation.validationMiddleware([ - validation.validateNonEmptyString('path'), - validation.validateInt('line'), - validation.validateInt('character'), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { path, line, character }: MonikersByPositionQueryArgs = req.query - await withDatabase(req, res, (database, ctx) => - database.monikersByPosition(path, { line, character }, ctx) - ) - } - ) - ) - - interface MonikerResultsQueryArgs { - modelType: string - scheme: string - identifier: string - skip?: number - take?: number - } - - interface MonikerResultsResponse { - locations: { path: string; range: lsp.Range }[] - count: number - } - - router.get( - '/dbs/:id([0-9]+)/monikerResults', - validation.validationMiddleware([ - validation.validateNonEmptyString('modelType'), - validation.validateNonEmptyString('scheme'), - validation.validateNonEmptyString('identifier'), - validation.validateOptionalInt('skip'), - validation.validateOptionalInt('take'), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { modelType, scheme, identifier, skip, take }: MonikerResultsQueryArgs = req.query - await withDatabase(req, res, (database, ctx) => - database.monikerResults( - modelType === 'definition' ? sqliteModels.DefinitionModel : sqliteModels.ReferenceModel, - { scheme, identifier }, - { skip, take }, - ctx - ) - ) - } - ) - ) - - interface PackageInformationQueryArgs { - path: string - packageInformationId: string - } - - type PackageInformationResponse = sqliteModels.PackageInformationData | undefined - - router.get( - '/dbs/:id([0-9]+)/packageInformation', - validation.validationMiddleware([ - validation.validateNonEmptyString('path'), - validation.validateNonEmptyString('packageInformationId'), - ]), - wrap( - async (req: express.Request, res: express.Response): Promise => { - const { path, packageInformationId }: PackageInformationQueryArgs = req.query - await withDatabase(req, res, (database, ctx) => - database.packageInformation(path, packageInformationId, ctx) - ) - } - ) - ) - - return router -} diff --git a/cmd/precise-code-intel/src/bundle-manager/routes/uploads.ts b/cmd/precise-code-intel/src/bundle-manager/routes/uploads.ts deleted file mode 100644 index 24d4e58be74..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/routes/uploads.ts +++ /dev/null @@ -1,100 +0,0 @@ -import express from 'express' -import { Logger } from 'winston' -import { Span } from 'opentracing' -import { wrap } from 'async-middleware' -import { addTags, TracingContext, logAndTraceCall } from '../../shared/tracing' -import { pipeline as _pipeline } from 'stream' -import { promisify } from 'util' -import * as fs from 'mz/fs' -import * as settings from '../settings' -import { dbFilename, uploadFilename } from '../../shared/paths' -import { ThrottleGroup, Throttle } from 'stream-throttle' - -const pipeline = promisify(_pipeline) - -/** - * Create a router containing the upload endpoints. - * - * @param logger The logger instance. - */ -export function createUploadRouter(logger: Logger): express.Router { - const router = express.Router() - - const makeServeThrottle = makeThrottleFactory( - settings.MAXIMUM_SERVE_BYTES_PER_SECOND, - settings.MAXIMUM_SERVE_CHUNK_BYTES - ) - - const makeUploadThrottle = makeThrottleFactory( - settings.MAXIMUM_UPLOAD_BYTES_PER_SECOND, - settings.MAXIMUM_UPLOAD_CHUNK_BYTES - ) - - /** - * Create a tracing context from the request logger and tracing span - * tagged with the given values. - * - * @param req The express request. - * @param tags The tags to apply to the logger and span. - */ - const createTracingContext = ( - req: express.Request & { span?: Span }, - tags: { [K: string]: unknown } - ): TracingContext => addTags({ logger, span: req.span }, tags) - - router.get( - '/uploads/:id([0-9]+)', - wrap( - async (req: express.Request, res: express.Response): Promise => { - const id = parseInt(req.params.id, 10) - const ctx = createTracingContext(req, { id }) - const filename = uploadFilename(settings.STORAGE_ROOT, id) - const stream = fs.createReadStream(filename) - await logAndTraceCall(ctx, 'Serving payload', () => pipeline(stream, makeServeThrottle(), res)) - } - ) - ) - - router.post( - '/uploads/:id([0-9]+)', - wrap( - async (req: express.Request, res: express.Response): Promise => { - const id = parseInt(req.params.id, 10) - const ctx = createTracingContext(req, { id }) - const filename = uploadFilename(settings.STORAGE_ROOT, id) - const stream = fs.createWriteStream(filename) - await logAndTraceCall(ctx, 'Uploading payload', () => pipeline(req, makeUploadThrottle(), stream)) - res.send() - } - ) - ) - - router.post( - '/dbs/:id([0-9]+)', - wrap( - async (req: express.Request, res: express.Response): Promise => { - const id = parseInt(req.params.id, 10) - const ctx = createTracingContext(req, { id }) - const filename = dbFilename(settings.STORAGE_ROOT, id) - const stream = fs.createWriteStream(filename) - await logAndTraceCall(ctx, 'Uploading payload', () => pipeline(req, makeUploadThrottle(), stream)) - res.send() - } - ) - ) - - return router -} - -/** - * Create a function that will create a throttle that can be used as a stream - * transformer. This transformer can limit both readable and writable streams. - * - * @param rate The maximum bit second of the stream. - * @param chunksize The size of chunks used to break down larger slices of data. - */ -function makeThrottleFactory(rate: number, chunksize: number): () => Throttle { - const opts = { rate, chunksize } - const throttleGroup = new ThrottleGroup(opts) - return () => throttleGroup.throttle(opts) -} diff --git a/cmd/precise-code-intel/src/bundle-manager/settings.ts b/cmd/precise-code-intel/src/bundle-manager/settings.ts deleted file mode 100644 index e1bbd666786..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/settings.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { readEnvInt } from '../shared/settings' - -/** Which port to run the bundle manager API on. Defaults to 3187. */ -export const HTTP_PORT = readEnvInt('HTTP_PORT', 3187) - -/** HTTP address for internal precise code intel API. */ -export const PRECISE_CODE_INTEL_API_SERVER_URL = - process.env.PRECISE_CODE_INTEL_API_SERVER_URL || 'http://localhost:3186' - -/** Where on the file system to store LSIF files. This should be a persistent volume. */ -export const STORAGE_ROOT = process.env.LSIF_STORAGE_ROOT || 'lsif-storage' - -/** - * The number of SQLite connections that can be opened at once. This - * value may be exceeded for a short period if many handles are held - * at once. - */ -export const CONNECTION_CACHE_CAPACITY = readEnvInt('CONNECTION_CACHE_CAPACITY', 100) - -/** The maximum number of documents that can be held in memory at once. */ -export const DOCUMENT_CACHE_CAPACITY = readEnvInt('DOCUMENT_CACHE_CAPACITY', 1024 * 1024 * 1024) - -/** The maximum number of result chunks that can be held in memory at once. */ -export const RESULT_CHUNK_CACHE_CAPACITY = readEnvInt('RESULT_CHUNK_CACHE_CAPACITY', 1024 * 1024 * 1024) - -/** The interval (in seconds) to clean the dbs directory. */ -export const PURGE_OLD_DUMPS_INTERVAL = readEnvInt('PURGE_OLD_DUMPS_INTERVAL', 60 * 30) - -/** How many uploads to query at once when determining if a db file is unreferenced. */ -export const DEAD_DUMP_BATCH_SIZE = readEnvInt('DEAD_DUMP_BATCH_SIZE', 100) - -/** The maximum space (in bytes) that the dbs directory can use. */ -export const DBS_DIR_MAXIMUM_SIZE_BYTES = readEnvInt('DBS_DIR_MAXIMUM_SIZE_BYTES', 1024 * 1024 * 1024 * 10) - -/** The interval (in seconds) to invoke the cleanFailedUploads task. */ -export const CLEAN_FAILED_UPLOADS_INTERVAL = readEnvInt('CLEAN_FAILED_UPLOADS_INTERVAL', 60 * 60 * 8) - -/** The maximum age (in seconds) that the files for an unprocessed upload can remain on disk. */ -export const FAILED_UPLOAD_MAX_AGE = readEnvInt('FAILED_UPLOAD_MAX_AGE', 24 * 60 * 60) - -/** How many times to retry requests to precise-code-intel-api-server in the background. */ -export const MAX_REQUEST_RETRIES = readEnvInt('MAX_REQUEST_RETRIES', 60) - -/** How long to wait (minimum, in seconds) between precise-code-intel-api-server request attempts. */ -export const MIN_REQUEST_RETRY_TIMEOUT = readEnvInt('MIN_REQUEST_RETRY_TIMEOUT', 1) - -/** How long to wait (maximum, in seconds) between precise-code-intel-api-server request attempts. */ -export const MAX_REQUEST_RETRY_TIMEOUT = readEnvInt('MAX_REQUEST_RETRY_TIMEOUT', 30) - -/** The maximum rate that the server will send upload payloads. */ -export const MAXIMUM_SERVE_BYTES_PER_SECOND = readEnvInt('MAXIMUM_SERVE_BYTES_PER_SECOND', 1024 * 1024 * 1024 * 10) // 10 GiB/sec - -/** The maximum chunk size the server will use to send upload payloads. */ -export const MAXIMUM_SERVE_CHUNK_BYTES = readEnvInt('MAXIMUM_SERVE_CHUNK_BYTES', 1024 * 1024 * 10) // 10 MiB - -/** The maximum rate that the server will receive upload payloads. */ -export const MAXIMUM_UPLOAD_BYTES_PER_SECOND = readEnvInt('MAXIMUM_UPLOAD_BYTES_PER_SECOND', 1024 * 1024 * 1024 * 10) // 10 GiB/sec - -/** The maximum chunk size the server will use to receive upload payloads. */ -export const MAXIMUM_UPLOAD_CHUNK_BYTES = readEnvInt('MAXIMUM_UPLOAD_CHUNK_BYTES', 1024 * 1024 * 10) // 10 MiB diff --git a/cmd/precise-code-intel/src/bundle-manager/tasks.ts b/cmd/precise-code-intel/src/bundle-manager/tasks.ts deleted file mode 100644 index f50b52e7079..00000000000 --- a/cmd/precise-code-intel/src/bundle-manager/tasks.ts +++ /dev/null @@ -1,206 +0,0 @@ -import * as settings from './settings' -import { Connection } from 'typeorm' -import { Logger } from 'winston' -import { ExclusivePeriodicTaskRunner } from '../shared/tasks' -import * as constants from '../shared/constants' -import * as fs from 'mz/fs' -import * as path from 'path' -import { chunk } from 'lodash' -import { createSilentLogger } from '../shared/logging' -import { TracingContext } from '../shared/tracing' -import { dbFilename, idFromFilename } from '../shared/paths' -import got from 'got' -import pRetry from 'p-retry' -import { parseJSON } from '../shared/encoding/json' - -/** - * Begin running cleanup tasks on a schedule in the background. - * - * @param connection The Postgres connection. - * @param logger The logger instance. - */ -export function startTasks(connection: Connection, logger: Logger): void { - const runner = new ExclusivePeriodicTaskRunner(connection, logger) - - runner.register({ - name: 'Purging old dumps', - intervalMs: settings.PURGE_OLD_DUMPS_INTERVAL, - task: ({ ctx }) => purgeOldDumps(settings.STORAGE_ROOT, settings.DBS_DIR_MAXIMUM_SIZE_BYTES, ctx), - }) - - runner.register({ - name: 'Cleaning failed uploads', - intervalMs: settings.CLEAN_FAILED_UPLOADS_INTERVAL, - task: ({ ctx }) => cleanFailedUploads(ctx), - }) - - runner.run() -} - -/** - * Remove dumps until the space occupied by the dbs directory is below - * the given limit. - * - * @param storageRoot The path where SQLite databases are stored. - * @param maximumSizeBytes The maximum number of bytes (< 0 means no limit). - * @param ctx The tracing context. - */ -async function purgeOldDumps( - storageRoot: string, - maximumSizeBytes: number, - { logger = createSilentLogger() }: TracingContext = {} -): Promise { - // First, remove all the files in the DB dir that don't have a corresponding - // lsif_upload record in the database. This will happen in the cases where an - // upload overlaps existing uploads which are deleted in batch from the db, - // but not from disk. This can also happen if the db file is written during - // processing but fails later while updating commits for that repo. - await removeDeadDumps(storageRoot, { logger }) - - if (maximumSizeBytes < 0) { - return Promise.resolve() - } - - let currentSizeBytes = await dirsize(path.join(storageRoot, constants.DBS_DIR)) - - while (currentSizeBytes > maximumSizeBytes) { - // While our current data usage is too big, find candidate dumps to delete - const payload: { id: number } = await makeServerRequest('/prune') - if (!payload) { - logger.warn( - 'Unable to reduce disk usage of the DB directory because deleting any single dump would drop in-use code intel for a repository.', - { currentSizeBytes, softMaximumSizeBytes: maximumSizeBytes } - ) - - break - } - - // Delete this dump and subtract its size from the current dir size - const filename = dbFilename(storageRoot, payload.id) - currentSizeBytes -= await filesize(filename) - await fs.unlink(filename) - } -} - -/** - * Remove db files that are not reachable from a pending or completed upload record. - * - * @param storageRoot The path where SQLite databases are stored. - * @param ctx The tracing context. - */ -async function removeDeadDumps( - storageRoot: string, - { logger = createSilentLogger() }: TracingContext = {} -): Promise { - let count = 0 - for (const basenames of chunk( - await fs.readdir(path.join(storageRoot, constants.DBS_DIR)), - settings.DEAD_DUMP_BATCH_SIZE - )) { - const pathsById = new Map() - for (const basename of basenames) { - const id = idFromFilename(basename) - if (!id) { - continue - } - - pathsById.set(id, path.join(storageRoot, constants.DBS_DIR, basename)) - } - - const states: Map = await makeServerRequest('/uploads', { ids: Array.from(pathsById.keys()) }) - for (const [id, dbPath] of pathsById.entries()) { - if (!states.has(id) || states.get(id) === 'errored') { - count++ - await fs.unlink(dbPath) - } - } - } - - if (count > 0) { - logger.debug('Removed dead dumps', { count }) - } -} - -/** - * Remove upload and temp files that are older than `FAILED_UPLOAD_MAX_AGE`. This assumes - * that an upload conversion's total duration (from enqueue to completion) is less than this - * interval during healthy operation. - * - * @param ctx The tracing context. - */ -async function cleanFailedUploads({ logger = createSilentLogger() }: TracingContext): Promise { - let count = 0 - for (const basename of await fs.readdir(path.join(settings.STORAGE_ROOT, constants.UPLOADS_DIR))) { - if (await purgeFile(path.join(settings.STORAGE_ROOT, constants.UPLOADS_DIR, basename))) { - count++ - } - } - - if (count > 0) { - logger.debug('Removed old files', { count }) - } -} - -/** - * Remove the given file if it was last modified longer than `FAILED_UPLOAD_MAX_AGE` seconds - * ago. Returns true if the file was removed and false otherwise. - * - * @param filename The file to remove. - */ -async function purgeFile(filename: string): Promise { - if (Date.now() - (await fs.stat(filename)).mtimeMs < settings.FAILED_UPLOAD_MAX_AGE * 1000) { - return false - } - - await fs.unlink(filename) - return true -} - -/** - * Calculate the cumulative size of all plain files in a directory, non-recursively. - * - * @param directory The directory path. - */ -async function dirsize(directory: string): Promise { - return ( - await Promise.all((await fs.readdir(directory)).map(filename => filesize(path.join(directory, filename)))) - ).reduce((a, b) => a + b, 0) -} - -/** - * Get the file size or zero if it doesn't exist. - * - * @param filename The filename. - */ -async function filesize(filename: string): Promise { - try { - return (await fs.stat(filename)).size - } catch (error) { - if (!(error && error.code === 'ENOENT')) { - throw error - } - - return 0 - } -} - -async function makeServerRequest(route: string, payload?: T): Promise { - return pRetry( - async (): Promise => - parseJSON( - ( - await got.post(new URL(route, settings.PRECISE_CODE_INTEL_API_SERVER_URL).href, { - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(payload), - }) - ).body - ), - { - factor: 1.5, - randomize: true, - retries: settings.MAX_REQUEST_RETRIES, - minTimeout: settings.MIN_REQUEST_RETRY_TIMEOUT * 1000, - maxTimeout: settings.MAX_REQUEST_RETRY_TIMEOUT * 1000, - } - ) -} diff --git a/cmd/precise-code-intel/src/shared/api/init.ts b/cmd/precise-code-intel/src/shared/api/init.ts deleted file mode 100644 index 79a25d6c2ed..00000000000 --- a/cmd/precise-code-intel/src/shared/api/init.ts +++ /dev/null @@ -1,62 +0,0 @@ -import express from 'express' -import promClient from 'prom-client' -import { default as tracingMiddleware } from 'express-opentracing' -import { errorHandler } from './middleware/errors' -import { logger as loggingMiddleware } from 'express-winston' -import { makeMetricsMiddleware } from './middleware/metrics' -import { Tracer } from 'opentracing' -import { Logger } from 'winston' -import { jsonReplacer } from '../encoding/json' - -export function startExpressApp({ - port, - routers = [], - logger, - tracer, - selectHistogram = () => undefined, -}: { - port: number - routers?: express.Router[] - logger: Logger - tracer?: Tracer - selectHistogram?: (route: string) => promClient.Histogram | undefined -}): void { - const loggingOptions = { - winstonInstance: logger, - level: 'debug', - ignoredRoutes: ['/ping', '/healthz', '/metrics'], - requestWhitelist: ['method', 'url'], - msg: 'Handled request', - } - - const app = express() - app.use(tracingMiddleware({ tracer })) - app.use(loggingMiddleware(loggingOptions)) - app.use(makeMetricsMiddleware(selectHistogram)) - app.use(createMetaRouter()) - - for (const route of routers) { - app.use(route) - } - - // Error handler must be registered last so its exception handlers - // will apply to all routes and other middleware. - app.use(errorHandler(logger)) - - app.set('json replacer', jsonReplacer) - - app.listen(port, () => logger.debug('API server listening', { port })) -} - -/** Create a router containing health and metrics endpoint. */ -function createMetaRouter(): express.Router { - const router = express.Router() - router.get('/ping', (_, res) => res.send('ok')) - router.get('/healthz', (_, res) => res.send('ok')) - router.get('/metrics', (_, res) => { - res.writeHead(200, { 'Content-Type': 'text/plain' }) - res.end(promClient.register.metrics()) - }) - - return router -} diff --git a/cmd/precise-code-intel/src/shared/api/middleware/errors.ts b/cmd/precise-code-intel/src/shared/api/middleware/errors.ts deleted file mode 100644 index 6b3583077a9..00000000000 --- a/cmd/precise-code-intel/src/shared/api/middleware/errors.ts +++ /dev/null @@ -1,45 +0,0 @@ -import express from 'express' -import { Logger } from 'winston' - -interface ErrorResponse { - message: string -} - -export interface ApiError { - message: string - status?: number -} - -export const isApiError = (val: unknown): val is ApiError => typeof val === 'object' && !!val && 'message' in val - -/** - * Middleware function used to convert uncaught exceptions into 500 responses. - * - * @param logger The logger instance. - */ -export const errorHandler = ( - logger: Logger -): (( - error: unknown, - req: express.Request, - res: express.Response, - next: express.NextFunction -) => void) => ( - error: unknown, - req: express.Request, - res: express.Response, - // Express uses argument length to distinguish middleware and error handlers - // eslint-disable-next-line @typescript-eslint/no-unused-vars - next: express.NextFunction -): void => { - const status = (isApiError(error) && error.status) || 500 - const message = (isApiError(error) && error.message) || 'Unknown error' - - if (status === 500) { - logger.error('uncaught exception', { error }) - } - - if (!res.headersSent) { - res.status(status).send({ message }) - } -} diff --git a/cmd/precise-code-intel/src/shared/api/middleware/metrics.ts b/cmd/precise-code-intel/src/shared/api/middleware/metrics.ts deleted file mode 100644 index 541cad8e5da..00000000000 --- a/cmd/precise-code-intel/src/shared/api/middleware/metrics.ts +++ /dev/null @@ -1,27 +0,0 @@ -import express from 'express' -import onFinished from 'on-finished' -import promClient from 'prom-client' - -/** - * Creates a middleware function used to emit HTTP durations for LSIF functions. - * Originally we used an express bundle, but that did not allow us to have different - * histogram bucket for different endpoints, which makes half of the metrics useless - * in the presence of large uploads. - */ -export const makeMetricsMiddleware = ( - selectHistogram: (route: string) => promClient.Histogram | undefined -) => (req: express.Request, res: express.Response, next: express.NextFunction): void => { - const histogram = selectHistogram(req.path) - - if (histogram !== undefined) { - const labels = { code: 0 } - const end = histogram.startTimer(labels) - - onFinished(res, () => { - labels.code = res.statusCode - end() - }) - } - - next() -} diff --git a/cmd/precise-code-intel/src/shared/api/middleware/validation.ts b/cmd/precise-code-intel/src/shared/api/middleware/validation.ts deleted file mode 100644 index 29852c49c4e..00000000000 --- a/cmd/precise-code-intel/src/shared/api/middleware/validation.ts +++ /dev/null @@ -1,85 +0,0 @@ -import express from 'express' -import { query, ValidationChain, validationResult, ValidationError } from 'express-validator' -import { parseCursor } from '../pagination/cursor' - -/** - * Create a query string validator for a required non-empty string value. - * - * @param key The query string key. - */ -export const validateNonEmptyString = (key: string): ValidationChain => query(key).isString().not().isEmpty() - -/** - * Create a query string validator for a possibly empty string value. - * - * @param key The query string key. - */ -export const validateOptionalString = (key: string): ValidationChain => - query(key) - .optional() - .customSanitizer(value => value || '') - -/** - * Create a query string validator for a possibly empty boolean value. - * - * @param key The query string key. - */ -export const validateOptionalBoolean = (key: string): ValidationChain => query(key).optional().isBoolean().toBoolean() - -/** - * Create a query string validator for an integer value. - * - * @param key The query string key. - */ -export const validateInt = (key: string): ValidationChain => query(key).isInt().toInt() - -/** - * Create a query string validator for a possibly empty integer value. - * - * @param key The query string key. - */ -export const validateOptionalInt = (key: string): ValidationChain => query(key).optional().isInt().toInt() - -/** A validator used for a string query field. */ -export const validateQuery = validateOptionalString('query') - -/** - * Create a query string validator for an LSIF upload state. - * - * @param key The query string key. - */ -export const validateLsifUploadState = query('state').optional().isIn(['queued', 'completed', 'errored', 'processing']) - -/** Create a validator for an integer limit field. */ -export const validateLimit = validateOptionalInt('limit') - -/** A validator used for an integer offset field. */ -export const validateOffset = validateOptionalInt('offset') - -/** Create a validator for a cursor that is serialized as the supplied generic type. */ -export const validateCursor = (): ValidationChain => - validateOptionalString('cursor').customSanitizer(value => parseCursor(value)) - -interface ValidationErrorResponse { - errors: Record -} - -/** - * Middleware function used to apply a sequence of validators and then return - * an unprocessable entity response with an error message if validation fails. - */ -export const validationMiddleware = (chains: ValidationChain[]) => async ( - req: express.Request, - res: express.Response, - next: express.NextFunction -): Promise => { - await Promise.all(chains.map(chain => chain.run(req))) - - const errors = validationResult(req) - if (!errors.isEmpty()) { - res.status(422).send({ errors: errors.mapped() }) - return - } - - next() -} diff --git a/cmd/precise-code-intel/src/shared/api/pagination/cursor.ts b/cmd/precise-code-intel/src/shared/api/pagination/cursor.ts deleted file mode 100644 index d6f6d96966c..00000000000 --- a/cmd/precise-code-intel/src/shared/api/pagination/cursor.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Parse a base64-encoded JSON payload into the expected type. - * - * @param cursorRaw The raw cursor. - */ -export function parseCursor(cursorRaw: string | undefined): T | undefined { - if (cursorRaw === undefined) { - return undefined - } - - try { - return JSON.parse(Buffer.from(cursorRaw, 'base64').toString()) - } catch { - throw Object.assign(new Error(`Malformed cursor supplied ${cursorRaw}`), { status: 400 }) - } -} - -/** - * Encode an arbitrary pagination cursor value into a string. - * - * @param cursor The cursor value. - */ -export function encodeCursor(cursor: T | undefined): string | undefined { - if (!cursor) { - return undefined - } - - return Buffer.from(JSON.stringify(cursor)).toString('base64') -} diff --git a/cmd/precise-code-intel/src/shared/api/pagination/limit-offset.ts b/cmd/precise-code-intel/src/shared/api/pagination/limit-offset.ts deleted file mode 100644 index c52e9bc2fe4..00000000000 --- a/cmd/precise-code-intel/src/shared/api/pagination/limit-offset.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Normalize limit and offset values extracted from the query string. - * - * @param query Parameter bag. - * @param defaultLimit The limit to use if one is not supplied. - */ -export const extractLimitOffset = ( - { - limit, - offset, - }: { - /** The limit value extracted from the query string. */ - limit?: number - /** The offset value extracted from the query string. */ - offset?: number - }, - defaultLimit: number -): { limit: number; offset: number } => ({ limit: limit || defaultLimit, offset: offset || 0 }) diff --git a/cmd/precise-code-intel/src/shared/api/pagination/link.ts b/cmd/precise-code-intel/src/shared/api/pagination/link.ts deleted file mode 100644 index ba7dab45453..00000000000 --- a/cmd/precise-code-intel/src/shared/api/pagination/link.ts +++ /dev/null @@ -1,23 +0,0 @@ -import express from 'express' - -/** - * Create a link header payload with a next link based on the previous endpoint. - * - * @param req The HTTP request. - * @param params The query params to overwrite. - */ -export function nextLink( - req: express.Request, - params: { [name: string]: string | number | boolean | undefined } -): string { - // Requests always have a host header - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const url = new URL(`${req.protocol}://${req.get('host')!}${req.originalUrl}`) - for (const [key, value] of Object.entries(params)) { - if (value !== undefined) { - url.searchParams.set(key, String(value)) - } - } - - return `<${url.href}>; rel="next"` -} diff --git a/cmd/precise-code-intel/src/shared/config/config.ts b/cmd/precise-code-intel/src/shared/config/config.ts deleted file mode 100644 index bfd84c3bfd0..00000000000 --- a/cmd/precise-code-intel/src/shared/config/config.ts +++ /dev/null @@ -1,116 +0,0 @@ -import * as json5 from 'json5' -import * as settings from './settings' -import got from 'got' -import { isEqual, pick } from 'lodash' -import { Logger } from 'winston' -import delay from 'delay' - -/** Service configuration data pulled from the frontend. */ -export interface Configuration { - /** The connection string for the Postgres database. */ - postgresDSN: string - - /** Whether or not to enable Jaeger. */ - useJaeger: boolean -} - -/** - * Create a configuration fetcher function and block until the first payload - * can be read from the frontend. Continue reading the configuration from the - * frontend in the background. If one of the fields that cannot be updated while - * the process remains up changes, it will forcibly exit the process to allow - * whatever orchestrator is running this process restart it. - * - * @param logger The logger instance. - */ -export async function waitForConfiguration(logger: Logger): Promise<() => Configuration> { - let oldConfiguration: Configuration | undefined - - await new Promise(resolve => { - updateConfiguration(logger, configuration => { - if (oldConfiguration !== undefined && requireRestart(oldConfiguration, configuration)) { - logger.error('Detected configuration change, restarting to take effect') - process.exit(1) - } - - oldConfiguration = configuration - resolve() - }).catch(() => { - /* noop */ - }) - }) - - // This value is guaranteed to be set by the resolution of the promise above - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return () => oldConfiguration! -} - -/** - * Determine if the two configurations differ by a field that cannot be changed - * while the process remains up and a restart would be required for the change to - * take effect. - * - * @param oldConfiguration The old configuration instance. - * @param newConfiguration The new configuration instance. - */ -function requireRestart(oldConfiguration: Configuration, newConfiguration: Configuration): boolean { - const fields = ['postgresDSN', 'useJaeger'] - return !isEqual(pick(oldConfiguration, fields), pick(newConfiguration, fields)) -} - -/** - * Read the configuration from the frontend on a loop. This function is async but does not - * return any meaningful value (the returned promise neither resolves nor rejects). - * - * @param logger The logger instance. - * @param onChange The callback to invoke each time the configuration is read. - */ -async function updateConfiguration(logger: Logger, onChange: (configuration: Configuration) => void): Promise { - const start = Date.now() - let previousError: Error | undefined - while (true) { - try { - onChange(await loadConfiguration()) - - // Clear old error run - previousError = undefined - } catch (error) { - const elapsed = Date.now() - start - - if ( - // Do not keep reporting the same error - error.message !== previousError?.message && - // Ignore all connection errors during startup period to allow the frontend to become reachable - (error.code !== 'ECONNREFUSED' || elapsed >= settings.DELAY_BEFORE_UNREACHABLE_LOG * 1000) - ) { - logger.error( - 'Failed to retrieve configuration from frontend', - error.code !== 'ECONNREFUSED' ? error : error.message - ) - - previousError = error - } - } - - // Do a jittery sleep _up to_ the config poll interval. - const durationMs = Math.random() * settings.CONFIG_POLL_INTERVAL * 1000 - await delay(durationMs) - } -} - -/** Read configuration from the frontend. */ -async function loadConfiguration(): Promise { - const url = new URL(`http://${settings.SRC_FRONTEND_INTERNAL}/.internal/configuration`).href - const resp = await got.post(url, { followRedirect: true }) - const payload = JSON.parse(resp.body) - - // Already parsed - const serviceConnections = payload.ServiceConnections - // Need to parse but must support comments + trailing commas - const site = json5.parse(payload.Site) - - return { - postgresDSN: serviceConnections.postgresDSN, - useJaeger: site.useJaeger || false, - } -} diff --git a/cmd/precise-code-intel/src/shared/config/settings.ts b/cmd/precise-code-intel/src/shared/config/settings.ts deleted file mode 100644 index 33825c89f2a..00000000000 --- a/cmd/precise-code-intel/src/shared/config/settings.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { readEnvInt } from '../settings' - -/** HTTP address for internal frontend HTTP API. */ -export const SRC_FRONTEND_INTERNAL = process.env.SRC_FRONTEND_INTERNAL || 'sourcegraph-frontend-internal' - -/** - * How long to wait before emitting error logs when polling config (in seconds). - * This needs to be long enough to allow the frontend to fully migrate the PostgreSQL - * database in most cases, to avoid log spam when running sourcegraph/server for the - * first time. - */ -export const DELAY_BEFORE_UNREACHABLE_LOG = readEnvInt('DELAY_BEFORE_UNREACHABLE_LOG', 15) - -/** How long to wait between polling config. */ -export const CONFIG_POLL_INTERVAL = 5 diff --git a/cmd/precise-code-intel/src/shared/constants.ts b/cmd/precise-code-intel/src/shared/constants.ts deleted file mode 100644 index 5172d29ee31..00000000000 --- a/cmd/precise-code-intel/src/shared/constants.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** The directory relative to the storage where SQLite databases are located. */ -export const DBS_DIR = 'dbs' - -/** The directory relative to the storage where raw dumps are uploaded. */ -export const UPLOADS_DIR = 'uploads' - -/** The maximum number of rows to bulk insert in Postgres. */ -export const MAX_POSTGRES_BATCH_SIZE = 5000 - -/** - * The maximum number of commits to visit breadth-first style when when finding - * the closest commit. - */ -export const MAX_TRAVERSAL_LIMIT = 100 - -/** A random integer specific to the Postgres database used to generate advisory lock ids. */ -export const ADVISORY_LOCK_ID_SALT = 1688730858 - -/** - * The number of commits to ask gitserver for when updating commit data for - * a particular repository. This should be just slightly above the max traversal - * limit. - */ -export const MAX_COMMITS_PER_UPDATE = MAX_TRAVERSAL_LIMIT * 1.5 - -/** The number of remote dumps we will query per page of reference results. */ -export const DEFAULT_REFERENCES_REMOTE_DUMP_LIMIT = 20 diff --git a/cmd/precise-code-intel/src/shared/database/inserter.ts b/cmd/precise-code-intel/src/shared/database/inserter.ts deleted file mode 100644 index 5b7e080afec..00000000000 --- a/cmd/precise-code-intel/src/shared/database/inserter.ts +++ /dev/null @@ -1,90 +0,0 @@ -import promClient from 'prom-client' -import { EntityManager } from 'typeorm' -import { instrument } from '../metrics' -import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity' - -/** - * A bag of prometheus metric objects that apply to a particular instance of - * `TableInserter`. - */ -interface TableInserterMetrics { - /** A histogram that is observed on each round-trip to the database. */ - durationHistogram: promClient.Histogram - - /** A counter that increments on each error that occurs during an insertion. */ - errorsCounter: promClient.Counter -} - -/** - * A batch inserter for a SQLite table. Inserting hundreds or thousands of rows in - * a loop is too inefficient, but due to the limit of SQLITE_MAX_VARIABLE_NUMBER, - * the entire set of values cannot be inserted in one bulk operation either. - * - * One inserter instance is created for each table that will receive a bulk - * payload. The inserter will periodically perform the insert operation - * when the number of values is at this maximum. - * - * See https://www.sqlite.org/limits.html#max_variable_number. - */ -export class TableInserter T> { - /** The set of entity values that will be inserted in the next invocation of `executeBatch`. */ - private batch: QueryDeepPartialEntity[] = [] - - /** - * Creates a new `TableInserter` with the given entity manager, the constructor - * of the model object for the table, and the maximum batch size. This number - * should be calculated by floor(MAX_VAR_NUMBER / fields_in_record). - * - * @param entityManager A transactional SQLite entity manager. - * @param model The model object constructor. - * @param maxBatchSize The maximum number of records that can be inserted at once. - * @param metrics The bag of metrics to use for this instance of the inserter. - * @param ignoreConflicts Whether or not to ignore conflicting data on unique constraint violations. - */ - constructor( - private entityManager: EntityManager, - private model: M, - private maxBatchSize: number, - private metrics: TableInserterMetrics, - private ignoreConflicts: boolean = false - ) {} - - /** - * Submit a model for insertion. This may happen immediately, on a - * subsequent call to insert, or when the `flush` method is called. - * - * @param model The instance to save. - */ - public async insert(model: QueryDeepPartialEntity): Promise { - this.batch.push(model) - - if (this.batch.length >= this.maxBatchSize) { - await this.executeBatch() - } - } - - /** Ensure any outstanding records are inserted into the database. */ - public flush(): Promise { - return this.executeBatch() - } - - /** - * If the current batch is non-empty, then perform an insert operation - * and reset the batch array. - */ - private async executeBatch(): Promise { - if (this.batch.length === 0) { - return - } - - let query = this.entityManager.createQueryBuilder().insert().into(this.model).values(this.batch) - - if (this.ignoreConflicts) { - query = query.onConflict('do nothing') - } - - await instrument(this.metrics.durationHistogram, this.metrics.errorsCounter, () => query.execute()) - - this.batch = [] - } -} diff --git a/cmd/precise-code-intel/src/shared/database/logger.ts b/cmd/precise-code-intel/src/shared/database/logger.ts deleted file mode 100644 index b1fe0ede1cd..00000000000 --- a/cmd/precise-code-intel/src/shared/database/logger.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Logger as TypeORMLogger } from 'typeorm' -import { PlatformTools } from 'typeorm/platform/PlatformTools' -import { Logger as WinstonLogger } from 'winston' - -/** - * A custom TypeORM logger that only logs slow database queries. - * - * We had previously set the TypeORM logging config to `['warn', 'error']`. - * This caused some issues as it would print the entire parameter set for large - * batch inserts to stdout. These parameters often included gzipped json payloads, - * which would lock up the terminal where the server was running. - * - * This logger will only print slow database queries. Any other error condition - * will be printed with the query, parameters, and underlying constraint violation - * as part of a thrown error, making it unnecessary to log here. - */ -export class DatabaseLogger implements TypeORMLogger { - constructor(private logger: WinstonLogger) {} - - public logQuerySlow(time: number, query: string, parameters?: unknown[]): void { - this.logger.warn('Slow database query', { - query: PlatformTools.highlightSql(query), - parameters, - executionTime: time, - }) - } - - public logQuery(): void { - /* no-op */ - } - - public logSchemaBuild(): void { - /* no-op */ - } - - public logMigration(): void { - /* no-op */ - } - - public logQueryError(): void { - /* no-op */ - } - - public log(): void { - /* no-op */ - } -} diff --git a/cmd/precise-code-intel/src/shared/database/metrics.ts b/cmd/precise-code-intel/src/shared/database/metrics.ts deleted file mode 100644 index fcc7ad12ad1..00000000000 --- a/cmd/precise-code-intel/src/shared/database/metrics.ts +++ /dev/null @@ -1,29 +0,0 @@ -import promClient from 'prom-client' - -// -// Query Metrics - -export const postgresQueryDurationHistogram = new promClient.Histogram({ - name: 'lsif_xrepo_query_duration_seconds', - help: 'Total time spent on Postgres database queries.', - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -export const postgresQueryErrorsCounter = new promClient.Counter({ - name: 'lsif_xrepo_query_errors_total', - help: 'The number of errors that occurred during a Postgres database query.', -}) - -// -// Insertion Metrics - -export const postgresInsertionDurationHistogram = new promClient.Histogram({ - name: 'lsif_xrepo_insertion_duration_seconds', - help: 'Total time spent on Postgres database insertions.', - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -export const postgresInsertionErrorsCounter = new promClient.Counter({ - name: 'lsif_xrepo_insertion_errors_total', - help: 'The number of errors that occurred during a Postgres database insertion.', -}) diff --git a/cmd/precise-code-intel/src/shared/database/postgres.ts b/cmd/precise-code-intel/src/shared/database/postgres.ts deleted file mode 100644 index 4c74261231f..00000000000 --- a/cmd/precise-code-intel/src/shared/database/postgres.ts +++ /dev/null @@ -1,191 +0,0 @@ -import * as metrics from './metrics' -import * as pgModels from '../models/pg' -import pRetry from 'p-retry' -import { Configuration } from '../config/config' -import { Connection, createConnection as _createConnection, EntityManager } from 'typeorm' -import { instrument } from '../metrics' -import { Logger } from 'winston' -import { PostgresConnectionCredentialsOptions } from 'typeorm/driver/postgres/PostgresConnectionCredentialsOptions' -import { TlsOptions } from 'tls' -import { DatabaseLogger } from './logger' -import * as settings from './settings' - -/** - * The minimum migration version required by this instance of the LSIF process. - * This should be updated any time a new LSIF migration is added to the migrations/ - * directory, as we watch the DB to ensure we're on at least this version prior to - * making use of the DB (which the frontend may still be migrating). - */ -const MINIMUM_MIGRATION_VERSION = 1528395671 - -/** - * Create a Postgres connection. This creates a typorm connection pool with - * the name `lsif`. The connection configuration is constructed by the method - * `createPostgresConnectionOptions`. This method blocks until the connection - * is established, then blocks indefinitely while the database migration state - * is behind the expected minimum, or dirty. If a connection is not made within - * a configurable timeout, an exception is thrown. - * - * @param configuration The current configuration. - * @param logger The logger instance. - */ -export async function createPostgresConnection(configuration: Configuration, logger: Logger): Promise { - // Parse current PostgresDSN into connection options usable by - // the typeorm postgres adapter. - const url = new URL(configuration.postgresDSN) - - // TODO(efritz) - handle allow, prefer, require, 'verify-ca', and 'verify-full' - const sslModes: { [name: string]: boolean | TlsOptions } = { - disable: false, - } - - const host = url.hostname - const port = parseInt(url.port, 10) || 5432 - const username = decodeURIComponent(url.username) - const password = decodeURIComponent(url.password) - const database = decodeURIComponent(url.pathname).substring(1) || username - const sslMode = url.searchParams.get('sslmode') - const ssl = sslMode ? sslModes[sslMode] : undefined - - // Get a working connection - const connection = await connect({ host, port, username, password, database, ssl }, logger) - - // Poll the schema migrations table until we are up to date - await waitForMigrations(connection, logger) - - return connection -} - -/** - * Create a connection to Postgres. This will re-attempt to access the database while - * the database does not exist. This is to give some time to the frontend to run the - * migrations that create the LSIF tables. The retry interval and attempt count can - * be tuned via `MAX_CONNECTION_RETRIES` and `CONNECTION_RETRY_INTERVAL` environment - * variables. - * - * @param connectionOptions The connection options. - * @param logger The logger instance. - */ -function connect(connectionOptions: PostgresConnectionCredentialsOptions, logger: Logger): Promise { - return pRetry( - () => { - logger.debug('Connecting to Postgres') - return connectPostgres(connectionOptions, '', logger) - }, - { - factor: 1.5, - randomize: true, - retries: settings.MAX_CONNECTION_RETRIES, - minTimeout: settings.MIN_CONNECTION_RETRY_TIMEOUT * 1000, - maxTimeout: settings.MAX_CONNECTION_RETRY_TIMEOUT * 1000, - } - ) -} - -/** - * Create a connection to Postgres. - * - * @param connectionOptions The connection options. - * @param suffix The database suffix (used for testing). - * @param logger The logger instance - */ -export function connectPostgres( - connectionOptions: PostgresConnectionCredentialsOptions, - suffix: string, - logger: Logger -): Promise { - return _createConnection({ - type: 'postgres', - name: `lsif${suffix}`, - entities: pgModels.entities, - logger: new DatabaseLogger(logger), - maxQueryExecutionTime: 1000, - ...connectionOptions, - }) -} - -/** - * Block until we can select a migration version from the database that is at - * least as large as our minimum migration version. - * - * @param connection The connection to use. - * @param logger The logger instance. - */ -function waitForMigrations(connection: Connection, logger: Logger): Promise { - const check = async (): Promise => { - logger.debug('Checking database version', { requiredVersion: MINIMUM_MIGRATION_VERSION }) - - const version = parseInt(await getMigrationVersion(connection), 10) - if (isNaN(version) || version < MINIMUM_MIGRATION_VERSION) { - throw new Error('Postgres database not up to date') - } - } - - return pRetry(check, { - factor: 1, - retries: settings.MAX_SCHEMA_POLL_RETRIES, - minTimeout: settings.SCHEMA_POLL_INTERVAL * 1000, - maxTimeout: settings.SCHEMA_POLL_INTERVAL * 1000, - }) -} - -/** - * Gets the current migration version from the frontend database. Throws on query - * error, if no migration version can be found, or if the current migration state - * is dirty. - * - * @param connection The database connection. - */ -async function getMigrationVersion(connection: Connection): Promise { - const rows: { - version: string - dirty: boolean - }[] = await connection.query('select * from schema_migrations') - - if (rows.length > 0 && !rows[0].dirty) { - return rows[0].version - } - - throw new Error('Unusable migration state.') -} - -/** - * Instrument `callback` with Postgres query histogram and error counter. - * - * @param callback The function invoke with the connection. - */ -export function instrumentQuery(callback: () => Promise): Promise { - return instrument(metrics.postgresQueryDurationHistogram, metrics.postgresQueryErrorsCounter, callback) -} - -/** - * Invoke `callback` with a transactional Postgres entity manager created - * from the wrapped connection. - * - * @param connection The Postgres connection. - * @param callback The function invoke with the entity manager. - */ -export function withInstrumentedTransaction( - connection: Connection, - callback: (connection: EntityManager) => Promise -): Promise { - return instrumentQuery(() => connection.transaction(callback)) -} - -/** - * Invokes the callback wrapped in instrumentQuery with the given entityManager, if supplied, - * and runs the callback in a transaction with a fresh entityManager otherwise. - * - * @param connection The Postgres connection. - * @param entityManager The EntityManager to use as part of a transaction. - * @param callback The function invoke with the entity manager. - */ -export function instrumentQueryOrTransaction( - connection: Connection, - entityManager: EntityManager | undefined, - callback: (connection: EntityManager) => Promise -): Promise { - return entityManager - ? instrumentQuery(() => callback(entityManager)) - : withInstrumentedTransaction(connection, callback) -} diff --git a/cmd/precise-code-intel/src/shared/database/settings.ts b/cmd/precise-code-intel/src/shared/database/settings.ts deleted file mode 100644 index d3daa90d152..00000000000 --- a/cmd/precise-code-intel/src/shared/database/settings.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { readEnvInt } from '../settings' - -/** How many times to try to check the current database migration version on startup. */ -export const MAX_SCHEMA_POLL_RETRIES = readEnvInt('MAX_SCHEMA_POLL_RETRIES', 60) - -/** How long to wait (in seconds) between queries to check the current database migration version on startup. */ -export const SCHEMA_POLL_INTERVAL = readEnvInt('SCHEMA_POLL_INTERVAL', 5) - -/** How many times to try to connect to Postgres on startup. */ -export const MAX_CONNECTION_RETRIES = readEnvInt('MAX_CONNECTION_RETRIES', 60) - -/** How long to wait (minimum, in seconds) between Postgres connection attempts. */ -export const MIN_CONNECTION_RETRY_TIMEOUT = readEnvInt('MIN_CONNECTION_RETRY_TIMEOUT', 1) - -/** How long to wait (maximum, in seconds) between Postgres connection attempts. */ -export const MAX_CONNECTION_RETRY_TIMEOUT = readEnvInt('MAX_CONNECTION_RETRY_TIMEOUT', 1) diff --git a/cmd/precise-code-intel/src/shared/database/sqlite.ts b/cmd/precise-code-intel/src/shared/database/sqlite.ts deleted file mode 100644 index b909e934136..00000000000 --- a/cmd/precise-code-intel/src/shared/database/sqlite.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Connection, createConnection as _createConnection } from 'typeorm' -import { Logger } from 'winston' -import { DatabaseLogger } from './logger' - -/** - * Create a SQLite connection from the given filename. - * - * @param database The database filename. - * @param entities The set of expected entities present in this schema. - * @param logger The logger instance. - */ -export function createSqliteConnection( - database: string, - // Decorators are not possible type check - // eslint-disable-next-line @typescript-eslint/ban-types - entities: Function[], - logger: Logger -): Promise { - return _createConnection({ - type: 'sqlite', - name: database, - database, - entities, - synchronize: true, - logger: new DatabaseLogger(logger), - maxQueryExecutionTime: 1000, - }) -} diff --git a/cmd/precise-code-intel/src/shared/datastructures/bloom-filter.test.ts b/cmd/precise-code-intel/src/shared/datastructures/bloom-filter.test.ts deleted file mode 100644 index d981b27d0d8..00000000000 --- a/cmd/precise-code-intel/src/shared/datastructures/bloom-filter.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createFilter, testFilter } from './bloom-filter' - -describe('testFilter', () => { - it('should test set membership', async () => { - const filter = await createFilter(['foo', 'bar', 'baz']) - expect(await testFilter(filter, 'foo')).toBeTruthy() - expect(await testFilter(filter, 'bar')).toBeTruthy() - expect(await testFilter(filter, 'baz')).toBeTruthy() - expect(await testFilter(filter, 'bonk')).toBeFalsy() - expect(await testFilter(filter, 'quux')).toBeFalsy() - }) -}) diff --git a/cmd/precise-code-intel/src/shared/datastructures/bloom-filter.ts b/cmd/precise-code-intel/src/shared/datastructures/bloom-filter.ts deleted file mode 100644 index 0c579394a0f..00000000000 --- a/cmd/precise-code-intel/src/shared/datastructures/bloom-filter.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { BloomFilter } from 'bloomfilter' -import { gunzipJSON, gzipJSON } from '../encoding/json' -import * as settings from './settings' - -/** A type that describes a the encoded version of a bloom filter. */ -export type EncodedBloomFilter = Buffer - -/** - * Create a bloom filter containing the given values and return an encoded verion. - * - * @param values The values to add to the bloom filter. - */ -export function createFilter(values: string[]): Promise { - const filter = new BloomFilter(settings.BLOOM_FILTER_BITS, settings.BLOOM_FILTER_NUM_HASH_FUNCTIONS) - for (const value of values) { - filter.add(value) - } - - // Need to shed the type of the array - const buckets = Array.from(filter.buckets) - - // Store the number of hash functions used to create this as it may change after - // this value is serialized. We don't want to test with more hash functions than - // it was created with, otherwise we'll get false negatives. - return gzipJSON({ numHashFunctions: settings.BLOOM_FILTER_NUM_HASH_FUNCTIONS, buckets }) -} - -/** - * Decode `filter` as created by `createFilter` and determine if `value` is a - * possible element. This may return a false positive (returning true if the - * element is not actually a member), but will not return false negatives. - * - * @param filter The encoded filter. - * @param value The value to test membership. - */ -export async function testFilter(filter: EncodedBloomFilter, value: string): Promise { - const { numHashFunctions, buckets } = await gunzipJSON(filter) - return new BloomFilter(buckets, numHashFunctions).test(value) -} diff --git a/cmd/precise-code-intel/src/shared/datastructures/default-map.test.ts b/cmd/precise-code-intel/src/shared/datastructures/default-map.test.ts deleted file mode 100644 index 529a4c72cc2..00000000000 --- a/cmd/precise-code-intel/src/shared/datastructures/default-map.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { DefaultMap } from './default-map' - -describe('DefaultMap', () => { - it('should leave get unchanged', () => { - const map = new DefaultMap(() => 'bar') - expect(map.get('foo')).toBeUndefined() - }) - - it('should create values on access', () => { - const map = new DefaultMap(() => 'bar') - expect(map.getOrDefault('foo')).toEqual('bar') - }) - - it('should respect explicit set', () => { - const map = new DefaultMap(() => 'bar') - map.set('foo', 'baz') - expect(map.getOrDefault('foo')).toEqual('baz') - }) - - it('should support nested gets', () => { - const map = new DefaultMap>( - () => new DefaultMap(() => []) - ) - - map.getOrDefault('foo').getOrDefault('bar').push('baz') - - map.getOrDefault('foo').getOrDefault('bar').push('bonk') - - const inner = map.get('foo') - expect(inner?.get('bar')).toEqual(['baz', 'bonk']) - }) -}) diff --git a/cmd/precise-code-intel/src/shared/datastructures/default-map.ts b/cmd/precise-code-intel/src/shared/datastructures/default-map.ts deleted file mode 100644 index b9811852b1a..00000000000 --- a/cmd/precise-code-intel/src/shared/datastructures/default-map.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * An extension of `Map` that defines `getOrDefault` for a type of stunted - * autovivification. This saves a bunch of code that needs to check if a - * nested type within a map is undefined on first access. - */ -export class DefaultMap extends Map { - /** - * Returns a new `DefaultMap`. - * - * @param defaultFactory The factory invoked when an undefined value is accessed. - */ - constructor(private defaultFactory: () => V) { - super() - } - - /** - * Get a key from the map. If the key does not exist, the default factory produces - * a value and inserted into the map before being returned. - * - * @param key The key to retrieve. - */ - public getOrDefault(key: K): V { - let value = super.get(key) - if (value !== undefined) { - return value - } - - value = this.defaultFactory() - this.set(key, value) - return value - } -} diff --git a/cmd/precise-code-intel/src/shared/datastructures/disjoint-set.test.ts b/cmd/precise-code-intel/src/shared/datastructures/disjoint-set.test.ts deleted file mode 100644 index c6f2d13b888..00000000000 --- a/cmd/precise-code-intel/src/shared/datastructures/disjoint-set.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DisjointSet } from './disjoint-set' - -describe('DisjointSet', () => { - it('should traverse relations in both directions', () => { - const set = new DisjointSet() - set.union(1, 2) - set.union(3, 4) - set.union(1, 3) - set.union(5, 6) - - expect(set.extractSet(1)).toEqual(new Set([1, 2, 3, 4])) - expect(set.extractSet(2)).toEqual(new Set([1, 2, 3, 4])) - expect(set.extractSet(3)).toEqual(new Set([1, 2, 3, 4])) - expect(set.extractSet(4)).toEqual(new Set([1, 2, 3, 4])) - expect(set.extractSet(5)).toEqual(new Set([5, 6])) - expect(set.extractSet(6)).toEqual(new Set([5, 6])) - }) -}) diff --git a/cmd/precise-code-intel/src/shared/datastructures/disjoint-set.ts b/cmd/precise-code-intel/src/shared/datastructures/disjoint-set.ts deleted file mode 100644 index a64c8ee456f..00000000000 --- a/cmd/precise-code-intel/src/shared/datastructures/disjoint-set.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { DefaultMap } from './default-map' - -/** - * A modified disjoint set or union-find data structure. Allows linking - * items together and retrieving the set of all items for a given set. - */ -export class DisjointSet { - /** For every linked value `v1` and `v2`, `v2` in `links[v1]` and `v1` in `links[v2]`. */ - private links = new DefaultMap>(() => new Set()) - - /** Return an iterator of all elements in the set. */ - public keys(): IterableIterator { - return this.links.keys() - } - - /** - * Link two values into the same set. If one or the other value is - * already in the set, then the sets of the two values will merge. - * - * @param v1 One linked value. - * @param v2 The other linked value. - */ - public union(v1: T, v2: T): void { - this.links.getOrDefault(v1).add(v2) - this.links.getOrDefault(v2).add(v1) - } - - /** - * Return the values in the same set as the given source value. - * - * @param v The source value. - */ - public extractSet(v: T): Set { - const set = new Set() - - let frontier = [v] - while (frontier.length > 0) { - const val = frontier.pop() - if (val === undefined) { - // hol up, frontier was non-empty! - throw new Error('Impossible condition.') - } - - if (!set.has(val)) { - set.add(val) - frontier = frontier.concat(Array.from(this.links.get(val) || [])) - } - } - - return set - } -} diff --git a/cmd/precise-code-intel/src/shared/datastructures/orderedset.test.ts b/cmd/precise-code-intel/src/shared/datastructures/orderedset.test.ts deleted file mode 100644 index d536b07d430..00000000000 --- a/cmd/precise-code-intel/src/shared/datastructures/orderedset.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { OrderedSet } from './orderedset' - -describe('OrderedSet', () => { - it('should not contain duplicates', () => { - const set = new OrderedSet(value => value) - set.push('foo') - set.push('foo') - set.push('bar') - set.push('bar') - - expect(set.values).toEqual(['foo', 'bar']) - }) - - it('should retain insertion order', () => { - const set = new OrderedSet(value => value) - set.push('bonk') - set.push('baz') - set.push('foo') - set.push('bar') - set.push('bar') - set.push('baz') - set.push('foo') - set.push('bonk') - - expect(set.values).toEqual(['bonk', 'baz', 'foo', 'bar']) - }) -}) diff --git a/cmd/precise-code-intel/src/shared/datastructures/orderedset.ts b/cmd/precise-code-intel/src/shared/datastructures/orderedset.ts deleted file mode 100644 index 17494cd14ba..00000000000 --- a/cmd/precise-code-intel/src/shared/datastructures/orderedset.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** A set of values that maintains insertion order. */ -export class OrderedSet { - private set = new Map() - - /** - * Create a new ordered set. - * - * @param makeKey The function to generate a unique string key from a value. - * @param values A set of values used to seed the set. - */ - constructor(private makeKey: (value: T) => string, values?: T[]) { - if (!values) { - return - } - - for (const value of values) { - this.push(value) - } - } - - /** The deduplicated values in insertion order. */ - public get values(): T[] { - return Array.from(this.set.values()) - } - - /** Insert a value into the set if it hasn't been seen before. */ - public push(value: T): void { - this.set.set(this.makeKey(value), value) - } -} diff --git a/cmd/precise-code-intel/src/shared/datastructures/settings.ts b/cmd/precise-code-intel/src/shared/datastructures/settings.ts deleted file mode 100644 index 16940775ed7..00000000000 --- a/cmd/precise-code-intel/src/shared/datastructures/settings.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { readEnvInt } from '../settings' - -// These parameters give us a 1 in 1.38x10^9 false positive rate if we assume -// that the number of unique URIs referrable by an external package is of the -// order of 10k (....but I have no idea if that is a reasonable estimate....). -// -// See the following link for a bloom calculator: https://hur.st/bloomfilter - -/** The number of bits allocated for new bloom filters. */ -export const BLOOM_FILTER_BITS = readEnvInt('BLOOM_FILTER_BITS', 64 * 1024) - -/** The number of hash functions to use to determine if a value is a member of the filter. */ -export const BLOOM_FILTER_NUM_HASH_FUNCTIONS = readEnvInt('BLOOM_FILTER_NUM_HASH_FUNCTIONS', 16) diff --git a/cmd/precise-code-intel/src/shared/encoding/json.test.ts b/cmd/precise-code-intel/src/shared/encoding/json.test.ts deleted file mode 100644 index e4a1da5c668..00000000000 --- a/cmd/precise-code-intel/src/shared/encoding/json.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { gunzipJSON, gzipJSON } from './json' - -describe('gzipJSON', () => { - it('should preserve maps', async () => { - const m = new Map([ - ['a', 1], - ['b', 2], - ['c', 3], - ]) - - const value = { - foo: [1, 2, 3], - bar: ['abc', 'xyz'], - baz: m, - } - - const encoded = await gzipJSON(value) - const decoded = await gunzipJSON(encoded) - expect(decoded).toEqual(value) - }) - - it('should preserve sets', async () => { - const s = new Set([1, 2, 3, 4, 5]) - - const value = { - foo: [1, 2, 3], - bar: ['abc', 'xyz'], - baz: s, - } - - const encoded = await gzipJSON(value) - const decoded = await gunzipJSON(encoded) - expect(decoded).toEqual(value) - }) -}) diff --git a/cmd/precise-code-intel/src/shared/encoding/json.ts b/cmd/precise-code-intel/src/shared/encoding/json.ts deleted file mode 100644 index 74f60cd2b98..00000000000 --- a/cmd/precise-code-intel/src/shared/encoding/json.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { gunzip, gzip } from 'mz/zlib' - -/** - * Return the gzipped JSON representation of `value`. - * - * @param value The value to encode. - */ -export function gzipJSON(value: T): Promise { - return gzip(Buffer.from(dumpJSON(value))) -} - -/** - * Reverse the operation of `gzipJSON`. - * - * @param value The value to decode. - */ -export async function gunzipJSON(value: Buffer): Promise { - return parseJSON((await gunzip(value)).toString()) -} - -/** The replacer used by dumpJSON to encode map and set values. */ -export const jsonReplacer = (key: string, value: T): { type: string; value: unknown } | T => { - if (value instanceof Map) { - return { - type: 'map', - value: [...value], - } - } - - if (value instanceof Set) { - return { - type: 'set', - value: [...value], - } - } - - return value -} - -/** - * Return the JSON representation of `value`. This has special logic to - * convert ES6 map and set structures into a JSON-representable value. - * This method, along with `parseJSON` should be used over the raw methods - * if the payload may contain maps. - * - * @param value The value to jsonify. - */ -export function dumpJSON(value: T): string { - return JSON.stringify(value, jsonReplacer) -} - -/** - * Parse the JSON representation of `value`. This has special logic to - * unmarshal map and set objects as encoded by `dumpJSON`. - * - * @param value The value to unmarshal. - */ -export function parseJSON(value: string): T { - return JSON.parse(value, (_, oldValue) => { - if (typeof oldValue === 'object' && oldValue !== null) { - if (oldValue.type === 'map') { - return new Map(oldValue.value) - } - - if (oldValue.type === 'set') { - return new Set(oldValue.value) - } - } - - return oldValue - }) -} diff --git a/cmd/precise-code-intel/src/shared/gitserver/gitserver.test.ts b/cmd/precise-code-intel/src/shared/gitserver/gitserver.test.ts deleted file mode 100644 index ca1c57acefd..00000000000 --- a/cmd/precise-code-intel/src/shared/gitserver/gitserver.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import nock from 'nock' -import { flattenCommitParents, getCommitsNear, getDirectoryChildren } from './gitserver' - -describe('getDirectoryChildren', () => { - it('should parse response from gitserver', async () => { - nock('http://frontend') - .post('/.internal/git/42/exec', { - args: ['ls-tree', '--name-only', 'c', '--', '.', 'foo/', 'bar/baz/'], - }) - .reply(200, 'a\nb\nbar/baz/x\nbar/baz/y\nc\nfoo/1\nfoo/2\nfoo/3\n') - - expect( - await getDirectoryChildren({ - frontendUrl: 'frontend', - repositoryId: 42, - commit: 'c', - dirnames: ['', 'foo', 'bar/baz/'], - }) - ).toEqual( - new Map([ - ['', new Set(['a', 'b', 'c'])], - ['foo', new Set(['foo/1', 'foo/2', 'foo/3'])], - ['bar/baz/', new Set(['bar/baz/x', 'bar/baz/y'])], - ]) - ) - }) -}) - -describe('getCommitsNear', () => { - it('should parse response from gitserver', async () => { - nock('http://frontend') - .post('/.internal/git/42/exec', { args: ['log', '--pretty=%H %P', 'l', '-150'] }) - .reply(200, 'a\nb c\nd e f\ng h i j k l') - - expect(await getCommitsNear('frontend', 42, 'l')).toEqual( - new Map([ - ['a', new Set()], - ['b', new Set(['c'])], - ['d', new Set(['e', 'f'])], - ['g', new Set(['h', 'i', 'j', 'k', 'l'])], - ]) - ) - }) - - it('should handle request for unknown repository', async () => { - nock('http://frontend').post('/.internal/git/42/exec').reply(404) - - expect(await getCommitsNear('frontend', 42, 'l')).toEqual(new Map()) - }) -}) - -describe('flattenCommitParents', () => { - it('should handle multiple commits', () => { - expect(flattenCommitParents(['a', 'b c', 'd e f', '', 'g h i j k l', 'm '])).toEqual( - new Map([ - ['a', new Set()], - ['b', new Set(['c'])], - ['d', new Set(['e', 'f'])], - ['g', new Set(['h', 'i', 'j', 'k', 'l'])], - ['m', new Set()], - ]) - ) - }) -}) diff --git a/cmd/precise-code-intel/src/shared/gitserver/gitserver.ts b/cmd/precise-code-intel/src/shared/gitserver/gitserver.ts deleted file mode 100644 index cc6079e2e48..00000000000 --- a/cmd/precise-code-intel/src/shared/gitserver/gitserver.ts +++ /dev/null @@ -1,197 +0,0 @@ -import got from 'got' -import { MAX_COMMITS_PER_UPDATE } from '../constants' -import { TracingContext, logAndTraceCall } from '../tracing' -import { instrument } from '../metrics' -import * as metrics from './metrics' - -/** - * Get the children of a set of directories at a particular commit. This function - * returns a list of sets `L` where `L[i]` is the set of children for `dirnames[i]`. - * - * Except for the root directory, denoted by the empty string, all supplied - * directories should be disjoint (one should not contain another). - * - * @param args Parameter bag. - */ -export async function getDirectoryChildren({ - frontendUrl, - repositoryId, - commit, - dirnames, - ctx = {}, -}: { - /** The url of the frontend internal API. */ - frontendUrl: string - /** The repository identifier. */ - repositoryId: number - /** The commit from which the gitserver queries should start. */ - commit: string - /** A list of repo-root-relative directories. */ - dirnames: string[] - /** The tracing context. */ - ctx?: TracingContext -}): Promise>> { - const args = ['ls-tree', '--name-only', commit, '--'] - - for (const dirname of dirnames) { - if (dirname === '') { - args.push('.') - } else { - args.push(dirname.endsWith('/') ? dirname : dirname + '/') - } - } - - // Retrieve a flat list of children of the dirnames constructed above. This returns a flat - // list sorted alphabetically, which we then need to partition by parent directory. - const uncategorizedChildren = await gitserverExecLines(frontendUrl, repositoryId, args, ctx) - - const childMap = new Map() - for (const dirname of dirnames) { - childMap.set( - dirname, - dirname === '' - ? new Set(uncategorizedChildren.filter(line => !line.includes('/'))) - : new Set(uncategorizedChildren.filter(line => line.startsWith(dirname))) - ) - } - - return childMap -} - -/** - * Get a list of commits for the given repository with their parent starting at the - * given commit and returning at most `MAX_COMMITS_PER_UPDATE` commits. The output - * is a map from commits to a set of parent commits. The set of parents may be empty. - * - * If the repository or commit is unknown by gitserver, then the the results will be - * empty but no error will be thrown. Any other error type will be thrown without - * modification. - * - * @param frontendUrl The url of the frontend internal API. - * @param repositoryId The repository identifier. - * @param commit The commit from which the gitserver queries should start. - * @param ctx The tracing context. - */ -export async function getCommitsNear( - frontendUrl: string, - repositoryId: number, - commit: string, - ctx: TracingContext = {} -): Promise>> { - const args = ['log', '--pretty=%H %P', commit, `-${MAX_COMMITS_PER_UPDATE}`] - - try { - return flattenCommitParents(await gitserverExecLines(frontendUrl, repositoryId, args, ctx)) - } catch (error) { - if (error.response && error.response.statusCode === 404) { - // Unknown repository - return new Map() - } - - throw error - } -} - -/** - * Convert git log output into a parentage map. Each line of the input should have the - * form `commit p1 p2 p3...`, where commits without a parent appear on a line of their - * own. The output is a map from commits a set of parent commits. The set of parents may - * be empty. - * - * @param lines The output lines of `git log`. - */ -export function flattenCommitParents(lines: string[]): Map> { - const commits = new Map() - for (const line of lines) { - const trimmed = line.trim() - if (trimmed === '') { - continue - } - - const [child, ...parentCommits] = trimmed.split(' ') - commits.set(child, new Set(parentCommits)) - } - - return commits -} - -/** - * Get the current tip of the default branch of the given repository. - * - * @param frontendUrl The url of the frontend internal API. - * @param repositoryId The repository identifier. - * @param ctx The tracing context. - */ -export async function getHead( - frontendUrl: string, - repositoryId: number, - ctx: TracingContext = {} -): Promise { - const lines = await gitserverExecLines(frontendUrl, repositoryId, ['rev-parse', 'HEAD'], ctx) - if (lines.length === 0) { - return undefined - } - - return lines[0] -} - -/** - * Execute a git command via gitserver and return its output split into non-empty lines. - * - * @param frontendUrl The url of the frontend internal API. - * @param repositoryId The repository identifier. - * @param args The command to run in the repository's git directory. - * @param ctx The tracing context. - */ -export async function gitserverExecLines( - frontendUrl: string, - repositoryId: number, - args: string[], - ctx: TracingContext = {} -): Promise { - return (await gitserverExec(frontendUrl, repositoryId, args, ctx)).split('\n').filter(line => Boolean(line)) -} - -/** - * Execute a git command via gitserver and return its raw output. - * - * @param frontendUrl The url of the frontend internal API. - * @param repositoryId The repository identifier. - * @param args The command to run in the repository's git directory. - * @param ctx The tracing context. - */ -function gitserverExec( - frontendUrl: string, - repositoryId: number, - args: string[], - ctx: TracingContext = {} -): Promise { - if (args[0] === 'git') { - // Prevent this from happening again: - // https://github.com/sourcegraph/sourcegraph/pull/5941 - // https://github.com/sourcegraph/sourcegraph/pull/6548 - throw new Error('Gitserver commands should not be prefixed with `git`') - } - - return logAndTraceCall(ctx, 'Executing git command', () => - instrument(metrics.gitserverDurationHistogram, metrics.gitserverErrorsCounter, async () => { - // Perform request - this may fail with a 404 or 500 - const resp = await got.post(new URL(`http://${frontendUrl}/.internal/git/${repositoryId}/exec`).href, { - body: JSON.stringify({ args }), - }) - - // Read trailers on a 200-level response - const status = resp.trailers['x-exec-exit-status'] - const stderr = resp.trailers['x-exec-stderr'] - - // Determine if underlying git command failed and throw an error - // in that case. Status will be undefined in some of our tests and - // will be the process exit code (given as a string) otherwise. - if (status !== undefined && status !== '0') { - throw new Error(`Failed to run git command ${['git', ...args].join(' ')}: ${String(stderr)}`) - } - - return resp.body - }) - ) -} diff --git a/cmd/precise-code-intel/src/shared/gitserver/metrics.ts b/cmd/precise-code-intel/src/shared/gitserver/metrics.ts deleted file mode 100644 index 4862d94deb9..00000000000 --- a/cmd/precise-code-intel/src/shared/gitserver/metrics.ts +++ /dev/null @@ -1,15 +0,0 @@ -import promClient from 'prom-client' - -// -// Gitserver Metrics - -export const gitserverDurationHistogram = new promClient.Histogram({ - name: 'lsif_gitserver_duration_seconds', - help: 'Total time spent on gitserver exec queries.', - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -export const gitserverErrorsCounter = new promClient.Counter({ - name: 'lsif_gitserver_errors_total', - help: 'The number of errors that occurred during a gitserver exec query.', -}) diff --git a/cmd/precise-code-intel/src/shared/input.test.ts b/cmd/precise-code-intel/src/shared/input.test.ts deleted file mode 100644 index ba80ada6653..00000000000 --- a/cmd/precise-code-intel/src/shared/input.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as fs from 'mz/fs' -import * as path from 'path' -import * as zlib from 'mz/zlib' -import rmfr from 'rmfr' -import { parseJsonLines, readGzippedJsonElementsFromFile, splitLines } from './input' -import { Readable } from 'stream' - -describe('readGzippedJsonElements', () => { - let tempPath!: string - - beforeAll(async () => { - tempPath = await fs.mkdtemp('test-', { encoding: 'utf8' }) - }) - - afterAll(async () => { - await rmfr(tempPath) - }) - - it('should decode gzip', async () => { - const lines = [ - { type: 'vertex', label: 'project' }, - { type: 'vertex', label: 'document' }, - { type: 'edge', label: 'item' }, - { type: 'edge', label: 'moniker' }, - ] - - const filename = path.join(tempPath, 'gzip.txt') - - const chunks = [] - for await (const chunk of Readable.from(lines.map(l => JSON.stringify(l)).join('\n')).pipe(zlib.createGzip())) { - chunks.push(chunk) - } - - await fs.writeFile(filename, Buffer.concat(chunks)) - - const elements: unknown[] = [] - for await (const element of readGzippedJsonElementsFromFile(filename)) { - elements.push(element) - } - - expect(elements).toEqual(lines) - }) - - it('should fail without gzip', async () => { - const lines = [ - '{"type": "vertex", "label": "project"}', - '{"type": "vertex", "label": "document"}', - '{"type": "edge", "label": "item"}', - '{"type": "edge", "label": "moniker"}', - ] - - const filename = path.join(tempPath, 'nogzip.txt') - await fs.writeFile(filename, lines.join('\n')) - - await expect(consume(readGzippedJsonElementsFromFile(filename))).rejects.toThrowError( - new Error('incorrect header check') - ) - }) - - it('should throw an error on IO error', async () => { - const filename = path.join(tempPath, 'missing.txt') - - await expect(consume(readGzippedJsonElementsFromFile(filename))).rejects.toThrowError( - new Error(`ENOENT: no such file or directory, open '${filename}'`) - ) - }) -}) - -describe('splitLines', () => { - it('should split input by newline', async () => { - const chunks = ['foo\n', 'bar', '\nbaz\n\nbonk\nqu', 'ux'] - - const lines: string[] = [] - for await (const line of splitLines(generate(chunks))) { - lines.push(line) - } - - expect(lines).toEqual(['foo', 'bar', 'baz', '', 'bonk', 'quux']) - }) -}) - -describe('parseJsonLines', () => { - it('should parse JSON', async () => { - const lines = [ - { type: 'vertex', label: 'project' }, - { type: 'vertex', label: 'document' }, - { type: 'edge', label: 'item' }, - { type: 'edge', label: 'moniker' }, - ] - - const elements: unknown[] = [] - for await (const element of parseJsonLines(generate(lines.map(l => JSON.stringify(l))))) { - elements.push(element) - } - - expect(elements).toEqual(lines) - }) - - it('should wrap parse errors', async () => { - const input = [ - '{"type": "vertex", "label": "project"}', - '{"type": "vertex", "label": "document"}', - '{"type": "edge", "label": "item"}', - '{"type": "edge" "label": "moniker"}', // missing comma - ] - - await expect(consume(parseJsonLines(generate(input)))).rejects.toThrowError( - new Error( - 'Failed to process line #4 ({"type": "edge" "label": "moniker"}): Unexpected string in JSON at position 16' - ) - ) - }) -}) - -// -// Helpers - -async function* generate(values: T[]): AsyncIterable { - // Make it actually async - await Promise.resolve() - - for (const value of values) { - yield value - } -} - -async function consume(iterable: AsyncIterable): Promise { - // We need to consume the iterable but can't make a meaningful - // binding for each element of the iteration. - for await (const _ of iterable) { - // no-op body, just consume iterable - } -} diff --git a/cmd/precise-code-intel/src/shared/input.ts b/cmd/precise-code-intel/src/shared/input.ts deleted file mode 100644 index 6b13a980061..00000000000 --- a/cmd/precise-code-intel/src/shared/input.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as fs from 'mz/fs' -import { createGunzip } from 'zlib' - -/** - * Yield parsed JSON elements from a file containing the gzipped JSON lines. - * - * @param path The filepath containing a gzipped compressed stream of JSON lines composing the LSIF dump. - */ -export function readGzippedJsonElementsFromFile(path: string): AsyncIterable { - const input = fs.createReadStream(path) - const piped = input.pipe(createGunzip()) - - // Ensure we forward errors opening/reading the file to the async - // iterator opened below. - input.on('error', error => piped.emit('error', error)) - - // Create the iterable - return parseJsonLines(splitLines(piped)) -} - -/** - * Transform an async iterable into an async iterable of lines. Each value - * is stripped of its trailing newline. Lines may be empty. - * - * @param input The input buffer. - */ -export async function* splitLines(input: AsyncIterable): AsyncIterable { - let buffer = '' - for await (const data of input) { - buffer += data.toString() - - do { - const index = buffer.indexOf('\n') - if (index < 0) { - break - } - - yield buffer.substring(0, index) - buffer = buffer.substring(index + 1) - } while (true) - } - - yield buffer -} - -/** - * Parses a stream of uncompressed JSON strings and yields each parsed line. - * Ignores empty lines. Throws an exception with line index and content when - * a non-empty line is not valid JSON. - * - * @param lines An iterable of JSON lines. - */ -export async function* parseJsonLines(lines: AsyncIterable): AsyncIterable { - let index = 0 - for await (const data of lines) { - index++ - - if (!data) { - continue - } - - try { - yield JSON.parse(data) - } catch (error) { - throw new Error(`Failed to process line #${index} (${data}): ${String(error?.message)}`) - } - } -} diff --git a/cmd/precise-code-intel/src/shared/logging.ts b/cmd/precise-code-intel/src/shared/logging.ts deleted file mode 100644 index d4d03a8e936..00000000000 --- a/cmd/precise-code-intel/src/shared/logging.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { createLogger as _createLogger, Logger, transports } from 'winston' -import { format, TransformableInfo } from 'logform' -import { inspect } from 'util' -import { MESSAGE } from 'triple-beam' - -/** - * Return a sanitized log level. - * - * @param value The raw log level. - */ -function toLogLevel(value: string): 'debug' | 'info' | 'warn' | 'error' { - if (value === 'debug' || value === 'info' || value === 'warn' || value === 'error') { - return value - } - - return 'info' -} - -/** The maximum level log message to output. */ -const LOG_LEVEL: 'debug' | 'info' | 'warn' | 'error' = toLogLevel((process.env.LOG_LEVEL || 'info').toLowerCase()) - -/** Create a structured logger. */ -export function createLogger(service: string): Logger { - const formatTransformer = (info: TransformableInfo): TransformableInfo => { - const attributes: { [name: string]: unknown } = {} - for (const [key, value] of Object.entries(info)) { - if (key !== 'level' && key !== 'message') { - attributes[key] = value - } - } - - info[MESSAGE] = `${info.level} ${info.message} ${inspect(attributes)}` - return info - } - - const uppercaseTransformer = (info: TransformableInfo): TransformableInfo => { - info.level = info.level.toUpperCase() - return info - } - - const colors = { - debug: 'dim', - info: 'cyan', - warn: 'yellow', - error: 'red', - } - - return _createLogger({ - level: LOG_LEVEL, - // Need to upper case level before colorization or we destroy ANSI codes - format: format.combine({ transform: uppercaseTransformer }, format.colorize({ level: true, colors }), { - transform: formatTransformer, - }), - defaultMeta: { service }, - transports: [new transports.Console({})], - }) -} - -/** Creates a silent logger. */ -export function createSilentLogger(): Logger { - return _createLogger({ silent: true }) -} - -/** - * Log the beginning, end, and exception of an operation. - * - * @param name The log message to output. - * @param logger The logger instance. - * @param f The operation to perform. - */ -export async function logCall(name: string, logger: Logger, f: () => Promise | T): Promise { - const timer = logger.startTimer() - logger.debug(`${name}: starting`) - - try { - const value = await f() - timer.done({ message: `${name}: finished`, level: 'debug' }) - return value - } catch (error) { - timer.done({ message: `${name}: failed`, level: 'error', error }) - throw error - } -} diff --git a/cmd/precise-code-intel/src/shared/maps.test.ts b/cmd/precise-code-intel/src/shared/maps.test.ts deleted file mode 100644 index ea564762c5e..00000000000 --- a/cmd/precise-code-intel/src/shared/maps.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { mustGetFromEither } from './maps' - -describe('mustGetFromEither', () => { - it('should return first defined value', () => { - const map1 = new Map() - const map2 = new Map() - - map2.set('foo', 'baz') - expect(mustGetFromEither(map1, map2, 'foo', '')).toEqual('baz') - map1.set('foo', 'bar') - expect(mustGetFromEither(map1, map2, 'foo', '')).toEqual('bar') - }) -}) diff --git a/cmd/precise-code-intel/src/shared/maps.ts b/cmd/precise-code-intel/src/shared/maps.ts deleted file mode 100644 index a7e870bcae2..00000000000 --- a/cmd/precise-code-intel/src/shared/maps.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Return the value of the given key from the given map. If the key does not - * exist in the map, an exception is thrown with the given error text. - * - * @param map The map to query. - * @param key The key to search for. - * @param elementType The type of element (used for exception message). - */ -export function mustGet(map: Map, key: K, elementType: string): V { - const value = map.get(key) - if (value !== undefined) { - return value - } - - throw new Error(`Unknown ${elementType} '${String(key)}'.`) -} - -/** - * Return the value of the given key from one of the given maps. The first - * non-undefined value to be found is returned. If the key does not exist in - * either map, an exception is thrown with the given error text. - * - * @param map1 The first map to query. - * @param map2 The second map to query. - * @param key The key to search for. - * @param elementType The type of element (used for exception message). - */ -export function mustGetFromEither( - map1: Map, - map2: Map, - key: K1 & K2, - elementType: string -): V1 | V2 { - for (const map of [map1, map2]) { - const value = map.get(key) - if (value !== undefined) { - return value - } - } - - throw new Error(`Unknown ${elementType} '${String(key)}'.`) -} diff --git a/cmd/precise-code-intel/src/shared/metrics.ts b/cmd/precise-code-intel/src/shared/metrics.ts deleted file mode 100644 index d0c7c879484..00000000000 --- a/cmd/precise-code-intel/src/shared/metrics.ts +++ /dev/null @@ -1,24 +0,0 @@ -import promClient from 'prom-client' - -/** - * Instrument the duration and error rate of the given function. - * - * @param durationHistogram The histogram for operation durations. - * @param errorsCounter The counter for errors. - * @param fn The function to instrument. - */ -export async function instrument( - durationHistogram: promClient.Histogram, - errorsCounter: promClient.Counter, - fn: () => Promise -): Promise { - const end = durationHistogram.startTimer() - try { - return await fn() - } catch (error) { - errorsCounter.inc() - throw error - } finally { - end() - } -} diff --git a/cmd/precise-code-intel/src/shared/models/hash.ts b/cmd/precise-code-intel/src/shared/models/hash.ts deleted file mode 100644 index 01778678a1b..00000000000 --- a/cmd/precise-code-intel/src/shared/models/hash.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as sqliteModels from './sqlite' - -/** - * Hash a string or numeric identifier into the range `[0, maxIndex)`. The - * hash algorithm here is similar to the one used in Java's String.hashCode. - * - * @param id The identifier to hash. - * @param maxIndex The maximum of the range. - */ -export function hashKey(id: sqliteModels.DefinitionReferenceResultId, maxIndex: number): number { - const s = `${id}` - - let hash = 0 - for (let i = 0; i < s.length; i++) { - const chr = s.charCodeAt(i) - hash = (hash << 5) - hash + chr - hash |= 0 - } - - // Hash value may be negative - must unset sign bit before modulus - return Math.abs(hash) % maxIndex -} diff --git a/cmd/precise-code-intel/src/shared/models/queries.ts b/cmd/precise-code-intel/src/shared/models/queries.ts deleted file mode 100644 index 374c27f54be..00000000000 --- a/cmd/precise-code-intel/src/shared/models/queries.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { MAX_TRAVERSAL_LIMIT } from '../constants' - -/** - * Return a recursive CTE `lineage` that returns ancestors of the commit for the given - * repository. This assumes that the repository identifier is $1 and the commit is $2. - */ -export function ancestorLineage(): string { - return ` - RECURSIVE lineage(id, "commit", parent, repository_id) AS ( - SELECT c.* FROM lsif_commits c WHERE c.repository_id = $1 AND c."commit" = $2 - UNION - SELECT c.* FROM lineage a JOIN lsif_commits c ON a.repository_id = c.repository_id AND a.parent = c."commit" - ) - ` -} - -/** - * Return a recursive CTE `lineage` that returns ancestors and descendants of the commit for - * the given repository. This assumes that the repository identifier is $1 and the commit is $2. - * This happens to evaluate in Postgres as a lazy generator, which allows us to pull the "next" - * closest commit in either direction from the source commit as needed. - */ -export function bidirectionalLineage(): string { - return ` - RECURSIVE lineage(id, "commit", parent_commit, repository_id, direction) AS ( - SELECT l.* FROM ( - -- seed recursive set with commit looking in ancestor direction - SELECT c.*, 'A' FROM lsif_commits c WHERE c.repository_id = $1 AND c."commit" = $2 - UNION - -- seed recursive set with commit looking in descendant direction - SELECT c.*, 'D' FROM lsif_commits c WHERE c.repository_id = $1 AND c."commit" = $2 - ) l - - UNION - - SELECT * FROM ( - WITH l_inner AS (SELECT * FROM lineage) - -- get next ancestors (multiple parents for merge commits) - SELECT c.*, 'A' FROM l_inner l JOIN lsif_commits c ON l.direction = 'A' AND c.repository_id = l.repository_id AND c."commit" = l.parent_commit - UNION - -- get next descendants - SELECT c.*, 'D' FROM l_inner l JOIN lsif_commits c ON l.direction = 'D' and c.repository_id = l.repository_id AND c.parent_commit = l."commit" - ) subquery - ) - ` -} - -/** - * Return a set of CTE definitions assuming the definition of a previous CTE named `lineage`. - * This creates the CTE `visible_ids`, which gathers the set of LSIF dump identifiers whose - * commit occurs in `lineage` (within the given traversal limit) and whose root does not - * overlap another visible dump from the same indexer. - * - * @param limit The maximum number of dumps that can be extracted from `lineage`. - */ -export function visibleDumps(limit: number = MAX_TRAVERSAL_LIMIT): string { - return ` - ${lineageWithDumps(limit)}, - visible_ids AS ( - -- Remove dumps where there exists another visible dump of smaller depth with an - -- overlapping root from the same indexer. Such dumps would not be returned with - -- a closest commit query so we don't want to return results for them in global - -- find-reference queries either. - SELECT DISTINCT t1.dump_id as id FROM lineage_with_dumps t1 WHERE NOT EXISTS ( - SELECT 1 FROM lineage_with_dumps t2 - WHERE t2.n < t1.n AND t1.indexer = t2.indexer AND ( - t2.root LIKE (t1.root || '%') OR - t1.root LIKE (t2.root || '%') - ) - ) - ) - ` -} - -/** - * Return a set of CTE definitions assuming the definition of a previous CTE named `lineage`. - * This creates the CTE `lineage_with_dumps`, which gathers the set of LSIF dump identifiers - * whose commit occurs in `lineage` (within the given traversal limit). - * - * @param limit The maximum number of dumps that can be extracted from `lineage`. - */ -function lineageWithDumps(limit: number = MAX_TRAVERSAL_LIMIT): string { - return ` - -- Limit the visibility to the maximum traversal depth and approximate - -- each commit's depth by its row number. - limited_lineage AS ( - SELECT a.*, row_number() OVER() as n from lineage a LIMIT ${limit} - ), - -- Correlate commits to dumps and filter out commits without LSIF data - lineage_with_dumps AS ( - SELECT a.*, d.root, d.indexer, d.id as dump_id FROM limited_lineage a - JOIN lsif_dumps d ON d.repository_id = a.repository_id AND d."commit" = a."commit" - ) - ` -} diff --git a/cmd/precise-code-intel/src/shared/models/sqlite.ts b/cmd/precise-code-intel/src/shared/models/sqlite.ts deleted file mode 100644 index 0c2dad72474..00000000000 --- a/cmd/precise-code-intel/src/shared/models/sqlite.ts +++ /dev/null @@ -1,305 +0,0 @@ -import * as lsif from 'lsif-protocol' -import { Column, Entity, Index, PrimaryColumn } from 'typeorm' -import { calcSqliteBatchSize } from './util' - -export type DocumentId = lsif.Id -export type DocumentPath = string -export type RangeId = lsif.Id -export type DefinitionResultId = lsif.Id -export type ReferenceResultId = lsif.Id -export type DefinitionReferenceResultId = DefinitionResultId | ReferenceResultId -export type HoverResultId = lsif.Id -export type MonikerId = lsif.Id -export type PackageInformationId = lsif.Id - -/** A type that describes a gzipped and JSON-encoded value of type `T`. */ -export type JSONEncoded = Buffer - -/** - * A type of hashed value created by hashing a value of type `T` and performing - * the modulus with a value of type `U`. This is to link the index of a result - * chunk to the hashed value of the identifiers stored within it. - */ -export type HashMod = number - -/** - * An entity within the database describing LSIF data for a single repository - * and commit pair. There should be only one metadata entity per database. - */ -@Entity({ name: 'meta' }) -export class MetaModel { - /** The number of model instances that can be inserted at once. */ - public static BatchSize = calcSqliteBatchSize(4) - - /** A unique ID required by typeorm entities: always zero here. */ - @PrimaryColumn('int') - public id!: number - - /** The version string of the input LSIF that created this database. */ - @Column('text') - public lsifVersion!: string - - /** The internal version of the LSIF system that created this database. */ - @Column('text') - public sourcegraphVersion!: string - - /** - * The number of result chunks allocated when converting the dump stored - * in this database. This is used as an upper bound for the hash into the - * `resultChunks` table and must be record to keep the hash generation - * stable. - */ - @Column('int') - public numResultChunks!: number -} - -/** - * An entity within the database describing LSIF data for a single repository and - * commit pair. This contains a JSON-encoded `DocumentData` object that describes - * relations within a single file of the dump. - */ -@Entity({ name: 'documents' }) -export class DocumentModel { - /** The number of model instances that can be inserted at once. */ - public static BatchSize = calcSqliteBatchSize(2) - - /** The root-relative path of the document. */ - @PrimaryColumn('text') - public path!: DocumentPath - - /** The JSON-encoded document data. */ - @Column('blob') - public data!: JSONEncoded -} - -/** - * An entity within the database describing LSIF data for a single repository and - * commit pair. This contains a JSON-encoded `ResultChunk` object that describes - * a subset of the definition and reference results of the dump. - */ -@Entity({ name: 'resultChunks' }) -export class ResultChunkModel { - /** The number of model instances that can be inserted at once. */ - public static BatchSize = calcSqliteBatchSize(2) - - /** - * The identifier of the chunk. This is also the index of the chunk during its - * construction, and the identifiers contained in this chunk hash to this index - * (modulo the total number of chunks for the dump). - */ - @PrimaryColumn('int') - public id!: HashMod - - /** The JSON-encoded chunk data. */ - @Column('blob') - public data!: JSONEncoded -} - -/** - * The base class for `DefinitionModel` and `ReferenceModel` as they have identical - * column descriptions. - */ -class Symbols { - /** The number of model instances that can be inserted at once. */ - public static BatchSize = calcSqliteBatchSize(8) - - /** A unique ID required by typeorm entities. */ - @PrimaryColumn('int') - public id!: number - - /** The name of the package type (e.g. npm, pip). */ - @Column('text') - public scheme!: string - - /** The unique identifier of the moniker. */ - @Column('text') - public identifier!: string - - /** The path of the document to which this reference belongs. */ - @Column('text') - public documentPath!: DocumentPath - - /** The zero-indexed line describing the start of this range. */ - @Column('int') - public startLine!: number - - /** The zero-indexed line describing the end of this range. */ - @Column('int') - public endLine!: number - - /** The zero-indexed line describing the start of this range. */ - @Column('int') - public startCharacter!: number - - /** The zero-indexed line describing the end of this range. */ - @Column('int') - public endCharacter!: number -} - -/** - * An entity within the database describing LSIF data for a single repository and commit - * pair. This maps external monikers to their range and the document that contains the - * definition of the moniker. - */ -@Entity({ name: 'definitions' }) -@Index(['scheme', 'identifier']) -export class DefinitionModel extends Symbols {} - -/** - * An entity within the database describing LSIF data for a single repository and commit - * pair. This maps imported monikers to their range and the document that contains a - * reference to the moniker. - */ -@Entity({ name: 'references' }) -@Index(['scheme', 'identifier']) -export class ReferenceModel extends Symbols {} - -/** - * Data for a single document within an LSIF dump. The data here can answer definitions, - * references, and hover queries if the results are all contained within the same document. - */ -export interface DocumentData { - /** A mapping from range identifiers to range data. */ - ranges: Map - - /** - * A map of hover result identifiers to hover results normalized as a single - * string. - */ - hoverResults: Map - - /** A map of moniker identifiers to moniker data. */ - monikers: Map - - /** A map of package information identifiers to package information data. */ - packageInformation: Map -} - -/** - * A range identifier that also specifies the identifier of the document to - * which it belongs. This is sometimes necessary as we hold definition and - * reference results between packages, but the identifier of the range must be - * looked up in a map of another encoded document. - */ -export interface DocumentIdRangeId { - /** - * The identifier of the document. The path of the document can be queried - * by this identifier in the containing document. - */ - documentId: DocumentId - - /** The identifier of the range in the referenced document. */ - rangeId: RangeId -} - -/** - * A range identifier that also specifies the path of the document to which it - * belongs. This is generally created by determining the path from an instance of - * `DocumentIdRangeId`. - */ -export interface DocumentPathRangeId { - /** The path of the document. */ - documentPath: DocumentPath - - /** The identifier of the range in the referenced document. */ - rangeId: RangeId -} - -/** - * A result chunk is a subset of the definition and reference result data for the - * LSIF dump. Results are inserted into chunks based on the hash code of their - * identifier (thus every chunk has a roughly proportional amount of data). - */ -export interface ResultChunkData { - /** - * A map from document identifiers to document paths. The document identifiers - * in the `documentIdRangeIds` field reference a concrete path stored here. - */ - documentPaths: Map - - /** - * A map from definition or reference result identifiers to the ranges that - * compose the result set. Each range is paired with the identifier of the - * document in which it can be found. - */ - documentIdRangeIds: Map -} - -/** - * An internal representation of a range vertex from an LSIF dump. It contains the same - * relevant edge data, which can be subsequently queried in the containing document. The - * data that was reachable via a result set has been collapsed into this object during - * import. - */ -export interface RangeData { - /** The line on which the range starts (0-indexed, inclusive). */ - startLine: number - - /** The line on which the range ends (0-indexed, inclusive). */ - startCharacter: number - - /** The character on which the range starts (0-indexed, inclusive). */ - endLine: number - - /** The character on which the range ends (0-indexed, inclusive). */ - endCharacter: number - - /** - * The identifier of the definition result attached to this range, if one exists. - * The definition result object can be queried by its identifier within the containing - * document. - */ - definitionResultId?: DefinitionResultId - - /** - * The identifier of the reference result attached to this range, if one exists. - * The reference result object can be queried by its identifier within the containing - * document. - */ - referenceResultId?: ReferenceResultId - - /** - * The identifier of the hover result attached to this range, if one exists. The - * hover result object can be queried by its identifier within the containing - * document. - */ - hoverResultId?: HoverResultId - - /** - * The set of moniker identifiers directly attached to this range. The moniker - * object can be queried by its identifier within the - * containing document. - */ - monikerIds: Set -} - -/** Data about a moniker attached to a range. */ -export interface MonikerData { - /** The kind of moniker (e.g. local, import, export). */ - kind: lsif.MonikerKind - - /** The name of the package type (e.g. npm, pip). */ - scheme: string - - /** The unique identifier of the moniker. */ - identifier: string - - /** - * The identifier of the package information to this moniker, if one exists. - * The package information object can be queried by its identifier within the - * containing document. - */ - packageInformationId?: PackageInformationId -} - -/** Additional data about a non-local moniker. */ -export interface PackageInformationData { - /** The name of the package the moniker describes. */ - name: string - - /** The version of the package the moniker describes. */ - version: string | null -} - -/** The entities composing the SQLite database models. */ -export const entities = [DefinitionModel, DocumentModel, MetaModel, ReferenceModel, ResultChunkModel] diff --git a/cmd/precise-code-intel/src/shared/models/util.ts b/cmd/precise-code-intel/src/shared/models/util.ts deleted file mode 100644 index 1297b3c142e..00000000000 --- a/cmd/precise-code-intel/src/shared/models/util.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Determine the table inserter batch size for an entity given the number of - * fields inserted for that entity. We cannot perform an insert operation in - * SQLite with more than 999 placeholder variables, so we need to flush our - * batch before we reach that amount. If fields are added to the models, the - * argument to this function also needs to change. - * - * @param numFields The number of fields for an entity. - */ -export function calcSqliteBatchSize(numFields: number): number { - return Math.floor(999 / numFields) -} diff --git a/cmd/precise-code-intel/src/shared/paths.ts b/cmd/precise-code-intel/src/shared/paths.ts deleted file mode 100644 index 45efce06b71..00000000000 --- a/cmd/precise-code-intel/src/shared/paths.ts +++ /dev/null @@ -1,56 +0,0 @@ -import * as constants from './constants' -import * as fs from 'mz/fs' -import * as path from 'path' - -/** - * Construct the path of the SQLite database file for the given dump. - * - * @param storageRoot The path where SQLite databases are stored. - * @param id The ID of the dump. - */ -export function dbFilename(storageRoot: string, id: number): string { - return path.join(storageRoot, constants.DBS_DIR, `${id}.lsif.db`) -} - -/** - * Construct the path of the raw upload file for the given identifier. - * - * @param storageRoot The path where uploads are stored. - * @param id The identifier of the upload. - */ -export function uploadFilename(storageRoot: string, id: number): string { - return path.join(storageRoot, constants.UPLOADS_DIR, `${id}.lsif.gz`) -} - -/** - * Returns the identifier of the database file. Handles both of the - * following formats: - * - * - `{id}.lsif.db` - * - `{id}-{repo}-{commit}.lsif.db` - * - * @param filename The filename. - */ -export function idFromFilename(filename: string): number | undefined { - const id = parseInt(path.parse(filename).name.split('-')[0], 10) - if (!isNaN(id)) { - return id - } - - return undefined -} - -/** - * Ensure the directory exists. - * - * @param directoryPath The directory path. - */ -export async function ensureDirectory(directoryPath: string): Promise { - try { - await fs.mkdir(directoryPath) - } catch (error) { - if (!(error && error.code === 'EEXIST')) { - throw error - } - } -} diff --git a/cmd/precise-code-intel/src/shared/settings.ts b/cmd/precise-code-intel/src/shared/settings.ts deleted file mode 100644 index 3fc4bd021c4..00000000000 --- a/cmd/precise-code-intel/src/shared/settings.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Reads an integer from an environment variable or defaults to the given value. - * - * @param key The environment variable name. - * @param defaultValue The default value. - */ -export function readEnvInt(key: string, defaultValue: number): number { - return (process.env[key] && parseInt(process.env[key] || '', 10)) || defaultValue -} diff --git a/cmd/precise-code-intel/src/shared/store/dependencies.test.ts b/cmd/precise-code-intel/src/shared/store/dependencies.test.ts deleted file mode 100644 index 4bbf65a0a1e..00000000000 --- a/cmd/precise-code-intel/src/shared/store/dependencies.test.ts +++ /dev/null @@ -1,236 +0,0 @@ -import * as util from '../test-util' -import * as pgModels from '../models/pg' -import { Connection } from 'typeorm' -import { fail } from 'assert' -import { DumpManager } from './dumps' -import { DependencyManager } from './dependencies' - -describe('DependencyManager', () => { - let connection!: Connection - let cleanup!: () => Promise - let dumpManager!: DumpManager - let dependencyManager!: DependencyManager - - const repositoryId1 = 100 - const repositoryId2 = 101 - - beforeAll(async () => { - ;({ connection, cleanup } = await util.createCleanPostgresDatabase()) - dumpManager = new DumpManager(connection) - dependencyManager = new DependencyManager(connection) - }) - - afterAll(async () => { - if (cleanup) { - await cleanup() - } - }) - - beforeEach(async () => { - if (connection) { - await util.truncatePostgresTables(connection) - } - }) - - it('should respect bloom filter', async () => { - if (!dependencyManager) { - fail('failed beforeAll') - } - - const ca = util.createCommit() - const cb = util.createCommit() - const cc = util.createCommit() - const cd = util.createCommit() - const ce = util.createCommit() - const cf = util.createCommit() - - const updatePackages = async ( - commit: string, - root: string, - identifiers: string[] - ): Promise => { - const dump = await util.insertDump(connection, dumpManager, repositoryId1, commit, root, 'test') - - await dependencyManager.addPackagesAndReferences( - dump.id, - [], - [ - { - package: { - scheme: 'npm', - name: 'p1', - version: '0.1.0', - }, - identifiers, - }, - ] - ) - - return dump - } - - // Note: roots must be unique so dumps are visible - const dumpa = await updatePackages(ca, 'r1', ['x', 'y', 'z']) - const dumpb = await updatePackages(cb, 'r2', ['y', 'z']) - const dumpf = await updatePackages(cf, 'r3', ['y', 'z']) - await updatePackages(cc, 'r4', ['x', 'z']) - await updatePackages(cd, 'r5', ['x']) - await updatePackages(ce, 'r6', ['x', 'z']) - - const getReferencedDumpIds = async () => { - const { packageReferences } = await dependencyManager.getPackageReferences({ - repositoryId: repositoryId2, - scheme: 'npm', - name: 'p1', - version: '0.1.0', - identifier: 'y', - limit: 50, - offset: 0, - }) - - return packageReferences.map(packageReference => packageReference.dump_id).sort() - } - - await dumpManager.updateCommits( - repositoryId1, - new Map>([ - [ca, new Set()], - [cb, new Set([ca])], - [cc, new Set([cb])], - [cd, new Set([cc])], - [ce, new Set([cd])], - [cf, new Set([ce])], - ]) - ) - await dumpManager.updateDumpsVisibleFromTip(repositoryId1, cf) - - // only references containing identifier y - expect(await getReferencedDumpIds()).toEqual([dumpa.id, dumpb.id, dumpf.id]) - }) - - it('should re-query if bloom filter prunes too many results', async () => { - if (!dependencyManager) { - fail('failed beforeAll') - } - - const updatePackages = async ( - commit: string, - root: string, - identifiers: string[] - ): Promise => { - const dump = await util.insertDump(connection, dumpManager, repositoryId1, commit, root, 'test') - - await dependencyManager.addPackagesAndReferences( - dump.id, - [], - [ - { - package: { - scheme: 'npm', - name: 'p1', - version: '0.1.0', - }, - identifiers, - }, - ] - ) - - return dump - } - - const dumps = [] - for (let i = 0; i < 250; i++) { - // Spread out uses of `y` so that we pull back a series of pages that are - // empty and half-empty after being filtered by the bloom filter. We will - // have to empty pages (i < 100) followed by three pages where very third - // uses the identifier. In all, there are fifty uses spread over 5 pages. - const isUse = i >= 100 && i % 3 === 0 - - const dump = await updatePackages(util.createCommit(), `r${i}`, ['x', isUse ? 'y' : 'z']) - dump.visibleAtTip = true - await connection.getRepository(pgModels.LsifUpload).save(dump) - - if (isUse) { - // Save use ids - dumps.push(dump.id) - } - } - - const { packageReferences } = await dependencyManager.getPackageReferences({ - repositoryId: repositoryId2, - scheme: 'npm', - name: 'p1', - version: '0.1.0', - identifier: 'y', - limit: 50, - offset: 0, - }) - - expect(packageReferences.map(packageReference => packageReference.dump_id).sort()).toEqual(dumps) - }) - - it('references only returned if dumps visible at tip', async () => { - if (!dependencyManager) { - fail('failed beforeAll') - } - - const ca = util.createCommit() - const cb = util.createCommit() - const cc = util.createCommit() - - const references = [ - { - package: { - scheme: 'npm', - name: 'p1', - version: '0.1.0', - }, - identifiers: ['x', 'y', 'z'], - }, - ] - - const dumpa = await util.insertDump(connection, dumpManager, repositoryId1, ca, '', 'test') - const dumpb = await util.insertDump(connection, dumpManager, repositoryId1, cb, '', 'test') - const dumpc = await util.insertDump(connection, dumpManager, repositoryId1, cc, '', 'test') - - await dependencyManager.addPackagesAndReferences(dumpa.id, [], references) - await dependencyManager.addPackagesAndReferences(dumpb.id, [], references) - await dependencyManager.addPackagesAndReferences(dumpc.id, [], references) - - const getReferencedDumpIds = async () => - ( - await dependencyManager.getPackageReferences({ - repositoryId: repositoryId2, - scheme: 'npm', - name: 'p1', - version: '0.1.0', - identifier: 'y', - limit: 50, - offset: 0, - }) - ).packageReferences - .map(packageReference => packageReference.dump_id) - .sort() - - const updateVisibility = async (visibleA: boolean, visibleB: boolean, visibleC: boolean) => { - dumpa.visibleAtTip = visibleA - dumpb.visibleAtTip = visibleB - dumpc.visibleAtTip = visibleC - await connection.getRepository(pgModels.LsifUpload).save(dumpa) - await connection.getRepository(pgModels.LsifUpload).save(dumpb) - await connection.getRepository(pgModels.LsifUpload).save(dumpc) - } - - // Set a, b visible from tip - await updateVisibility(true, true, false) - expect(await getReferencedDumpIds()).toEqual([dumpa.id, dumpb.id]) - - // Clear a, b visible from tip, set c visible fro - await updateVisibility(false, false, true) - expect(await getReferencedDumpIds()).toEqual([dumpc.id]) - - // Clear all visible from tip - await updateVisibility(false, false, false) - expect(await getReferencedDumpIds()).toEqual([]) - }) -}) diff --git a/cmd/precise-code-intel/src/shared/store/dumps.test.ts b/cmd/precise-code-intel/src/shared/store/dumps.test.ts deleted file mode 100644 index 43f8730f85c..00000000000 --- a/cmd/precise-code-intel/src/shared/store/dumps.test.ts +++ /dev/null @@ -1,766 +0,0 @@ -import * as util from '../test-util' -import * as pgModels from '../models/pg' -import nock from 'nock' -import { Connection } from 'typeorm' -import { DumpManager } from './dumps' -import { fail } from 'assert' -import { MAX_TRAVERSAL_LIMIT } from '../constants' - -describe('DumpManager', () => { - let connection!: Connection - let cleanup!: () => Promise - let dumpManager!: DumpManager - - let counter = 100 - const nextId = () => { - counter++ - return counter - } - - beforeAll(async () => { - ;({ connection, cleanup } = await util.createCleanPostgresDatabase()) - dumpManager = new DumpManager(connection) - }) - - afterAll(async () => { - if (cleanup) { - await cleanup() - } - }) - - beforeEach(async () => { - if (connection) { - await util.truncatePostgresTables(connection) - } - }) - - it('should find closest commits with LSIF data (first commit graph)', async () => { - if (!dumpManager) { - fail('failed beforeAll') - } - - // This database has the following commit graph: - // - // [a] --+--- b --------+--e -- f --+-- [g] - // | | | - // +-- [c] -- d --+ +--- h - - const repositoryId = nextId() - const ca = util.createCommit() - const cb = util.createCommit() - const cc = util.createCommit() - const cd = util.createCommit() - const ce = util.createCommit() - const cf = util.createCommit() - const cg = util.createCommit() - const ch = util.createCommit() - - // Add relations - await dumpManager.updateCommits( - repositoryId, - new Map>([ - [ca, new Set()], - [cb, new Set([ca])], - [cc, new Set([ca])], - [cd, new Set([cc])], - [ce, new Set([cb])], - [ce, new Set([cd])], - [cf, new Set([ce])], - [cg, new Set([cf])], - [ch, new Set([cf])], - ]) - ) - - // Add dumps - await util.insertDump(connection, dumpManager, repositoryId, ca, '', 'test') - await util.insertDump(connection, dumpManager, repositoryId, cc, '', 'test') - await util.insertDump(connection, dumpManager, repositoryId, cg, '', 'test') - - const d1 = await dumpManager.findClosestDumps(repositoryId, ca, 'file.ts') - const d2 = await dumpManager.findClosestDumps(repositoryId, cb, 'file.ts') - const d3 = await dumpManager.findClosestDumps(repositoryId, cc, 'file.ts') - const d4 = await dumpManager.findClosestDumps(repositoryId, cd, 'file.ts') - const d5 = await dumpManager.findClosestDumps(repositoryId, cf, 'file.ts') - const d6 = await dumpManager.findClosestDumps(repositoryId, cg, 'file.ts') - const d7 = await dumpManager.findClosestDumps(repositoryId, ce, 'file.ts') - const d8 = await dumpManager.findClosestDumps(repositoryId, ch, 'file.ts') - - expect(d1).toHaveLength(1) - expect(d2).toHaveLength(1) - expect(d3).toHaveLength(1) - expect(d4).toHaveLength(1) - expect(d5).toHaveLength(1) - expect(d6).toHaveLength(1) - expect(d7).toHaveLength(1) - expect(d8).toHaveLength(1) - - // Test closest commit - expect(d1[0].commit).toEqual(ca) - expect(d2[0].commit).toEqual(ca) - expect(d3[0].commit).toEqual(cc) - expect(d4[0].commit).toEqual(cc) - expect(d5[0].commit).toEqual(cg) - expect(d6[0].commit).toEqual(cg) - - // Multiple nearest are chosen arbitrarily - expect([ca, cc, cg]).toContain(d7[0].commit) - expect([ca, cc]).toContain(d8[0].commit) - }) - - it('should find closest commits with LSIF data (second commit graph)', async () => { - if (!dumpManager) { - fail('failed beforeAll') - } - - // This database has the following commit graph: - // - // a --+-- [b] ---- c - // | - // +--- d --+-- e -- f - // | - // +-- g -- h - - const repositoryId = nextId() - const ca = util.createCommit() - const cb = util.createCommit() - const cc = util.createCommit() - const cd = util.createCommit() - const ce = util.createCommit() - const cf = util.createCommit() - const cg = util.createCommit() - const ch = util.createCommit() - - // Add relations - await dumpManager.updateCommits( - repositoryId, - new Map>([ - [ca, new Set()], - [cb, new Set([ca])], - [cc, new Set([cb])], - [cd, new Set([ca])], - [ce, new Set([cd])], - [cf, new Set([ce])], - [cg, new Set([cd])], - [ch, new Set([cg])], - ]) - ) - - // Add dumps - await util.insertDump(connection, dumpManager, repositoryId, cb, '', 'test') - - const d1 = await dumpManager.findClosestDumps(repositoryId, ca, 'file.ts') - const d2 = await dumpManager.findClosestDumps(repositoryId, cb, 'file.ts') - const d3 = await dumpManager.findClosestDumps(repositoryId, cc, 'file.ts') - - expect(d1).toHaveLength(1) - expect(d2).toHaveLength(1) - expect(d3).toHaveLength(1) - - // Test closest commit - expect(d1[0].commit).toEqual(cb) - expect(d2[0].commit).toEqual(cb) - expect(d3[0].commit).toEqual(cb) - expect(await dumpManager.findClosestDumps(repositoryId, cd, 'file.ts')).toHaveLength(0) - expect(await dumpManager.findClosestDumps(repositoryId, ce, 'file.ts')).toHaveLength(0) - expect(await dumpManager.findClosestDumps(repositoryId, cf, 'file.ts')).toHaveLength(0) - expect(await dumpManager.findClosestDumps(repositoryId, cg, 'file.ts')).toHaveLength(0) - expect(await dumpManager.findClosestDumps(repositoryId, ch, 'file.ts')).toHaveLength(0) - }) - - it('should find closest commits with LSIF data (distinct roots)', async () => { - if (!dumpManager) { - fail('failed beforeAll') - } - - // This database has the following commit graph: - // - // a --+-- [b] - // - // Where LSIF dumps exist at b at roots: root1/ and root2/. - - const repositoryId = nextId() - const ca = util.createCommit() - const cb = util.createCommit() - - // Add relations - await dumpManager.updateCommits( - repositoryId, - new Map>([ - [ca, new Set()], - [cb, new Set([ca])], - ]) - ) - - // Add dumps - await util.insertDump(connection, dumpManager, repositoryId, cb, 'root1/', '') - await util.insertDump(connection, dumpManager, repositoryId, cb, 'root2/', '') - - // Test closest commit - const d1 = await dumpManager.findClosestDumps(repositoryId, ca, 'blah') - const d2 = await dumpManager.findClosestDumps(repositoryId, cb, 'root1/file.ts') - const d3 = await dumpManager.findClosestDumps(repositoryId, cb, 'root2/file.ts') - const d4 = await dumpManager.findClosestDumps(repositoryId, ca, 'root2/file.ts') - - expect(d1).toHaveLength(0) - expect(d2).toHaveLength(1) - expect(d3).toHaveLength(1) - expect(d4).toHaveLength(1) - - expect(d2[0].commit).toEqual(cb) - expect(d2[0].root).toEqual('root1/') - expect(d3[0].commit).toEqual(cb) - expect(d3[0].root).toEqual('root2/') - expect(d4[0].commit).toEqual(cb) - expect(d4[0].root).toEqual('root2/') - - const d5 = await dumpManager.findClosestDumps(repositoryId, ca, 'root3/file.ts') - expect(d5).toHaveLength(0) - - await util.insertDump(connection, dumpManager, repositoryId, cb, '', '') - const d6 = await dumpManager.findClosestDumps(repositoryId, ca, 'root2/file.ts') - const d7 = await dumpManager.findClosestDumps(repositoryId, ca, 'root3/file.ts') - - expect(d6).toHaveLength(1) - expect(d7).toHaveLength(1) - - expect(d6[0].commit).toEqual(cb) - expect(d6[0].root).toEqual('') - expect(d7[0].commit).toEqual(cb) - expect(d7[0].root).toEqual('') - }) - - it('should find closest commits with LSIF data (overlapping roots)', async () => { - if (!dumpManager) { - fail('failed beforeAll') - } - - // This database has the following commit graph: - // - // a -- b --+-- c --+-- e -- f - // | | - // +-- d --+ - // - // With the following LSIF dumps: - // - // | Commit | Root | Indexer | - // | ------ + ------- + ------- | - // | a | root3/ | A | - // | a | root4/ | B | - // | b | root1/ | A | - // | b | root2/ | A | - // | b | | B | (overwrites root4/ at commit a) - // | c | root1/ | A | (overwrites root1/ at commit b) - // | d | | B | (overwrites (root) at commit b) - // | e | root2/ | A | (overwrites root2/ at commit b) - // | f | root1/ | A | (overwrites root1/ at commit b) - - const repositoryId = nextId() - const ca = util.createCommit() - const cb = util.createCommit() - const cc = util.createCommit() - const cd = util.createCommit() - const ce = util.createCommit() - const cf = util.createCommit() - - // Add relations - await dumpManager.updateCommits( - repositoryId, - new Map>([ - [ca, new Set()], - [cb, new Set([ca])], - [cc, new Set([cb])], - [cd, new Set([cb])], - [ce, new Set([cc, cd])], - [cf, new Set([ce])], - ]) - ) - - // Add dumps - await util.insertDump(connection, dumpManager, repositoryId, ca, 'root3/', 'A') - await util.insertDump(connection, dumpManager, repositoryId, ca, 'root4/', 'B') - await util.insertDump(connection, dumpManager, repositoryId, cb, 'root1/', 'A') - await util.insertDump(connection, dumpManager, repositoryId, cb, 'root2/', 'A') - await util.insertDump(connection, dumpManager, repositoryId, cb, '', 'B') - await util.insertDump(connection, dumpManager, repositoryId, cc, 'root1/', 'A') - await util.insertDump(connection, dumpManager, repositoryId, cd, '', 'B') - await util.insertDump(connection, dumpManager, repositoryId, ce, 'root2/', 'A') - await util.insertDump(connection, dumpManager, repositoryId, cf, 'root1/', 'A') - - // Test closest commit - const d1 = await dumpManager.findClosestDumps(repositoryId, cd, 'root1/file.ts') - expect(d1).toHaveLength(2) - expect(d1[0].commit).toEqual(cd) - expect(d1[0].root).toEqual('') - expect(d1[0].indexer).toEqual('B') - expect(d1[1].commit).toEqual(cb) - expect(d1[1].root).toEqual('root1/') - expect(d1[1].indexer).toEqual('A') - - const d2 = await dumpManager.findClosestDumps(repositoryId, ce, 'root2/file.ts') - expect(d2).toHaveLength(2) - expect(d2[0].commit).toEqual(ce) - expect(d2[0].root).toEqual('root2/') - expect(d2[0].indexer).toEqual('A') - expect(d2[1].commit).toEqual(cd) - expect(d2[1].root).toEqual('') - expect(d2[1].indexer).toEqual('B') - - const d3 = await dumpManager.findClosestDumps(repositoryId, cc, 'root3/file.ts') - expect(d3).toHaveLength(2) - expect(d3[0].commit).toEqual(cb) - expect(d3[0].root).toEqual('') - expect(d3[0].indexer).toEqual('B') - expect(d3[1].commit).toEqual(ca) - expect(d3[1].root).toEqual('root3/') - expect(d3[1].indexer).toEqual('A') - - const d4 = await dumpManager.findClosestDumps(repositoryId, ca, 'root4/file.ts') - expect(d4).toHaveLength(1) - expect(d4[0].commit).toEqual(ca) - expect(d4[0].root).toEqual('root4/') - expect(d4[0].indexer).toEqual('B') - - const d5 = await dumpManager.findClosestDumps(repositoryId, cb, 'root4/file.ts') - expect(d5).toHaveLength(1) - expect(d5[0].commit).toEqual(cb) - expect(d5[0].root).toEqual('') - expect(d5[0].indexer).toEqual('B') - }) - - it('should not return elements farther than MAX_TRAVERSAL_LIMIT', async () => { - if (!dumpManager) { - fail('failed beforeAll') - } - - // This repository has the following commit graph (ancestors to the left): - // - // MAX_TRAVERSAL_LIMIT -- ... -- 2 -- 1 -- 0 - // - // Note: we use 'a' as a suffix for commit numbers on construction so that - // we can distinguish `1` and `11` (`1a1a1a...` and `11a11a11a..`). - - const repositoryId = nextId() - const c0 = util.createCommit(0) - const c1 = util.createCommit(1) - const cpen = util.createCommit(MAX_TRAVERSAL_LIMIT / 2 - 1) - const cmax = util.createCommit(MAX_TRAVERSAL_LIMIT / 2) - - const commits = new Map>( - Array.from({ length: MAX_TRAVERSAL_LIMIT }, (_, i) => [ - util.createCommit(i), - new Set([util.createCommit(i + 1)]), - ]) - ) - - // Add relations - await dumpManager.updateCommits(repositoryId, commits) - - // Add dumps - await util.insertDump(connection, dumpManager, repositoryId, c0, '', 'test') - - const d1 = await dumpManager.findClosestDumps(repositoryId, c0, 'file.ts') - const d2 = await dumpManager.findClosestDumps(repositoryId, c1, 'file.ts') - const d3 = await dumpManager.findClosestDumps(repositoryId, cpen, 'file.ts') - - expect(d1).toHaveLength(1) - expect(d2).toHaveLength(1) - expect(d3).toHaveLength(1) - - // Test closest commit - expect(d1[0].commit).toEqual(c0) - expect(d2[0].commit).toEqual(c0) - expect(d3[0].commit).toEqual(c0) - - // (Assuming MAX_TRAVERSAL_LIMIT = 100) - // At commit `50`, the traversal limit will be reached before visiting commit `0` - // because commits are visited in this order: - // - // | depth | commit | - // | ----- | ------ | - // | 1 | 50 | (with direction 'A') - // | 2 | 50 | (with direction 'D') - // | 3 | 51 | - // | 4 | 49 | - // | 5 | 52 | - // | 6 | 48 | - // | ... | | - // | 99 | 99 | - // | 100 | 1 | (limit reached) - - expect(await dumpManager.findClosestDumps(repositoryId, cmax, 'file.ts')).toHaveLength(0) - - // Add closer dump - await util.insertDump(connection, dumpManager, repositoryId, c1, '', 'test') - - // Now commit 1 should be found - const dumps = await dumpManager.findClosestDumps(repositoryId, cmax, 'file.ts') - expect(dumps).toHaveLength(1) - expect(dumps[0].commit).toEqual(c1) - }) - - it('should prune overlapping roots during visibility check', async () => { - if (!dumpManager) { - fail('failed beforeAll') - } - - // This database has the following commit graph: - // - // a -- b -- c -- d -- e -- f -- g - - const repositoryId = nextId() - const ca = util.createCommit() - const cb = util.createCommit() - const cc = util.createCommit() - const cd = util.createCommit() - const ce = util.createCommit() - const cf = util.createCommit() - const cg = util.createCommit() - - // Add relations - await dumpManager.updateCommits( - repositoryId, - new Map>([ - [ca, new Set()], - [cb, new Set([ca])], - [cc, new Set([cb])], - [cd, new Set([cc])], - [ce, new Set([cd])], - [cf, new Set([ce])], - [cg, new Set([cf])], - ]) - ) - - // Add dumps - await util.insertDump(connection, dumpManager, repositoryId, ca, 'r1', 'test') - await util.insertDump(connection, dumpManager, repositoryId, cb, 'r2', 'test') - await util.insertDump(connection, dumpManager, repositoryId, cc, '', 'test') // overwrites r1, r2 - const d1 = await util.insertDump(connection, dumpManager, repositoryId, cd, 'r3', 'test') // overwrites '' - const d2 = await util.insertDump(connection, dumpManager, repositoryId, cf, 'r4', 'test') - await util.insertDump(connection, dumpManager, repositoryId, cg, 'r5', 'test') // not traversed - - await dumpManager.updateDumpsVisibleFromTip(repositoryId, cf) - const visibleDumps = await dumpManager.getVisibleDumps(repositoryId) - expect(visibleDumps.map((dump: pgModels.LsifDump) => dump.id).sort()).toEqual([d1.id, d2.id]) - }) - - it('should prune overlapping roots of the same indexer during visibility check', async () => { - if (!dumpManager) { - fail('failed beforeAll') - } - // This database has the following commit graph: - // - // a -- b -- c -- d -- e -- f -- g - - const repositoryId = nextId() - const ca = util.createCommit() - const cb = util.createCommit() - const cc = util.createCommit() - const cd = util.createCommit() - const ce = util.createCommit() - const cf = util.createCommit() - const cg = util.createCommit() - - // Add relations - await dumpManager.updateCommits( - repositoryId, - new Map>([ - [ca, new Set()], - [cb, new Set([ca])], - [cc, new Set([cb])], - [cd, new Set([cc])], - [ce, new Set([cd])], - [cf, new Set([ce])], - [cg, new Set([cf])], - ]) - ) - - // Add dumps from indexer A - await util.insertDump(connection, dumpManager, repositoryId, ca, 'r1', 'A') - const d1 = await util.insertDump(connection, dumpManager, repositoryId, cc, 'r2', 'A') - const d2 = await util.insertDump(connection, dumpManager, repositoryId, cd, 'r1', 'A') // overwrites r1 - const d3 = await util.insertDump(connection, dumpManager, repositoryId, cf, 'r3', 'A') - await util.insertDump(connection, dumpManager, repositoryId, cg, 'r4', 'A') // not traversed - - // Add dumps from indexer B - await util.insertDump(connection, dumpManager, repositoryId, ca, 'r1', 'B') - await util.insertDump(connection, dumpManager, repositoryId, cc, 'r2', 'B') - await util.insertDump(connection, dumpManager, repositoryId, cd, '', 'B') // overwrites r1, r2 - const d5 = await util.insertDump(connection, dumpManager, repositoryId, ce, 'r3', 'B') // overwrites '' - - await dumpManager.updateDumpsVisibleFromTip(repositoryId, cf) - const visibleDumps = await dumpManager.getVisibleDumps(repositoryId) - expect(visibleDumps.map((dump: pgModels.LsifDump) => dump.id).sort()).toEqual([d1.id, d2.id, d3.id, d5.id]) - }) - - it('should traverse branching paths during visibility check', async () => { - if (!dumpManager) { - fail('failed beforeAll') - } - - // This database has the following commit graph: - // - // a --+-- [b] --- c ---+ - // | | - // +--- d --- [e] --+ -- [h] --+-- [i] - // | | - // +-- [f] --- g --------------+ - - const repositoryId = nextId() - const ca = util.createCommit() - const cb = util.createCommit() - const cc = util.createCommit() - const cd = util.createCommit() - const ce = util.createCommit() - const ch = util.createCommit() - const ci = util.createCommit() - const cf = util.createCommit() - const cg = util.createCommit() - - // Add relations - await dumpManager.updateCommits( - repositoryId, - new Map>([ - [ca, new Set()], - [cb, new Set([ca])], - [cc, new Set([cb])], - [cd, new Set([ca])], - [ce, new Set([cd])], - [ch, new Set([cc, ce])], - [ci, new Set([ch, cg])], - [cf, new Set([ca])], - [cg, new Set([cf])], - ]) - ) - - // Add dumps - await util.insertDump(connection, dumpManager, repositoryId, cb, 'r2', 'test') - const dump1 = await util.insertDump(connection, dumpManager, repositoryId, ce, 'r2/a', 'test') // overwrites r2 in commit b - const dump2 = await util.insertDump(connection, dumpManager, repositoryId, ce, 'r2/b', 'test') - await util.insertDump(connection, dumpManager, repositoryId, cf, 'r1/a', 'test') - await util.insertDump(connection, dumpManager, repositoryId, cf, 'r1/b', 'test') - const dump3 = await util.insertDump(connection, dumpManager, repositoryId, ch, 'r1', 'test') // overwrites r1/{a,b} in commit f - const dump4 = await util.insertDump(connection, dumpManager, repositoryId, ci, 'r3', 'test') - - await dumpManager.updateDumpsVisibleFromTip(repositoryId, ci) - const visibleDumps = await dumpManager.getVisibleDumps(repositoryId) - expect(visibleDumps.map((dump: pgModels.LsifDump) => dump.id).sort()).toEqual([ - dump1.id, - dump2.id, - dump3.id, - dump4.id, - ]) - }) - - it('should not set dumps visible farther than MAX_TRAVERSAL_LIMIT', async () => { - if (!dumpManager) { - fail('failed beforeAll') - } - - // This repository has the following commit graph (ancestors to the left): - // - // (MAX_TRAVERSAL_LIMIT + 1) -- ... -- 2 -- 1 -- 0 - // - // Note: we use 'a' as a suffix for commit numbers on construction so that - // we can distinguish `1` and `11` (`1a1a1a...` and `11a11a11a...`). - - const repositoryId = nextId() - const c0 = util.createCommit(0) - const c1 = util.createCommit(1) - const cpen = util.createCommit(MAX_TRAVERSAL_LIMIT - 1) - const cmax = util.createCommit(MAX_TRAVERSAL_LIMIT) - - const commits = new Map>( - Array.from({ length: MAX_TRAVERSAL_LIMIT + 1 }, (_, i) => [ - util.createCommit(i), - new Set([util.createCommit(i + 1)]), - ]) - ) - - // Add relations - await dumpManager.updateCommits(repositoryId, commits) - - // Add dumps - const dump1 = await util.insertDump(connection, dumpManager, repositoryId, cmax, '', 'test') - - await dumpManager.updateDumpsVisibleFromTip(repositoryId, cmax) - let visibleDumps = await dumpManager.getVisibleDumps(repositoryId) - expect(visibleDumps.map((dump: pgModels.LsifDump) => dump.id).sort()).toEqual([dump1.id]) - - await dumpManager.updateDumpsVisibleFromTip(repositoryId, c1) - visibleDumps = await dumpManager.getVisibleDumps(repositoryId) - expect(visibleDumps.map((dump: pgModels.LsifDump) => dump.id).sort()).toEqual([dump1.id]) - - await dumpManager.updateDumpsVisibleFromTip(repositoryId, c0) - visibleDumps = await dumpManager.getVisibleDumps(repositoryId) - expect(visibleDumps.map((dump: pgModels.LsifDump) => dump.id).sort()).toEqual([]) - - // Add closer dump - const dump2 = await util.insertDump(connection, dumpManager, repositoryId, cpen, '', 'test') - - // Now commit cpen should be found - await dumpManager.updateDumpsVisibleFromTip(repositoryId, c0) - visibleDumps = await dumpManager.getVisibleDumps(repositoryId) - expect(visibleDumps.map((dump: pgModels.LsifDump) => dump.id).sort()).toEqual([dump2.id]) - }) -}) - -describe('discoverAndUpdateCommit', () => { - let counter = 200 - const nextId = () => { - counter++ - return counter - } - - it('should update tracked commits', async () => { - const repositoryId = nextId() - const ca = util.createCommit() - const cb = util.createCommit() - const cc = util.createCommit() - - nock('http://frontend') - .post(`/.internal/git/${repositoryId}/exec`) - .reply(200, `${ca}\n${cb} ${ca}\n${cc} ${cb}`) - - const { connection, cleanup } = await util.createCleanPostgresDatabase() - - try { - const dumpManager = new DumpManager(connection) - await util.insertDump(connection, dumpManager, repositoryId, ca, '', 'test') - - await dumpManager.updateCommits( - repositoryId, - await dumpManager.discoverCommits({ - repositoryId, - commit: cc, - frontendUrl: 'frontend', - }) - ) - - // Ensure all commits are now tracked - expect((await connection.getRepository(pgModels.Commit).find()).map(c => c.commit).sort()).toEqual([ - ca, - cb, - cc, - ]) - } finally { - await cleanup() - } - }) - - it('should early-out if commit is tracked', async () => { - const repositoryId = nextId() - const ca = util.createCommit() - const cb = util.createCommit() - - const { connection, cleanup } = await util.createCleanPostgresDatabase() - - try { - const dumpManager = new DumpManager(connection) - await util.insertDump(connection, dumpManager, repositoryId, ca, '', 'test') - await dumpManager.updateCommits( - repositoryId, - new Map>([[cb, new Set()]]) - ) - - // This test ensures the following does not make a gitserver request. - // As we did not register a nock interceptor, any request will result - // in an exception being thrown. - - await dumpManager.updateCommits( - repositoryId, - await dumpManager.discoverCommits({ - repositoryId, - commit: cb, - frontendUrl: 'frontend', - }) - ) - } finally { - await cleanup() - } - }) - - it('should early-out if repository is unknown', async () => { - const repositoryId = nextId() - const ca = util.createCommit() - - const { connection, cleanup } = await util.createCleanPostgresDatabase() - - try { - const dumpManager = new DumpManager(connection) - - // This test ensures the following does not make a gitserver request. - // As we did not register a nock interceptor, any request will result - // in an exception being thrown. - - await dumpManager.updateCommits( - repositoryId, - await dumpManager.discoverCommits({ - repositoryId, - commit: ca, - frontendUrl: 'frontend', - }) - ) - } finally { - await cleanup() - } - }) -}) - -describe('discoverAndUpdateTips', () => { - let counter = 300 - const nextId = () => { - counter++ - return counter - } - - it('should update tips', async () => { - const repositoryId = nextId() - const ca = util.createCommit() - const cb = util.createCommit() - const cc = util.createCommit() - const cd = util.createCommit() - const ce = util.createCommit() - - nock('http://frontend') - .post(`/.internal/git/${repositoryId}/exec`, { args: ['rev-parse', 'HEAD'] }) - .reply(200, ce) - - const { connection, cleanup } = await util.createCleanPostgresDatabase() - - try { - const dumpManager = new DumpManager(connection) - await dumpManager.updateCommits( - repositoryId, - new Map>([ - [ca, new Set()], - [cb, new Set([ca])], - [cc, new Set([cb])], - [cd, new Set([cc])], - [ce, new Set([cd])], - ]) - ) - await util.insertDump(connection, dumpManager, repositoryId, ca, 'foo', 'test') - await util.insertDump(connection, dumpManager, repositoryId, cb, 'foo', 'test') - await util.insertDump(connection, dumpManager, repositoryId, cc, 'bar', 'test') - - const tipCommit = await dumpManager.discoverTip({ - repositoryId, - frontendUrl: 'frontend', - }) - if (!tipCommit) { - throw new Error('Expected a tip commit') - } - await dumpManager.updateDumpsVisibleFromTip(repositoryId, tipCommit) - - const d1 = await dumpManager.getDump(repositoryId, ca, 'foo/test.ts') - const d2 = await dumpManager.getDump(repositoryId, cb, 'foo/test.ts') - const d3 = await dumpManager.getDump(repositoryId, cc, 'bar/test.ts') - - expect(d1?.visibleAtTip).toBeFalsy() - expect(d2?.visibleAtTip).toBeTruthy() - expect(d3?.visibleAtTip).toBeTruthy() - } finally { - await cleanup() - } - }) -}) diff --git a/cmd/precise-code-intel/src/shared/store/dumps.ts b/cmd/precise-code-intel/src/shared/store/dumps.ts deleted file mode 100644 index 96ef6154a80..00000000000 --- a/cmd/precise-code-intel/src/shared/store/dumps.ts +++ /dev/null @@ -1,368 +0,0 @@ -import { uniq } from 'lodash' -import * as sharedMetrics from '../database/metrics' -import * as pgModels from '../models/pg' -import { getCommitsNear, getHead } from '../gitserver/gitserver' -import { Brackets, Connection, EntityManager } from 'typeorm' -import { logAndTraceCall, TracingContext } from '../tracing' -import { instrumentQuery, instrumentQueryOrTransaction, withInstrumentedTransaction } from '../database/postgres' -import { TableInserter } from '../database/inserter' -import { visibleDumps, ancestorLineage, bidirectionalLineage } from '../models/queries' -import { isDefined } from '../util' - -/** The insertion metrics for Postgres. */ -const insertionMetrics = { - durationHistogram: sharedMetrics.postgresInsertionDurationHistogram, - errorsCounter: sharedMetrics.postgresQueryErrorsCounter, -} - -/** A wrapper around the database tables that control dumps and commits. */ -export class DumpManager { - /** - * Create a new `DumpManager` backed by the given database connection. - * - * @param connection The Postgres connection. - */ - constructor(private connection: Connection) {} - - /** - * Find the dump for the given repository and commit. - * - * @param repositoryId The repository identifier. - * @param commit The commit. - * @param file A filename that should be included in the dump. - */ - public getDump(repositoryId: number, commit: string, file: string): Promise { - return instrumentQuery(() => - this.connection - .getRepository(pgModels.LsifDump) - .createQueryBuilder() - .select() - .where({ repositoryId, commit }) - .andWhere(":file LIKE (root || '%')", { file }) - .getOne() - ) - } - - /** - * Get a dump by identifier. - * - * @param id The dump identifier. - */ - public getDumpById(id: pgModels.DumpId): Promise { - return instrumentQuery(() => this.connection.getRepository(pgModels.LsifDump).findOne({ id })) - } - - /** - * Bulk get dumps by identifier. - * - * @param ids The dump identifiers. - */ - public async getDumpsByIds(ids: pgModels.DumpId[]): Promise> { - if (ids.length === 0) { - return new Map() - } - - const dumps = await instrumentQuery(() => - this.connection.getRepository(pgModels.LsifDump).createQueryBuilder().select().whereInIds(ids).getMany() - ) - - return new Map(dumps.map(d => [d.id, d])) - } - - /** - * Return a map from upload ids to their state. - * - * @param ids The upload ids to fetch. - */ - public async getUploadStates(ids: pgModels.DumpId[]): Promise> { - if (ids.length === 0) { - return new Map() - } - - const result: { id: pgModels.DumpId; state: pgModels.LsifUploadState }[] = await instrumentQuery(() => - this.connection - .getRepository(pgModels.LsifUpload) - .createQueryBuilder() - .select(['id', 'state']) - .where('id IN (:...ids)', { ids }) - .getRawMany() - ) - - return new Map(result.map(u => [u.id, u.state])) - } - - /** - * Find the visible dumps. This method is used for testing. - * - * @param repositoryId The repository identifier. - */ - public getVisibleDumps(repositoryId: number): Promise { - return instrumentQuery(() => - this.connection - .getRepository(pgModels.LsifDump) - .createQueryBuilder() - .select() - .where({ repositoryId, visibleAtTip: true }) - .getMany() - ) - } - - /** - * Get the oldest dump that is not visible at the tip of its repository. - * - * @param entityManager The EntityManager to use as part of a transaction. - */ - public getOldestPrunableDump( - entityManager: EntityManager = this.connection.createEntityManager() - ): Promise { - return instrumentQuery(() => - entityManager - .getRepository(pgModels.LsifDump) - .createQueryBuilder() - .select() - .where({ visibleAtTip: false }) - .orderBy('uploaded_at') - .getOne() - ) - } - - /** - * Return the dump 'closest' to the given target commit (a direct descendant or ancestor of - * the target commit). If no closest commit can be determined, this method returns undefined. - * - * This method returns dumps ordered by commit distance (nearest first). - * - * @param repositoryId The repository identifier. - * @param commit The target commit. - * @param file One of the files in the dump. - * @param ctx The tracing context. - * @param frontendUrl The url of the frontend internal API. - */ - public async findClosestDumps( - repositoryId: number, - commit: string, - file: string, - ctx: TracingContext = {}, - frontendUrl?: string - ): Promise { - // Request updated commit data from gitserver if this commit isn't already - // tracked. This will pull back ancestors for this commit up to a certain - // (configurable) depth and insert them into the database. This populates - // the necessary data for the following query. - if (frontendUrl) { - await this.updateCommits( - repositoryId, - await this.discoverCommits({ repositoryId, commit, frontendUrl, ctx }), - ctx - ) - } - - return logAndTraceCall(ctx, 'Finding closest dump', async () => { - const query = ` - WITH - ${bidirectionalLineage()}, - ${visibleDumps()} - - SELECT d.dump_id FROM lineage_with_dumps d - WHERE $3 LIKE (d.root || '%') AND d.dump_id IN (SELECT * FROM visible_ids) - ORDER BY d.n - ` - - return withInstrumentedTransaction(this.connection, async entityManager => { - const results: { dump_id: number }[] = await entityManager.query(query, [repositoryId, commit, file]) - const dumpIds = results.map(({ dump_id }) => dump_id) - if (dumpIds.length === 0) { - return [] - } - - const uniqueDumpIds = uniq(dumpIds) - - const dumps = await entityManager - .getRepository(pgModels.LsifDump) - .createQueryBuilder() - .select() - .where('id IN (:...ids)', { ids: uniqueDumpIds }) - .getMany() - - const dumpByID = new Map(dumps.map(dump => [dump.id, dump])) - return uniqueDumpIds.map(id => dumpByID.get(id)).filter(isDefined) - }) - }) - } - - /** - * Determine the set of dumps which are 'visible' from the given commit and set the - * `visible_at_tip` flags. Unset the flag for each invisible dump for this repository. - * This will traverse all ancestor commits but not descendants, as the given commit - * is assumed to be the tip of the default branch. For each dump that is filtered out - * of the set of results, there must be a dump with a smaller depth from the given - * commit that has a root that overlaps with the filtered dump. The other such dump - * is necessarily a dump associated with a closer commit for the same root. - * - * @param repositoryId The repository identifier. - * @param commit The head of the default branch. - * @param ctx The tracing context. - * @param entityManager The EntityManager to use as part of a transaction. - */ - public updateDumpsVisibleFromTip( - repositoryId: number, - commit: string, - ctx: TracingContext = {}, - entityManager: EntityManager = this.connection.createEntityManager() - ): Promise { - const query = ` - WITH - ${ancestorLineage()}, - ${visibleDumps()} - - -- Update dump records by: - -- (1) unsetting the visibility flag of all previously visible dumps, and - -- (2) setting the visibility flag of all currently visible dumps - UPDATE lsif_dumps d - SET visible_at_tip = id IN (SELECT * from visible_ids) - WHERE d.repository_id = $1 AND (d.id IN (SELECT * from visible_ids) OR d.visible_at_tip) - ` - - return logAndTraceCall(ctx, 'Updating dumps visible from tip', () => - instrumentQuery(() => entityManager.query(query, [repositoryId, commit])) - ) - } - - /** - * Update the known commits for a repository. The input commits must be a map from commits to - * a set of parent commits. Commits without a parent should have an empty set of parents, but - * should still be present in the map. - * - * @param repositoryId The repository identifier. - * @param commits The commit parentage data. - * @param ctx The tracing context. - * @param entityManager The EntityManager to use as part of a transaction. - */ - public updateCommits( - repositoryId: number, - commits: Map>, - ctx: TracingContext = {}, - entityManager?: EntityManager - ): Promise { - return logAndTraceCall(ctx, 'Updating commits', () => - instrumentQueryOrTransaction(this.connection, entityManager, async definiteEntityManager => { - const commitInserter = new TableInserter( - definiteEntityManager, - pgModels.Commit, - pgModels.Commit.BatchSize, - insertionMetrics, - true // Do nothing on conflict - ) - - for (const [commit, parentCommits] of commits) { - if (parentCommits.size === 0) { - await commitInserter.insert({ repositoryId, commit, parentCommit: null }) - } - - for (const parentCommit of parentCommits) { - await commitInserter.insert({ repositoryId, commit, parentCommit }) - } - } - - await commitInserter.flush() - }) - ) - } - - /** - * Get a list of commits for the given repository with their parent starting at the - * given commit and returning at most `MAX_COMMITS_PER_UPDATE` commits. The output - * is a map from commits to a set of parent commits. The set of parents may be empty. - * If we already have commit parentage information for this commit, this function - * will do nothing. - * - * @param args Parameter bag. - */ - public async discoverCommits({ - repositoryId, - commit, - frontendUrl, - ctx = {}, - }: { - /** The repository identifier. */ - repositoryId: number - /** The commit from which the gitserver queries should start. */ - commit: string - /** The url of the frontend internal API. */ - frontendUrl: string - /** The tracing context. */ - ctx?: TracingContext - }): Promise>> { - const matchingRepos = await instrumentQuery(() => - this.connection.getRepository(pgModels.LsifUpload).count({ where: { repositoryId } }) - ) - if (matchingRepos === 0) { - return new Map() - } - - const matchingCommits = await instrumentQuery(() => - this.connection.getRepository(pgModels.Commit).count({ where: { repositoryId, commit } }) - ) - if (matchingCommits > 0) { - return new Map() - } - - return getCommitsNear(frontendUrl, repositoryId, commit, ctx) - } - - /** - * Query gitserver for the head of the default branch for the given repository. - * - * @param args Parameter bag. - */ - public discoverTip({ - repositoryId, - frontendUrl, - ctx = {}, - }: { - /** The repository identifier. */ - repositoryId: number - /** The url of the frontend internal API. */ - frontendUrl: string - /** The tracing context. */ - ctx?: TracingContext - }): Promise { - return logAndTraceCall(ctx, 'Getting repository metadata', () => getHead(frontendUrl, repositoryId, ctx)) - } - - /** - * Delete existing dumps from the same repo@commit and indexer that overlap with the - * current root (where the existing root is a prefix of the current root, or vice versa). - * - * @param repositoryId The repository identifier. - * @param commit The commit. - * @param root The root of all files that are in the dump. - * @param indexer The indexer used to produce the dump. - * @param ctx The tracing context. - * @param entityManager The EntityManager to use as part of a transaction. - */ - public async deleteOverlappingDumps( - repositoryId: number, - commit: string, - root: string, - indexer: string | undefined, - ctx: TracingContext = {}, - entityManager: EntityManager = this.connection.createEntityManager() - ): Promise { - return logAndTraceCall(ctx, 'Clearing overlapping dumps', () => - instrumentQuery(async () => { - await entityManager - .getRepository(pgModels.LsifUpload) - .createQueryBuilder() - .delete() - .where({ repositoryId, commit, indexer, state: 'completed' }) - .andWhere( - new Brackets(qb => - qb.where(":root LIKE (root || '%')", { root }).orWhere("root LIKE (:root || '%')", { root }) - ) - ) - .execute() - }) - ) - } -} diff --git a/cmd/precise-code-intel/src/shared/store/locks.ts b/cmd/precise-code-intel/src/shared/store/locks.ts deleted file mode 100644 index c4d019553da..00000000000 --- a/cmd/precise-code-intel/src/shared/store/locks.ts +++ /dev/null @@ -1,71 +0,0 @@ -import * as crc32 from 'crc-32' -import { ADVISORY_LOCK_ID_SALT } from '../constants' -import { Connection } from 'typeorm' - -/** - * Hold a Postgres advisory lock while executing the given function. Note that acquiring - * an advisory lock is an (indefinitely) blocking operation. - * - * For more information, see - * https://www.postgresql.org/docs/9.6/static/explicit-locking.html#ADVISORY-LOCKS - * - * @param connection The Postgres connection. - * @param name The name of the lock. - * @param f The function to execute while holding the lock. - */ -export async function withLock(connection: Connection, name: string, f: () => Promise): Promise { - const lockId = createLockId(name) - await connection.query('SELECT pg_advisory_lock($1)', [lockId]) - try { - return await f() - } finally { - await connection.query('SELECT pg_advisory_unlock($1)', [lockId]) - } -} - -/** - * Hold a Postgres advisory lock while executing the given function. If the lock cannot be - * acquired immediately, the function will return undefined without invoking the function. - * - * For more information, see - * https://www.postgresql.org/docs/9.6/static/explicit-locking.html#ADVISORY-LOCKS - * - * @param connection The Postgres connection. - * @param name The name of the lock. - * @param f The function to execute while holding the lock. - */ -export async function tryWithLock( - connection: Connection, - name: string, - f: () => Promise -): Promise { - const lockId = createLockId(name) - if (await connection.query('SELECT pg_try_advisory_lock($1)', [lockId])) { - try { - return await f() - } finally { - await connection.query('SELECT pg_advisory_unlock($1)', [lockId]) - } - } - - return undefined -} - -/** - * Create an integer identifier that will be unique to this app, but will always be the same for this given - * name within the application. - * - * We base our advisory lock identifier generation technique on golang-migrate. For the original source, see - * https://github.com/golang-migrate/migrate/blob/6c96ef02dfbf9430f7286b58afc15718588f2e13/database/util.go#L12. - * - * Advisory lock ids should be deterministically generated such that a single app will return the same lock id - * for the same name, but distinct apps are unlikely to generate the same id (using the same name or not). To - * accomplish this, we hash the name into an integer, then multiply it by some app-specific salt to reduce the - * collision space with another application. Each app should choose a salt uniformly at random. This - * application's salt is distinct from the golang-migrate salt. - * - * @param name The name of the lock. - */ -function createLockId(name: string): number { - return crc32.str(name) * ADVISORY_LOCK_ID_SALT -} diff --git a/cmd/precise-code-intel/src/shared/store/metrics.ts b/cmd/precise-code-intel/src/shared/store/metrics.ts deleted file mode 100644 index 6011bd2b71d..00000000000 --- a/cmd/precise-code-intel/src/shared/store/metrics.ts +++ /dev/null @@ -1,10 +0,0 @@ -import promClient from 'prom-client' - -// -// Bloom Filter Metrics - -export const bloomFilterEventsCounter = new promClient.Counter({ - name: 'lsif_bloom_filter_events_total', - help: 'The number of bloom filter hits and misses.', - labelNames: ['type'], -}) diff --git a/cmd/precise-code-intel/src/shared/tasks.ts b/cmd/precise-code-intel/src/shared/tasks.ts deleted file mode 100644 index 36e7dc8d4de..00000000000 --- a/cmd/precise-code-intel/src/shared/tasks.ts +++ /dev/null @@ -1,71 +0,0 @@ -import AsyncPolling from 'async-polling' -import { Connection } from 'typeorm' -import { logAndTraceCall, TracingContext } from './tracing' -import { Logger } from 'winston' -import { tryWithLock } from './store/locks' - -interface Task { - intervalMs: number - handler: () => Promise -} - -/** - * A collection of tasks that are invoked periodically, each holding an - * exclusive advisory lock on a Postgres database connection. - */ -export class ExclusivePeriodicTaskRunner { - private tasks: Task[] = [] - - /** - * Create a new task runner. - * - * @param connection The Postgres connection. - * @param logger The logger instance. - */ - constructor(private connection: Connection, private logger: Logger) {} - - /** - * Register a task to be performed while holding an exclusive advisory lock in Postgres. - * - * @param args Parameter bag - */ - public register({ - /** The task name. */ - name, - /** The interval between task invocations. */ - intervalMs, - /** The function to invoke. */ - task, - /** Whether or not to silence logging. */ - silent = false, - }: { - name: string - intervalMs: number - task: ({ connection, ctx }: { connection: Connection; ctx: TracingContext }) => Promise - silent?: boolean - }): void { - const taskArgs = { connection: this.connection, ctx: {} } - - this.tasks.push({ - intervalMs, - handler: () => - tryWithLock(this.connection, name, () => - silent - ? task(taskArgs) - : logAndTraceCall({ logger: this.logger }, name, ctx => task({ ...taskArgs, ctx })) - ), - }) - } - - /** Start running all registered tasks on the specified interval. */ - public run(): void { - for (const { intervalMs, handler } of this.tasks) { - const fn = async (end: () => void): Promise => { - await handler() - end() - } - - AsyncPolling(fn, intervalMs * 1000).run() - } - } -} diff --git a/cmd/precise-code-intel/src/shared/test-util.ts b/cmd/precise-code-intel/src/shared/test-util.ts deleted file mode 100644 index 6de8c8d2594..00000000000 --- a/cmd/precise-code-intel/src/shared/test-util.ts +++ /dev/null @@ -1,170 +0,0 @@ -import * as fs from 'mz/fs' -import * as nodepath from 'path' -import * as uuid from 'uuid' -import * as pgModels from './models/pg' -import { child_process } from 'mz' -import { Connection } from 'typeorm' -import { connectPostgres } from './database/postgres' -import { userInfo } from 'os' -import { DumpManager } from './store/dumps' -import { createSilentLogger } from './logging' - -/** - * Create a new postgres database with a random suffix, apply the frontend - * migrations (via the ./dev/migrate.sh script) and return an open connection. - * This uses the PG* environment variables for host, port, user, and password. - * This also returns a cleanup function that will destroy the database, which - * should be called at the end of the test. - */ -export async function createCleanPostgresDatabase(): Promise<{ connection: Connection; cleanup: () => Promise }> { - // Each test has a random dbname suffix - const suffix = uuid.v4().substring(0, 8) - - // Pull test db config from environment - const host = process.env.PGHOST || 'localhost' - const port = parseInt(process.env.PGPORT || '5432', 10) - const username = process.env.PGUSER || userInfo().username || 'postgres' - const password = process.env.PGPASSWORD || '' - const database = `sourcegraph-test-lsif-${suffix}` - - // Determine the path of the migrate script. This will cover the case where `yarn test` is - // run from within the root or from the precise-code-intel directory. - const migrationsPath = nodepath.join((await fs.exists('migrations')) ? '' : '../..', 'migrations') - - // Ensure environment gets passed to child commands - const env = { - ...process.env, - PGHOST: host, - PGPORT: `${port}`, - PGUSER: username, - PGPASSWORD: password, - PGSSLMODE: 'disable', - PGDATABASE: database, - } - - // Construct postgres connection string using environment above. We disable this - // eslint rule because we want it to use bash interpolation, not typescript string - // templates. - // - // eslint-disable-next-line no-template-curly-in-string - const connectionString = 'postgres://${PGUSER}:${PGPASSWORD}@${PGHOST}:${PGPORT}/${PGDATABASE}?sslmode=disable' - - // Define command text - const createCommand = `createdb ${database}` - const dropCommand = `dropdb --if-exists ${database}` - const migrateCommand = `migrate -database "${connectionString}" -path ${migrationsPath} up` - - // Create cleanup function to run after test. This will close the connection - // created below (if successful), then destroy the database that was created - // for the test. It is necessary to close the database first, otherwise we - // get failures during the after hooks: - // - // dropdb: database removal failed: ERROR: database "sourcegraph-test-lsif-5033c9e8" is being accessed by other users - - let connection: Connection - const cleanup = async (): Promise => { - if (connection) { - await connection.close() - } - - await child_process.exec(dropCommand, { env }).then(() => undefined) - } - - // Try to create database - await child_process.exec(createCommand, { env }) - - try { - // Run migrations then connect to database - await child_process.exec(migrateCommand, { env }) - connection = await connectPostgres( - { host, port, username, password, database, ssl: false }, - suffix, - createSilentLogger() - ) - return { connection, cleanup } - } catch (error) { - // We made a database but can't use it - try to clean up - // before throwing the original error. - - try { - await cleanup() - } catch (_) { - // If a new error occurs, swallow it - } - - // Throw the original error - throw error - } -} - -/** - * Truncate all tables that do not match `schema_migrations`. - * - * @param connection The connection to use. - */ -export async function truncatePostgresTables(connection: Connection): Promise { - const results: { table_name: string }[] = await connection.query( - "SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE' AND table_name != 'schema_migrations'" - ) - - const tableNames = results.map(row => row.table_name).join(', ') - await connection.query(`truncate ${tableNames} restart identity`) -} - -/** - * Insert an upload entity and return the corresponding dump entity. - * - * @param connection The Postgres connection. - * @param dumpManager The dumps manager instance. - * @param repositoryId The repository identifier. - * @param commit The commit. - * @param root The root of all files in the dump. - * @param indexer The type of indexer used to produce this dump. - */ -export async function insertDump( - connection: Connection, - dumpManager: DumpManager, - repositoryId: number, - commit: string, - root: string, - indexer: string -): Promise { - await dumpManager.deleteOverlappingDumps(repositoryId, commit, root, indexer, {}) - - const upload = new pgModels.LsifUpload() - upload.repositoryId = repositoryId - upload.commit = commit - upload.root = root - upload.indexer = indexer - upload.uploadedAt = new Date() - upload.state = 'completed' - upload.numParts = 1 - upload.uploadedParts = [0] - await connection.createEntityManager().save(upload) - - const dump = new pgModels.LsifDump() - dump.id = upload.id - dump.repositoryId = repositoryId - dump.commit = commit - dump.root = root - dump.indexer = indexer - return dump -} - -/** A counter used for unique commit generation. */ -let commitBase = 0 - -/** - * Create a 40-character commit. - * - * @param base A unique numeric base to repeat. - */ -export function createCommit(base?: number): string { - if (base === undefined) { - base = commitBase - commitBase++ - } - - // Add 'a' to differentiate between similar numeric bases such as `1a1a...` and `11a11a...`. - return (base + 'a').repeat(40).substring(0, 40) -} diff --git a/cmd/precise-code-intel/src/shared/tracing.ts b/cmd/precise-code-intel/src/shared/tracing.ts deleted file mode 100644 index 92bc8e4641a..00000000000 --- a/cmd/precise-code-intel/src/shared/tracing.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { createSilentLogger, logCall } from './logging' -import { ERROR } from 'opentracing/lib/ext/tags' -import { initTracerFromEnv } from 'jaeger-client' -import { Logger } from 'winston' -import { Span, Tracer } from 'opentracing' - -/** - * A bag of logging and tracing instances passed around a current - * HTTP request or upload conversion. - */ -export interface TracingContext { - /** The current tagged logger instance. Optional for testing. */ - logger?: Logger - - /** The current opentracing span. Optional for testing. */ - span?: Span -} - -/** - * Add tags to the logger and span. Returns a new context. - * - * @param ctx The tracing context. - * @param tags The tags to add to the logger and span. - */ -export function addTags( - { logger = createSilentLogger(), span = new Span() }: TracingContext, - tags: { [name: string]: unknown } -): TracingContext { - return { logger: logger.child(tags), span: span.addTags(tags) } -} - -/** - * Logs an event to the span of The tracing context, if its defined. - * - * @param ctx The tracing context. - * @param event The name of the event. - * @param pairs The values to log. - */ -export function logSpan( - { span = new Span() }: TracingContext, - event: string, - pairs: { [name: string]: unknown } -): void { - span.log({ event, ...pairs }) -} - -/** - * Create a distributed tracer. - * - * @param serviceName The name of the process. - * @param configuration The current configuration. - */ -export function createTracer( - serviceName: string, - { - useJaeger, - }: { - /** Whether or not to enable Jaeger. */ - useJaeger: boolean - } -): Tracer | undefined { - if (useJaeger) { - const config = { - serviceName, - sampler: { - type: 'const', - param: 1, - }, - } - - return initTracerFromEnv(config, {}) - } - - return undefined -} - -/** - * Trace an operation. - * - * @param name The log message to output. - * @param parent The parent span instance. - * @param f The operation to perform. - */ -export async function traceCall(name: string, parent: Span, f: (span: Span) => Promise | T): Promise { - const span = parent.tracer().startSpan(name, { childOf: parent }) - - try { - return await f(span) - } catch (error) { - span.setTag(ERROR, true) - span.log({ - event: 'error', - 'error.object': error, - stack: error.stack, - message: error.message, - }) - - throw error - } finally { - span.finish() - } -} - -/** - * Log and trace the execution of a function. - * - * @param ctx The tracing context. - * @param name The name of the span and text of the log message. - * @param f The function to invoke. - */ -export function logAndTraceCall( - { logger = createSilentLogger(), span = new Span() }: TracingContext, - name: string, - f: (ctx: TracingContext) => Promise | T -): Promise { - return logCall(name, logger, () => traceCall(name, span, childSpan => f({ logger, span: childSpan }))) -} diff --git a/cmd/precise-code-intel/src/shared/util.ts b/cmd/precise-code-intel/src/shared/util.ts deleted file mode 100644 index 1064eab49ee..00000000000 --- a/cmd/precise-code-intel/src/shared/util.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Returns true if the given value is not undefined. - * - * @param value The value to test. - */ -export function isDefined(value: T | undefined): value is T { - return value !== undefined -} diff --git a/cmd/precise-code-intel/src/shared/visibility.ts b/cmd/precise-code-intel/src/shared/visibility.ts deleted file mode 100644 index 7590f000ea8..00000000000 --- a/cmd/precise-code-intel/src/shared/visibility.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { EntityManager } from 'typeorm' -import { DumpManager } from './store/dumps' -import { TracingContext } from './tracing' - -/** - * Update the commits for this repo, and update the visible_at_tip flag on the dumps - * of this repository. This will query for commits starting from both the current tip - * of the repo and from given commit. - * - * @param args Parameter bag. - */ -export async function updateCommitsAndDumpsVisibleFromTip({ - entityManager, - dumpManager, - frontendUrl, - repositoryId, - commit, - ctx = {}, -}: { - /** The EntityManager to use as part of a transaction. */ - entityManager: EntityManager - /** The dumps manager instance. */ - dumpManager: DumpManager - /** The url of the frontend internal API. */ - frontendUrl: string - /** The repository id. */ - repositoryId: number - /** - * An optional commit. This should be supplied if an upload was just - * processed. If no commit is supplied, then the commits will be queried - * only from the tip commit of the default branch. - */ - commit?: string - /** The tracing context. */ - ctx?: TracingContext -}): Promise { - const tipCommit = await dumpManager.discoverTip({ - repositoryId, - frontendUrl, - ctx, - }) - if (tipCommit === undefined) { - throw new Error('No tip commit available for repository') - } - - const commits = commit - ? await dumpManager.discoverCommits({ - repositoryId, - commit, - frontendUrl, - ctx, - }) - : new Map() - - if (tipCommit !== commit) { - // If the tip is ahead of this commit, we also want to discover all of - // the commits between this commit and the tip so that we can accurately - // determine what is visible from the tip. If we do not do this before the - // updateDumpsVisibleFromTip call below, no dumps will be reachable from - // the tip and all dumps will be invisible. - - const tipCommits = await dumpManager.discoverCommits({ - repositoryId, - commit: tipCommit, - frontendUrl, - ctx, - }) - - for (const [k, v] of tipCommits.entries()) { - commits.set( - k, - new Set([...(commits.get(k) || []), ...v]) - ) - } - } - - await dumpManager.updateCommits(repositoryId, commits, ctx, entityManager) - await dumpManager.updateDumpsVisibleFromTip(repositoryId, tipCommit, ctx, entityManager) -} diff --git a/cmd/precise-code-intel/src/worker/conversion/batch.test.ts b/cmd/precise-code-intel/src/worker/conversion/batch.test.ts deleted file mode 100644 index 1720f31cb06..00000000000 --- a/cmd/precise-code-intel/src/worker/conversion/batch.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { createBatcher } from './batch' -import { range } from 'lodash' - -describe('createBatcher', () => { - it('should traverse entire tree', () => { - const values = gatherValues('foo', [ - ...range(10).map(i => `bar/${i}.ts`), - ...range(10).map(i => `bar/baz/${i}.ts`), - ...range(10).map(i => `bar/baz/bonk/${i}.ts`), - ]) - - expect(values).toEqual([[''], ['foo'], ['foo/bar'], ['foo/bar/baz'], ['foo/bar/baz/bonk']]) - }) - - it('should batch entries at same depth', () => { - const values = gatherValues( - 'foo', - ['bar', 'baz', 'bonk'].map(d => `${d}/sub/file.ts`) - ) - - expect(values).toEqual([ - [''], - ['foo'], - ['foo/bar', 'foo/baz', 'foo/bonk'], - ['foo/bar/sub', 'foo/baz/sub', 'foo/bonk/sub'], - ]) - }) - - it('should batch entries at same depth (wide)', () => { - const is = range(0, 5) - const ds = ['bar', 'baz', 'bonk'] - - const values = gatherValues( - 'foo', - ds.flatMap(d => is.map(i => `${d}/${i}/file.ts`)) - ) - - expect(values).toEqual([ - [''], - ['foo'], - ['foo/bar', 'foo/baz', 'foo/bonk'], - [ - 'foo/bar/0', - 'foo/bar/1', - 'foo/bar/2', - 'foo/bar/3', - 'foo/bar/4', - 'foo/baz/0', - 'foo/baz/1', - 'foo/baz/2', - 'foo/baz/3', - 'foo/baz/4', - 'foo/bonk/0', - 'foo/bonk/1', - 'foo/bonk/2', - 'foo/bonk/3', - 'foo/bonk/4', - ], - ]) - }) - - it('should cut subtrees that do not exist', () => { - const ds = ['bar', 'baz', 'bonk'] - const ss = ['a', 'b', 'c'] - const is = range(1, 4) - - const blacklist = ['foo/bar', 'foo/baz/a', 'foo/bonk/a/1', 'foo/bonk/a/2', 'foo/bonk/b/1', 'foo/bonk/b/3'] - - const values = gatherValues( - 'foo', - ds.flatMap(d => ss.flatMap(s => is.map(i => `${d}/${s}/${i}/sub/file.ts`))), - blacklist - ) - - const prune = (paths: string[]): string[] => - // filter out all proper descendants of the blacklist - paths.filter(p => blacklist.includes(p) || !blacklist.some(b => p.includes(b))) - - expect(values).toEqual([ - [''], - ['foo'], - prune(ds.map(d => `foo/${d}`)), - prune(ds.flatMap(d => ss.map(s => `foo/${d}/${s}`))), - prune(ds.flatMap(d => ss.flatMap(s => is.map(i => `foo/${d}/${s}/${i}`)))), - prune(ds.flatMap(d => ss.flatMap(s => is.map(i => `foo/${d}/${s}/${i}/sub`)))), - ]) - }) -}) - -function gatherValues(root: string, documentPaths: string[], blacklist: string[] = []): string[][] { - const batcher = createBatcher(root, documentPaths) - - let done: boolean | undefined - let batch: string[] | void | undefined - - const all = [] - while (true) { - ;({ value: batch, done } = batcher.next((batch || []).filter(x => !blacklist?.includes(x)))) - if (done || !batch) { - break - } - - all.push(batch) - } - - return all -} diff --git a/cmd/precise-code-intel/src/worker/conversion/batch.ts b/cmd/precise-code-intel/src/worker/conversion/batch.ts deleted file mode 100644 index 730993893ee..00000000000 --- a/cmd/precise-code-intel/src/worker/conversion/batch.ts +++ /dev/null @@ -1,100 +0,0 @@ -import * as path from 'path' -import { dirnameWithoutDot } from './paths' - -/** - * Create a directory tree from the complete set of file paths in an LSIF dump. - * Returns a generator that will perform a breadth-first traversal of the tree. - * - * @param root The LSIF dump root. - * @param documentPaths The set of file paths in an LSIF dump. - */ -export function createBatcher(root: string, documentPaths: string[]): Generator { - return traverse(createTree(root, documentPaths)) -} - -/** A node in a directory path tree. */ -interface Node { - /** The name of a directory in this level of the directory tree. */ - dirname: string - - /** The segments directly nested in this node. */ - children: Node[] -} - -/** - * Create a directory tree from the complete set of file paths in an LSIF dump. - * - * @param root The LSIF dump root. - * @param documentPaths The set of file paths in an LSIF dump. - */ -function createTree(root: string, documentPaths: string[]): Node { - // Construct the the set of root-relative parent directories for each path - const documentDirs = documentPaths.map(documentPath => dirnameWithoutDot(path.join(root, documentPath))) - // Deduplicate and throw out any directory that is outside of the dump root - const dirs = Array.from(new Set(documentDirs)).filter(dirname => !dirname.startsWith('..')) - - // Construct the canned root node - const rootNode: Node = { dirname: '', children: [] } - - for (const dir of dirs) { - // Skip the dump root as the following loop body would make - // it a child of itself. This is a non-obvious edge condition. - if (dir === '') { - continue - } - - let node = rootNode - - // For each path segment in this directory, traverse down the - // tree. Any node that doesn't exist is created with empty - // children. - - for (const dirname of dir.split('/')) { - let child = node.children.find(n => n.dirname === dirname) - if (!child) { - child = { dirname, children: [] } - node.children.push(child) - } - - node = child - } - } - - return rootNode -} - -/** - * Perform a breadth-first traversal of a directory tree. Returns a generator that - * acts as a coroutine visitor. The generator first yields the root (empty) path, - * then waits for the caller to return the set of paths from the last batch that - * exist in git. The next batch returned will only include the paths that are - * properly nested under a previously returned value. - * - * @param root The root node. - */ -function* traverse(root: Node): Generator { - // The frontier is the candidate batch that will be returned in the next - // call to the generator. This is a superset of paths that will actually - // be returned after pruning non-existent paths. - let frontier: [string, Node[]][] = [['', root.children]] - - while (frontier.length > 0) { - // Yield our current batch and wait for the caller to return the set - // of previously yielded paths that exist. - const exists = yield frontier.map(([parent]) => parent) - - frontier = frontier - // Remove any children from the frontier that are not a proper - // descendant of some path that was confirmed to be exist. This - // will stop us from iterating children that definitely don't - // exist as the parent is already known to be an un-tracked path. - .filter(([parent]) => exists.includes(parent)) - .flatMap(([parent, children]) => - // Join the current path to a node with the node's segment to - // get the full path to that node. Create the new frontier from - // the current frontier's children after pruning the non-existent - // paths. - children.map((child): [string, Node[]] => [path.join(parent, child.dirname), child.children]) - ) - } -} diff --git a/cmd/precise-code-intel/src/worker/conversion/conversion.ts b/cmd/precise-code-intel/src/worker/conversion/conversion.ts deleted file mode 100644 index f3df39c947b..00000000000 --- a/cmd/precise-code-intel/src/worker/conversion/conversion.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as pgModels from '../../shared/models/pg' -import { TracingContext } from '../../shared/tracing' -import { EntityManager } from 'typeorm' -import { convertLsif } from './importer' -import { createSilentLogger } from '../../shared/logging' -import { DependencyManager } from '../../shared/store/dependencies' -import { PathExistenceChecker } from './existence' - -/** - * Convert the LSIF dump input into a SQLite database and populate the dependency tables - * with packages and reference data. - * - * @param entityManager The EntityManager to use as part of a transaction. - * @param dependencyManager The dependency manager instance. - * @param frontendUrl The url of the frontend internal API. - * @param upload The unprocessed upload record. - * @param sourcePath The path to the upload file. - * @param targetPath The target database filename. - * @param ctx The tracing context. - */ -export async function convertDatabase( - entityManager: EntityManager, - dependencyManager: DependencyManager, - frontendUrl: string, - upload: pgModels.LsifUpload, - sourcePath: string, - targetPath: string, - { logger = createSilentLogger(), span }: TracingContext -): Promise { - const ctx = { logger, span } - - const pathExistenceChecker = new PathExistenceChecker({ - repositoryId: upload.repositoryId, - commit: upload.commit, - root: upload.root, - frontendUrl, - ctx, - }) - - // Create database in a temp path - const { packages, references } = await convertLsif({ - path: sourcePath, - root: upload.root, - database: targetPath, - pathExistenceChecker, - ctx, - }) - - // Insert dump and add packages and references to Postgres - await dependencyManager.addPackagesAndReferences(upload.id, packages, references, ctx, entityManager) -} diff --git a/cmd/precise-code-intel/src/worker/conversion/correlator.test.ts b/cmd/precise-code-intel/src/worker/conversion/correlator.test.ts deleted file mode 100644 index 3d523073911..00000000000 --- a/cmd/precise-code-intel/src/worker/conversion/correlator.test.ts +++ /dev/null @@ -1,323 +0,0 @@ -import * as lsif from 'lsif-protocol' -import { Correlator, normalizeHover } from './correlator' - -describe('Correlator', () => { - it('should stash lsif version and project root from metadata', () => { - const c = new Correlator() - c.insert({ - id: '1', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.metaData, - positionEncoding: 'utf-16', - version: '0.4.3', - projectRoot: 'file:///lsif-test', - }) - - const projectRoot = c.projectRoot - expect(c.lsifVersion).toEqual('0.4.3') - expect(projectRoot?.href).toEqual('file:///lsif-test/') - }) - - it('should require metadata vertex before document vertices', () => { - const c = new Correlator() - - expect(() => { - c.insert({ - id: '1', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.document, - uri: 'file:///lsif-test/index.ts', - languageId: 'typescript', - }) - }).toThrowError(new Error('No metadata defined.')) - }) - - it('should find root-relative document paths', () => { - const c = new Correlator() - c.insert({ - id: '1', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.metaData, - positionEncoding: 'utf-16', - version: '0.4.3', - projectRoot: 'file:///lsif-test', - }) - - c.insert({ - id: '2', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.document, - uri: 'file:///lsif-test/sub/path/index.ts', - languageId: 'typescript', - }) - - expect(c.documentPaths).toEqual(new Map([['2', 'sub/path/index.ts']])) - }) - - it('should determine type of item relation the outV property', () => { - const c = new Correlator() - c.insert({ - id: '1', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.metaData, - positionEncoding: 'utf-16', - version: '0.4.3', - projectRoot: 'file:///lsif-test', - }) - - c.insert({ - id: '2', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.document, - uri: 'file:///lsif-test/sub/path/index.ts', - languageId: 'typescript', - }) - - c.insert({ - id: '3', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.range, - start: { line: 3, character: 16 }, - end: { line: 3, character: 19 }, - }) - - c.insert({ - id: '4', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.definitionResult, - }) - - c.insert({ - id: '5', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.referenceResult, - }) - - c.insert({ - id: '5', - type: lsif.ElementTypes.edge, - label: lsif.EdgeLabels.item, - outV: '4', - inVs: ['3'], - document: '2', - }) - - c.insert({ - id: '5', - type: lsif.ElementTypes.edge, - label: lsif.EdgeLabels.item, - outV: '5', - inVs: ['3'], - document: '2', - }) - - const defs = c.definitionData.get('4') - expect(defs?.get('2')).toEqual(['3']) - - const refs = c.referenceData.get('5') - expect(refs?.get('2')).toEqual(['3']) - }) - - it('should correlate linked reference results', () => { - const c = new Correlator() - - c.insert({ - id: '2', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.referenceResult, - }) - - c.insert({ - id: '3', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.referenceResult, - }) - - c.insert({ - id: '4', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.referenceResult, - }) - - c.insert({ - id: '5', - type: lsif.ElementTypes.edge, - label: lsif.EdgeLabels.item, - outV: '2', - inVs: ['3', '4'], - document: '1', - }) - - c.insert({ - id: '6', - type: lsif.ElementTypes.edge, - label: lsif.EdgeLabels.item, - outV: '4', - inVs: ['3'], - document: '1', - }) - - expect(c.linkedReferenceResults.extractSet('2')).toEqual(new Set(['2', '3', '4'])) - expect(c.linkedReferenceResults.extractSet('3')).toEqual(new Set(['2', '3', '4'])) - expect(c.linkedReferenceResults.extractSet('4')).toEqual(new Set(['2', '3', '4'])) - }) - - it('should normalize hover results', () => { - const c = new Correlator() - c.insert({ - id: '1', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.hoverResult, - result: { - contents: { - language: 'typescript', - value: 'bar', - }, - }, - }) - - expect(c.hoverData.get('1')).toEqual('```typescript\nbar\n```') - }) - - it('should stash imported monikers', () => { - const c = new Correlator() - c.insert({ - id: '1', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.moniker, - kind: lsif.MonikerKind.import, - scheme: 'tsc', - identifier: 'lsif-test:index:foo', - }) - - c.insert({ - id: '2', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.packageInformation, - manager: 'npm', - name: 'dependency', - version: '0.1.0', - }) - - c.insert({ - id: '3', - type: lsif.ElementTypes.edge, - label: lsif.EdgeLabels.packageInformation, - outV: '1', - inV: '2', - }) - - expect(c.importedMonikers).toEqual(new Set(['1'])) - }) - - it('should stash exported monikers', () => { - const c = new Correlator() - c.insert({ - id: '1', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.moniker, - kind: lsif.MonikerKind.export, - scheme: 'tsc', - identifier: 'lsif-test:index:foo', - }) - - c.insert({ - id: '2', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.packageInformation, - manager: 'npm', - name: 'dependency', - version: '0.1.0', - }) - - c.insert({ - id: '3', - type: lsif.ElementTypes.edge, - label: lsif.EdgeLabels.packageInformation, - outV: '1', - inV: '2', - }) - - expect(c.exportedMonikers).toEqual(new Set(['1'])) - }) - - it('should correlate monikers', () => { - const c = new Correlator() - c.insert({ - id: '1', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.range, - start: { line: 3, character: 16 }, - end: { line: 3, character: 19 }, - }) - - c.insert({ - id: '2', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.moniker, - scheme: 'tsc', - identifier: 'lsif-test:index:foo', - }) - - c.insert({ - id: '3', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.moniker, - scheme: 'npm', - identifier: 'lsif-test:index:foo', - }) - - c.insert({ - id: '4', - type: lsif.ElementTypes.vertex, - label: lsif.VertexLabels.moniker, - scheme: 'super-npm', - identifier: 'lsif-test:index:foo', - }) - - c.insert({ - id: '5', - type: lsif.ElementTypes.edge, - label: lsif.EdgeLabels.moniker, - outV: '1', - inV: '2', - }) - - c.insert({ - id: '6', - type: lsif.ElementTypes.edge, - label: lsif.EdgeLabels.nextMoniker, - outV: '2', - inV: '3', - }) - - c.insert({ - id: '6', - type: lsif.ElementTypes.edge, - label: lsif.EdgeLabels.nextMoniker, - outV: '3', - inV: '4', - }) - - const range = c.rangeData.get('1') - expect(range?.monikerIds).toEqual(new Set(['2'])) - expect(c.linkedMonikers.extractSet('2')).toEqual(new Set(['2', '3', '4'])) - expect(c.linkedMonikers.extractSet('3')).toEqual(new Set(['2', '3', '4'])) - expect(c.linkedMonikers.extractSet('4')).toEqual(new Set(['2', '3', '4'])) - }) -}) - -describe('normalizeHover', () => { - it('should handle all lsp.Hover types', () => { - expect(normalizeHover({ contents: 'foo' })).toEqual('foo') - expect(normalizeHover({ contents: { language: 'typescript', value: 'bar' } })).toEqual( - '```typescript\nbar\n```' - ) - expect(normalizeHover({ contents: { kind: 'markdown', value: 'baz' } })).toEqual('baz') - expect( - normalizeHover({ - contents: ['foo', { language: 'typescript', value: 'bar' }], - }) - ).toEqual('foo\n\n---\n\n```typescript\nbar\n```') - }) -}) diff --git a/cmd/precise-code-intel/src/worker/conversion/correlator.ts b/cmd/precise-code-intel/src/worker/conversion/correlator.ts deleted file mode 100644 index 0a982ff76d3..00000000000 --- a/cmd/precise-code-intel/src/worker/conversion/correlator.ts +++ /dev/null @@ -1,463 +0,0 @@ -import * as sqliteModels from '../../shared/models/sqlite' -import * as lsif from 'lsif-protocol' -import { createSilentLogger } from '../../shared/logging' -import { DefaultMap } from '../../shared/datastructures/default-map' -import { DisjointSet } from '../../shared/datastructures/disjoint-set' -import { Hover, MarkupContent } from 'vscode-languageserver-types' -import { Logger } from 'winston' -import { mustGet, mustGetFromEither } from '../../shared/maps' -import { relativePath } from './paths' - -/** Identifiers of result set vertices. */ -export type ResultSetId = lsif.Id - -/** - * An internal representation of a result set vertex. This is only used during - * correlation and import as we flatten this data into the range vertices for - * faster queries. - */ -export interface ResultSetData { - /** The identifier of the definition result attached to this result set. */ - definitionResultId?: sqliteModels.DefinitionResultId - - /** The identifier of the reference result attached to this result set. */ - referenceResultId?: sqliteModels.ReferenceResultId - - /** The identifier of the hover result attached to this result set. */ - hoverResultId?: sqliteModels.HoverResultId - - /** The set of moniker identifiers directly attached to this result set. */ - monikerIds: Set -} - -/** - * Common state around the conversion of a single LSIF dump upload. This class - * receives the parsed vertex or edge, line by line and adds it into an in-memory - * adjacency-list graph structure that is later processed and converted into a - * SQLite database on disk. - */ -export class Correlator { - /** - * The LSIF version of the input. This is extracted from the metadata vertex at - * the beginning of processing. - */ - public lsifVersion?: string - - /** - * The root of all document URIs. This is extracted from the metadata vertex at - * the beginning of processing. - */ - public projectRoot?: URL - - // Vertex data - public documentPaths = new Map() - public rangeData = new Map() - public resultSetData = new Map() - public hoverData = new Map() - public monikerData = new Map() - public packageInformationData = new Map() - public unsupportedVertexes = new Set() - - // Edge data - public nextData = new Map() - public containsData = new Map>() - public definitionData = new Map< - sqliteModels.DefinitionResultId, - DefaultMap - >() - public referenceData = new Map< - sqliteModels.ReferenceResultId, - DefaultMap - >() - - /** A disjoint set of monikers linked by `nextMoniker` edges. */ - public linkedMonikers = new DisjointSet() - - /** A disjoint set of reference results linked by `item` edges. */ - public linkedReferenceResults = new DisjointSet() - - /** The set of exported moniker identifiers that have package information attached. */ - public importedMonikers = new Set() - - /** The set of exported moniker identifiers that have package information attached. */ - public exportedMonikers = new Set() - - /** - * Creates a new Correlator. - * - * @param dumpRoot The repository-relative root of all files that are in the dump. - * @param logger The logger instance. - */ - constructor(private dumpRoot: string = '', private logger: Logger = createSilentLogger()) {} - - /** - * Process a single vertex or edge. - * - * @param element A vertex or edge element from the LSIF dump. - */ - public insert(element: lsif.Vertex | lsif.Edge): void { - if (element.type === lsif.ElementTypes.vertex) { - switch (element.label) { - case lsif.VertexLabels.metaData: - this.handleMetaData(element) - break - - case lsif.VertexLabels.document: { - if (!this.projectRoot) { - throw new Error('No metadata defined.') - } - - const path = relativePath(this.projectRoot, new URL(element.uri)) - this.documentPaths.set(element.id, path) - this.containsData.set(element.id, new Set()) - break - } - - // The remaining vertex handlers stash data into an appropriate map. This data - // may be retrieved when an edge that references it is seen, or when a document - // is finalized. - - case lsif.VertexLabels.range: - this.rangeData.set(element.id, { - startLine: element.start.line, - startCharacter: element.start.character, - endLine: element.end.line, - endCharacter: element.end.character, - monikerIds: new Set(), - }) - break - - case lsif.VertexLabels.resultSet: - this.resultSetData.set(element.id, { monikerIds: new Set() }) - break - - case lsif.VertexLabels.definitionResult: - this.definitionData.set( - element.id, - new DefaultMap(() => []) - ) - break - - case lsif.VertexLabels.referenceResult: - this.referenceData.set( - element.id, - new DefaultMap(() => []) - ) - break - - case lsif.VertexLabels.hoverResult: - this.hoverData.set(element.id, normalizeHover(element.result)) - break - - case lsif.VertexLabels.moniker: - this.monikerData.set(element.id, { - kind: element.kind || lsif.MonikerKind.local, - scheme: element.scheme, - identifier: element.identifier, - }) - break - - case lsif.VertexLabels.packageInformation: - this.packageInformationData.set(element.id, { - name: element.name, - version: element.version || null, - }) - break - - default: - // Some vertex labels are not yet supported: - // - // - typeDefinitionResult - // - implementationResult - // - ... others in the future - // - // We keep track of these unsupported vertexes so that we - // don't mistake it for a missing vertex later when visiting - // edges. - this.unsupportedVertexes.add(element.id) - break - } - } - - if (element.type === lsif.ElementTypes.edge) { - switch (element.label) { - case lsif.EdgeLabels.contains: - this.handleContains(element) - break - - case lsif.EdgeLabels.next: - this.handleNextEdge(element) - break - - case lsif.EdgeLabels.item: - this.handleItemEdge(element) - break - - case lsif.EdgeLabels.textDocument_definition: - this.handleDefinitionEdge(element) - break - - case lsif.EdgeLabels.textDocument_references: - this.handleReferenceEdge(element) - break - - case lsif.EdgeLabels.textDocument_hover: - this.handleHoverEdge(element) - break - - case lsif.EdgeLabels.moniker: - this.handleMonikerEdge(element) - break - - case lsif.EdgeLabels.nextMoniker: - this.handleNextMonikerEdge(element) - break - - case lsif.EdgeLabels.packageInformation: - this.handlePackageInformationEdge(element) - break - } - } - } - - // - // Vertex Handlers - - /** - * This should be the first vertex seen. Extract the project root so we - * can create relative paths for documents and cache the LSIF protocol - * version that we will later insert into he metadata table. - * - * @param vertex The metadata vertex. - */ - private handleMetaData({ version, projectRoot }: lsif.MetaData): void { - this.lsifVersion = version - this.projectRoot = new URL(projectRoot.endsWith('/') ? projectRoot : projectRoot + '/') - - // We assume that the project root in the LSIF dump is either: - // - // (1) the root of the LSIF dump, or - // (2) the root of the repository - // - // These are the common cases and we don't explicitly support - // anything else. Here we normalize to (1) by appending the dump - // root if it's not already suffixed by it. - - if (this.dumpRoot !== '' && !this.projectRoot.href.endsWith(this.dumpRoot)) { - this.projectRoot = new URL(this.dumpRoot, `${this.projectRoot.href}/`) - } - } - - // - // Edge Handlers - - /** - * Add range data ids into the document in which they are contained. Ensures - * all referenced vertices are defined. - * - * @param edge The contains edge. - */ - private handleContains(edge: lsif.contains): void { - // Do not track project contains - if (!this.documentPaths.has(edge.outV)) { - return - } - - const set = mustGet(this.containsData, edge.outV, 'contains') - for (const inV of edge.inVs) { - mustGet(this.rangeData, inV, 'range') - set.add(inV) - } - } - - /** - * Update definition and reference fields from an item edge. Ensures all - * referenced vertices are defined. - * - * @param edge The item edge. - */ - private handleItemEdge(edge: lsif.item): void { - if (this.definitionData.has(edge.outV)) { - const documentMap = mustGet(this.definitionData, edge.outV, 'definitionResult') - const rangeIds = documentMap.getOrDefault(edge.document) - for (const inV of edge.inVs) { - mustGet(this.rangeData, inV, 'range') - rangeIds.push(inV) - } - - return - } - - if (this.referenceData.has(edge.outV)) { - const documentMap = mustGet(this.referenceData, edge.outV, 'referenceResult') - const rangeIds = documentMap.getOrDefault(edge.document) - for (const inV of edge.inVs) { - if (this.referenceData.has(inV)) { - this.linkedReferenceResults.union(edge.outV, inV) - } else { - mustGet(this.rangeData, inV, 'range') - rangeIds.push(inV) - } - } - - return - } - - if (this.unsupportedVertexes.has(edge.outV)) { - this.logger.debug('Skipping edge from an unsupported vertex', { edge }) - return - } - - throw new Error(`Item edge references a nonexistent vertex ${JSON.stringify(edge)}`) - } - - /** - * Attaches the specified moniker to the specified range or result set. Ensures all referenced - * vertices are defined. - * - * @param edge The moniker edge. - */ - private handleMonikerEdge(edge: lsif.moniker): void { - const source = mustGetFromEither( - this.rangeData, - this.resultSetData, - edge.outV, - 'range/resultSet' - ) - - mustGet(this.monikerData, edge.inV, 'moniker') - source.monikerIds = new Set([edge.inV]) - } - - /** - * Sets the next field of the specified range or result set. Ensures all referenced vertices - * are defined. - * - * @param edge The next edge. - */ - private handleNextEdge(edge: lsif.next): void { - mustGetFromEither( - this.rangeData, - this.resultSetData, - edge.outV, - 'range/resultSet' - ) - - mustGet(this.resultSetData, edge.inV, 'resultSet') - this.nextData.set(edge.outV, edge.inV) - } - - /** - * Correlates monikers together so that when one moniker is queried, each correlated moniker - * is also returned as a strongly connected set. Ensures all referenced vertices are defined. - * - * @param edge The nextMoniker edge. - */ - private handleNextMonikerEdge(edge: lsif.nextMoniker): void { - mustGet(this.monikerData, edge.inV, 'moniker') - mustGet(this.monikerData, edge.outV, 'moniker') - this.linkedMonikers.union(edge.inV, edge.outV) - } - - /** - * Sets the package information of the specified moniker. If the moniker is an export moniker, - * then the package information will also be returned as an exported package by the `finalize` - * method. Ensures all referenced vertices are defined. - * - * @param edge The packageInformation edge. - */ - private handlePackageInformationEdge(edge: lsif.packageInformation): void { - const source = mustGet(this.monikerData, edge.outV, 'moniker') - mustGet(this.packageInformationData, edge.inV, 'packageInformation') - source.packageInformationId = edge.inV - - if (source.kind === 'export') { - this.exportedMonikers.add(edge.outV) - } - - if (source.kind === 'import') { - this.importedMonikers.add(edge.outV) - } - } - - /** - * Sets the definition result of the specified range or result set. Ensures all referenced - * vertices are defined. - * - * @param edge The textDocument/definition edge. - */ - private handleDefinitionEdge(edge: lsif.textDocument_definition): void { - const outV = mustGetFromEither( - this.rangeData, - this.resultSetData, - edge.outV, - 'range/resultSet' - ) - - mustGet(this.definitionData, edge.inV, 'definitionResult') - outV.definitionResultId = edge.inV - } - - /** - * Sets the reference result of the specified range or result set. Ensures all - * referenced vertices are defined. - * - * @param edge The textDocument/references edge. - */ - private handleReferenceEdge(edge: lsif.textDocument_references): void { - const outV = mustGetFromEither( - this.rangeData, - this.resultSetData, - edge.outV, - 'range/resultSet' - ) - - mustGet(this.referenceData, edge.inV, 'referenceResult') - outV.referenceResultId = edge.inV - } - - /** - * Sets the hover result of the specified range or result set. Ensures all referenced - * vertices are defined. - * - * @param edge The textDocument/hover edge. - */ - private handleHoverEdge(edge: lsif.textDocument_hover): void { - const outV = mustGetFromEither( - this.rangeData, - this.resultSetData, - edge.outV, - 'range/resultSet' - ) - - mustGet(this.hoverData, edge.inV, 'hoverResult') - outV.hoverResultId = edge.inV - } -} - -/** - * Normalize an LSP hover object into a string. - * - * @param hover The hover object. - */ -export function normalizeHover(hover: Hover): string { - const normalizeContent = (content: string | MarkupContent | { language: string; value: string }): string => { - if (typeof content === 'string') { - return content - } - - if (MarkupContent.is(content)) { - return content.value - } - - const tick = '```' - return `${tick}${content.language}\n${content.value}\n${tick}` - } - - const separator = '\n\n---\n\n' - const contents = Array.isArray(hover.contents) ? hover.contents : [hover.contents] - return contents - .map(c => normalizeContent(c).trim()) - .filter(s => s) - .join(separator) -} diff --git a/cmd/precise-code-intel/src/worker/conversion/existence.test.ts b/cmd/precise-code-intel/src/worker/conversion/existence.test.ts deleted file mode 100644 index f549a700435..00000000000 --- a/cmd/precise-code-intel/src/worker/conversion/existence.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as sinon from 'sinon' -import { PathExistenceChecker } from './existence' -import { getDirectoryChildren } from '../../shared/gitserver/gitserver' -import { range } from 'lodash' - -describe('PathExistenceChecker', () => { - it('should test path existence in git tree', async () => { - const children = new Map([ - ['', ['web', 'shared']], - ['web', ['web/foo.ts']], - ['web/shared', ['web/shared/bonk.ts']], - ['shared', ['shared/bar.ts', 'shared/baz.ts']], - ]) - - const pathExistenceChecker = new PathExistenceChecker({ - repositoryId: 42, - commit: 'c', - root: 'web', - frontendUrl: 'frontend', - mockGetDirectoryChildren: ({ dirnames }) => - Promise.resolve(new Map(dirnames.map(dirname => [dirname, new Set(children.get(dirname))]))), - }) - - await pathExistenceChecker.warmCache([ - 'foo.ts', - 'bar.ts', - 'shared/bonk.ts', - '../shared/bar.ts', - '../shared/bar.ts', - '../shared/bonk.ts', - '../node_modules/@types/quux.ts', - '../../node_modules/@types/oops.ts', - ]) - - // Test within root - expect(pathExistenceChecker.shouldIncludePath('foo.ts', false)).toBeTruthy() - expect(pathExistenceChecker.shouldIncludePath('bar.ts', false)).toBeFalsy() - expect(pathExistenceChecker.shouldIncludePath('shared/bonk.ts', false)).toBeTruthy() - // Test outside root but within repo - expect(pathExistenceChecker.shouldIncludePath('../shared/bar.ts', false)).toBeTruthy() - expect(pathExistenceChecker.shouldIncludePath('../shared/bar.ts', true)).toBeFalsy() - expect(pathExistenceChecker.shouldIncludePath('../shared/bonk.ts', false)).toBeFalsy() - expect(pathExistenceChecker.shouldIncludePath('../node_modules/@types/quux.ts', false)).toBeFalsy() - - // Test outside repo - expect(pathExistenceChecker.shouldIncludePath('../../node_modules/@types/oops.ts', false)).toBeFalsy() - }) - - it('should cache directory contents', async () => { - const children = new Map([['', Array.from(range(100).map(i => `${i}.ts`))]]) - const mockGetDirectoryChildren = sinon.spy(({ dirnames }) => - Promise.resolve(new Map(dirnames.map(dirname => [dirname, new Set(children.get(dirname))]))) - ) - const pathExistenceChecker = new PathExistenceChecker({ - repositoryId: 42, - commit: 'c', - root: '', - frontendUrl: 'frontend', - mockGetDirectoryChildren, - }) - - await pathExistenceChecker.warmCache(Array.from(range(100).flatMap(i => [`${i}.ts`, `${i}.js`]))) - - for (let i = 0; i < 100; i++) { - expect(pathExistenceChecker.shouldIncludePath(`${i}.ts`, false)).toBeTruthy() - expect(pathExistenceChecker.shouldIncludePath(`${i}.js`, false)).toBeFalsy() - } - - expect(mockGetDirectoryChildren.callCount).toEqual(1) - }) - - it('should early out on untracked ancestors', async () => { - const children = new Map([['', ['not_node_modules']]]) - const mockGetDirectoryChildren = sinon.spy(({ dirnames }) => - Promise.resolve(new Map(dirnames.map(dirname => [dirname, new Set(children.get(dirname))]))) - ) - - const pathExistenceChecker = new PathExistenceChecker({ - repositoryId: 42, - commit: 'c', - root: '', - frontendUrl: 'frontend', - mockGetDirectoryChildren, - }) - - await pathExistenceChecker.warmCache( - range(0, 100).flatMap(i => [`node_modules/${i}/deeply/nested/lib/file.ts`]) - ) - - for (let i = 0; i < 100; i++) { - const path = `node_modules/${i}/deeply/nested/lib/file.ts` - expect(pathExistenceChecker.shouldIncludePath(path, false)).toBeFalsy() - } - - // Should only check children of / and /node_modules - expect(mockGetDirectoryChildren.callCount).toEqual(2) - }) -}) diff --git a/cmd/precise-code-intel/src/worker/conversion/importer.ts b/cmd/precise-code-intel/src/worker/conversion/importer.ts deleted file mode 100644 index 5df27955258..00000000000 --- a/cmd/precise-code-intel/src/worker/conversion/importer.ts +++ /dev/null @@ -1,783 +0,0 @@ -import * as sqliteModels from '../../shared/models/sqlite' -import * as lsif from 'lsif-protocol' -import { Correlator, ResultSetData, ResultSetId } from './correlator' -import { createSqliteConnection } from '../../shared/database/sqlite' -import { databaseInsertionDurationHistogram, databaseInsertionErrorsCounter } from '../metrics' -import { DefaultMap } from '../../shared/datastructures/default-map' -import { EntityManager } from 'typeorm' -import { gzipJSON } from '../../shared/encoding/json' -import { hashKey } from '../../shared/models/hash' -import { isEqual, uniqWith } from 'lodash' -import { logAndTraceCall, TracingContext } from '../../shared/tracing' -import { mustGet } from '../../shared/maps' -import { Package, SymbolReferences } from '../../shared/store/dependencies' -import { readGzippedJsonElementsFromFile } from '../../shared/input' -import { TableInserter } from '../../shared/database/inserter' -import { createSilentLogger } from '../../shared/logging' -import { PathExistenceChecker } from './existence' -import * as settings from '../settings' - -/** The insertion metrics for the database. */ -const inserterMetrics = { - durationHistogram: databaseInsertionDurationHistogram, - errorsCounter: databaseInsertionErrorsCounter, -} - -/** - * The internal version of our SQLite databases. We need to keep this in case - * we add something that can't be done transparently; if we change how we process - * something in the future we'll need to consider a number of previous version - * while we update or re-process the already-uploaded data. - */ -const INTERNAL_LSIF_VERSION = '0.1.0' - -/** - * Populate a SQLite database with the given input stream. Returns the - * data required to populate the dependency tables in Postgres. - * - * @param args Parameter bag. - */ -export async function convertLsif({ - path, - root, - database, - pathExistenceChecker, - ctx: { logger = createSilentLogger(), span } = {}, -}: { - /** The filepath containing a gzipped compressed stream of JSON lines composing the LSIF dump. */ - path: string - /** The root of all files that are in the dump. */ - root: string - /** The filepath of the database to populate. */ - database: string - /** An object that tracks whether a path is visible within the LSIF dump. */ - pathExistenceChecker: PathExistenceChecker - /** The tracing context. */ - ctx?: TracingContext -}): Promise<{ packages: Package[]; references: SymbolReferences[] }> { - const connection = await createSqliteConnection(database, sqliteModels.entities, logger) - - try { - await connection.query('PRAGMA synchronous = OFF') - await connection.query('PRAGMA journal_mode = OFF') - - return await connection.transaction(entityManager => - importLsif(entityManager, path, root, pathExistenceChecker, { logger, span }) - ) - } finally { - await connection.close() - } -} - -/** - * Correlate each vertex and edge together, then populate the provided entity manager - * with the document, definition, and reference information. Returns the package and - * external reference data needed to populate the dependency tables in Postgres. - * - * @param entityManager A transactional SQLite entity manager. - * @param path The filepath containing a gzipped compressed stream of JSON lines composing the LSIF dump. - * @param root The root of all files that are in the dump. - * @param pathExistenceChecker An object that tracks whether a path is visible within the LSIF dump. - * @param ctx The tracing context. - */ -export async function importLsif( - entityManager: EntityManager, - path: string, - root: string, - pathExistenceChecker: PathExistenceChecker, - ctx: TracingContext -): Promise<{ packages: Package[]; references: SymbolReferences[] }> { - // Correlate input data into in-memory maps - const correlator = new Correlator(root, ctx.logger) - await logAndTraceCall(ctx, 'Correlating LSIF data', async () => { - for await (const element of readGzippedJsonElementsFromFile(path) as AsyncIterable) { - correlator.insert(element) - } - }) - - if (correlator.lsifVersion === undefined) { - throw new Error('No metadata defined.') - } - - // Determine if multiple documents are defined with the same URI. This happens in - // some indexers (such as lsif-tsc) that index dependent projects into the same - // dump as the target project. For each set of documents that share a path, we - // choose one document to be the canonical representative and merge the contains, - // definition, and reference data into the unique canonical document. - await logAndTraceCall(ctx, 'Merging documents', () => mergeDocuments(correlator)) - - // Determine which reference results are linked together. Determine a canonical - // reference result for each set so that we can remap all identifiers to the - // chosen one. - const canonicalReferenceResultIds = await logAndTraceCall(ctx, 'Canonicalizing reference results', () => - canonicalizeReferenceResults(correlator) - ) - - // Make all necessary visibility queries to gitserver here. This allows us to batch - // the requests to reduce the number of network roundtrips, and also allows us to - // time the total cost of fetching this data from gitserver by doing it all in one - // place. If we perform the queries lazily, we would need to add the timings for - // each individual span in the resulting trace. - await pathExistenceChecker.warmCache(Array.from(correlator.documentPaths.values())) - - // Calculate the number of result chunks that we'll attempt to populate - const numResults = correlator.definitionData.size + correlator.referenceData.size - const numResultChunks = Math.min( - settings.MAX_NUM_RESULT_CHUNKS, - Math.floor(numResults / settings.RESULTS_PER_RESULT_CHUNK) || 1 - ) - - // Insert metadata - const metaInserter = new TableInserter( - entityManager, - sqliteModels.MetaModel, - sqliteModels.MetaModel.BatchSize, - inserterMetrics - ) - await populateMetadataTable(correlator, metaInserter, numResultChunks) - await metaInserter.flush() - - // Insert documents - await logAndTraceCall(ctx, 'Populating documents', async () => { - const documentInserter = new TableInserter( - entityManager, - sqliteModels.DocumentModel, - sqliteModels.DocumentModel.BatchSize, - inserterMetrics - ) - await populateDocumentsTable(correlator, documentInserter, canonicalReferenceResultIds, pathExistenceChecker) - await documentInserter.flush() - }) - - // Insert result chunks - await logAndTraceCall(ctx, 'Populating result chunks', async () => { - const resultChunkInserter = new TableInserter( - entityManager, - sqliteModels.ResultChunkModel, - sqliteModels.ResultChunkModel.BatchSize, - inserterMetrics - ) - await populateResultChunksTable(correlator, resultChunkInserter, numResultChunks, pathExistenceChecker) - await resultChunkInserter.flush() - }) - - // Insert definitions and references - await logAndTraceCall(ctx, 'Populating definitions and references', async () => { - const definitionInserter = new TableInserter( - entityManager, - sqliteModels.DefinitionModel, - sqliteModels.DefinitionModel.BatchSize, - inserterMetrics - ) - const referenceInserter = new TableInserter( - entityManager, - sqliteModels.ReferenceModel, - sqliteModels.ReferenceModel.BatchSize, - inserterMetrics - ) - await populateDefinitionsAndReferencesTables( - correlator, - definitionInserter, - referenceInserter, - pathExistenceChecker - ) - await definitionInserter.flush() - await referenceInserter.flush() - }) - - // Return data to populate dependency tables in Postgres - return { packages: getPackages(correlator), references: getReferences(correlator) } -} - -/** - * Correlate, encode, and insert all document entries for this dump. - * - * @param correlator The correlator with all vertices and edges inserted. - * @param documentInserter The inserter for the documents table. - * @param canonicalReferenceResultIds A map from reference result identifiers to its canonical identifier. - * @param pathExistenceChecker An object that tracks whether a path is visible within the LSIF dump. - */ -async function populateDocumentsTable( - correlator: Correlator, - documentInserter: TableInserter sqliteModels.DocumentModel>, - canonicalReferenceResultIds: Map, - pathExistenceChecker: PathExistenceChecker -): Promise { - // Collapse result sets data into the ranges that can reach them. The - // remainder of this function assumes that we can completely ignore - // the "next" edges coming from range data. - for (const [rangeId, range] of correlator.rangeData) { - canonicalizeItem(correlator, canonicalReferenceResultIds, rangeId, range) - } - - // Gather and insert document data that includes the ranges contained in the document, - // any associated hover data, and any associated moniker data/package information. - // Each range also has identifiers that correlate to a definition or reference result - // which can be found in a result chunk, created in the next step. - - for (const [documentId, documentPath] of correlator.documentPaths) { - // Do not gather any document that is not within the dump root or does not exist - // in git. If the path is outside of the dump root, then it will never be queried - // as the current text document path and the dump root are compared to determine - // which dump to open. If the path does not exist in git, it will also never be - // queried. - if (!pathExistenceChecker.shouldIncludePath(documentPath)) { - continue - } - - // Create document record from the correlated information. This will also insert - // external definitions and references into the maps initialized above, which are - // inserted into the definitions and references table, respectively, below. - const document = gatherDocument(correlator, documentId, documentPath) - - // Encode and insert document record - await documentInserter.insert({ - path: documentPath, - data: await gzipJSON({ - ranges: document.ranges, - hoverResults: document.hoverResults, - monikers: document.monikers, - packageInformation: document.packageInformation, - }), - }) - } -} - -/** - * Correlate and insert all result chunk entries for this dump. - * - * @param correlator The correlator with all vertices and edges inserted. - * @param resultChunkInserter The inserter for the result chunks table. - * @param numResultChunks The number of result chunks used to hash compute the result identifier hash. - * @param pathExistenceChecker An object that tracks whether a path is visible within the LSIF dump. - */ -async function populateResultChunksTable( - correlator: Correlator, - resultChunkInserter: TableInserter sqliteModels.ResultChunkModel>, - numResultChunks: number, - pathExistenceChecker: PathExistenceChecker -): Promise { - // Create all the result chunks we'll be populating and inserting up-front. Data will - // be inserted into result chunks based on hash values (modulo the number of result chunks), - // and we don't want to create them lazily. - - const resultChunks = new Array(numResultChunks).fill(null).map(() => ({ - paths: new Map(), - documentIdRangeIds: new Map(), - })) - - const chunkResults = ( - data: Map> - ): void => { - for (const [id, documentRanges] of data) { - // Flatten map into list of ranges - let flattenedRangeList: (sqliteModels.DocumentIdRangeId & { documentPath: string })[] = [] - for (const [documentId, rangeIds] of documentRanges) { - const documentPath = mustGet(correlator.documentPaths, documentId, 'documentPath') - - // Skip pointing to locations that are not available in git. This can occur - // with indexers that point to generated files or dependencies that are not - // committed (e.g. node_modules). Keeping these in the dump can cause the - // UI to redirect to a path that doesn't exist. - if (!pathExistenceChecker.shouldIncludePath(documentPath, false)) { - continue - } - - flattenedRangeList = flattenedRangeList.concat( - rangeIds.map(rangeId => ({ documentId, documentPath, rangeId })) - ) - } - - // Insert ranges into target result chunk - const resultChunk = resultChunks[hashKey(id, resultChunks.length)] - const documentIdRangeIds = flattenedRangeList.map(({ documentId, rangeId }) => ({ documentId, rangeId })) - resultChunk.documentIdRangeIds.set(id, documentIdRangeIds) - - for (const { documentId } of documentIdRangeIds) { - // Add paths into the result chunk where they are used - resultChunk.paths.set(documentId, mustGet(correlator.documentPaths, documentId, 'documentPath')) - } - } - } - - // Add definitions and references to result chunks - chunkResults(correlator.definitionData) - chunkResults(correlator.referenceData) - - for (const [id, resultChunk] of resultChunks.entries()) { - // Empty chunk, no need to serialize as it will never be queried - if (resultChunk.paths.size === 0 && resultChunk.documentIdRangeIds.size === 0) { - continue - } - - const data = await gzipJSON({ - documentPaths: resultChunk.paths, - documentIdRangeIds: resultChunk.documentIdRangeIds, - }) - - // Encode and insert result chunk record - await resultChunkInserter.insert({ id, data }) - } -} - -/** - * Correlate and insert all definition and reference entries for this dump. - * - * @param correlator The correlator with all vertices and edges inserted. - * @param definitionInserter The inserter for the definitions table. - * @param referenceInserter The inserter for the references table. - * @param pathExistenceChecker An object that tracks whether a path is visible within the LSIF dump. - */ -async function populateDefinitionsAndReferencesTables( - correlator: Correlator, - definitionInserter: TableInserter sqliteModels.DefinitionModel>, - referenceInserter: TableInserter sqliteModels.ReferenceModel>, - pathExistenceChecker: PathExistenceChecker -): Promise { - // Determine the set of monikers that are attached to a definition or a - // reference result. Correlating information in this way has two benefits: - // (1) it reduces duplicates in the definitions and references tables - // (2) it stop us from re-iterating over the range data of the entire - // LSIF dump, which is by far the largest proportion of data. - - const definitionMonikers = new DefaultMap>( - () => new Set() - ) - const referenceMonikers = new DefaultMap>( - () => new Set() - ) - - for (const range of correlator.rangeData.values()) { - if (range.monikerIds.size === 0) { - continue - } - - if (range.definitionResultId !== undefined) { - const set = definitionMonikers.getOrDefault(range.definitionResultId) - for (const monikerId of range.monikerIds) { - set.add(monikerId) - } - } - - if (range.referenceResultId !== undefined) { - const set = referenceMonikers.getOrDefault(range.referenceResultId) - for (const monikerId of range.monikerIds) { - set.add(monikerId) - } - } - } - - const insertMonikerRanges = async ( - data: Map>, - monikers: Map>, - inserter: TableInserter< - sqliteModels.DefinitionModel | sqliteModels.ReferenceModel, - new () => sqliteModels.DefinitionModel | sqliteModels.ReferenceModel - > - ): Promise => { - for (const [id, documentRanges] of data) { - // Get monikers. Nothing to insert if we don't have any. - const monikerIds = monikers.get(id) - if (monikerIds === undefined) { - continue - } - - // Correlate each moniker with the document/range pairs stored in - // the result set provided by the data argument of this function. - - for (const monikerId of monikerIds) { - const moniker = mustGet(correlator.monikerData, monikerId, 'moniker') - - for (const [documentId, rangeIds] of documentRanges) { - const documentPath = mustGet(correlator.documentPaths, documentId, 'documentPath') - - // Skip definitions or references that point to a document that are not - // present in the dump. Including this would cause a query that always - // fails when it cannot resolve the missing document data. - if (!pathExistenceChecker.shouldIncludePath(documentPath)) { - continue - } - - for (const rangeId of rangeIds) { - const range = mustGet(correlator.rangeData, rangeId, 'range') - - await inserter.insert({ - scheme: moniker.scheme, - identifier: moniker.identifier, - documentPath, - ...range, - }) - } - } - } - } - } - - // Insert definitions and references records - await insertMonikerRanges(correlator.definitionData, definitionMonikers, definitionInserter) - await insertMonikerRanges(correlator.referenceData, referenceMonikers, referenceInserter) -} - -/** - * Insert metadata row. This gives us a place to store the version of the converter that - * created a database in case we have backwards-incompatible changes in the future that - * require historic version flagging. This also stores the number of result chunks - * determined above so that we can have stable hashes at query time. - * - * @param correlator The correlator with all vertices and edges inserted. - * @param metaInserter The inserter for the meta table. - * @param numResultChunks The number of result chunks used to hash compute the result identifier hash. - */ -async function populateMetadataTable( - correlator: Correlator, - metaInserter: TableInserter sqliteModels.MetaModel>, - numResultChunks: number -): Promise { - await metaInserter.insert({ - id: 1, - lsifVersion: correlator.lsifVersion, - sourcegraphVersion: INTERNAL_LSIF_VERSION, - numResultChunks, - }) -} - -/** - * Gather all package information that is referenced by an exported - * moniker. These will be the packages that are provided by the repository - * represented by this LSIF dump. - * - * @param correlator The correlator with all vertices and edges inserted. - */ -function getPackages(correlator: Correlator): Package[] { - const packages: Package[] = [] - for (const id of correlator.exportedMonikers) { - const source = mustGet(correlator.monikerData, id, 'moniker') - const packageInformationId = assertId(source.packageInformationId) - const packageInfo = mustGet(correlator.packageInformationData, packageInformationId, 'packageInformation') - packages.push({ - scheme: source.scheme, - name: packageInfo.name, - version: packageInfo.version, - }) - } - - return uniqWith(packages, isEqual) -} - -/** - * Gather all imported moniker identifiers along with their package - * information. These will be the packages that are a dependency of the - * repository represented by this LSIF dump. - * - * @param correlator The correlator with all vertices and edges inserted. - */ -function getReferences(correlator: Correlator): SymbolReferences[] { - const packageIdentifiers: Map = new Map() - for (const id of correlator.importedMonikers) { - const source = mustGet(correlator.monikerData, id, 'moniker') - const packageInformationId = assertId(source.packageInformationId) - const packageInfo = mustGet(correlator.packageInformationData, packageInformationId, 'packageInformation') - const pkg = JSON.stringify({ - scheme: source.scheme, - name: packageInfo.name, - version: packageInfo.version, - }) - - const list = packageIdentifiers.get(pkg) - if (list) { - list.push(source.identifier) - } else { - packageIdentifiers.set(pkg, [source.identifier]) - } - } - - return Array.from(packageIdentifiers).map(([key, identifiers]) => ({ - package: JSON.parse(key) as Package, - identifiers, - })) -} - -/** - * Merge the data in the correlator of all documents that share the same path. This - * function works by moving the contains, definition, and reference data keyed by a - * document with a duplicate path into a canonical document with that path. The first - * document inserted for a path is the canonical document for that path. This function - * guarantees that duplicate document ids are removed from these maps. - * - * @param correlator The correlator with all vertices and edges inserted. - */ -function mergeDocuments(correlator: Correlator): void { - const uriMap = new Map() - for (const [id, path] of correlator.documentPaths.entries()) { - const canonicalId = uriMap.get(path) - if (canonicalId === undefined) { - uriMap.set(path, id) - continue - } - - mergeContains(id, canonicalId, correlator.containsData) - mergeDefinitionReferences(id, canonicalId, correlator.definitionData) - mergeDefinitionReferences(id, canonicalId, correlator.referenceData) - - // Discard the document data as a flag to prevent inserting one - // of the documents subsumed by the canonical representative. - correlator.documentPaths.delete(id) - } -} - -/** - * Move the contains data for document `id` into the contains data of document - * `canonicalId`, then delete the reference to document `id.` - * - * @param id The id of the document to replace. - * @param canonicalId The id of the document to subsume the other. - * @param containsData The containment map of the correlator. - */ -function mergeContains( - id: sqliteModels.DocumentId, - canonicalId: sqliteModels.DocumentId, - containsData: Map> -): void { - const contains = mustGet(containsData, id, 'contains') - const canonicalContains = mustGet(containsData, canonicalId, 'contains') - for (const rangeId of contains) { - canonicalContains.add(rangeId) - } - - // Do not keep refs to document id we're removing - containsData.delete(id) -} - -/** - * Move the definition or reference data for document `id` into the definition or - * reference data of document `canonicalId`, then delete the reference to document - * `id.` - * - * @param id The id of the document to replace. - * @param canonicalId The id of the document to subsume the other. - * @param map The definitions or references map of the correlator. - */ -function mergeDefinitionReferences( - id: sqliteModels.DocumentId, - canonicalId: sqliteModels.DocumentId, - map: Map> -): void { - for (const value of map.values()) { - const data = value.get(id) - if (data !== undefined) { - for (const rangeId of data || []) { - value.getOrDefault(canonicalId).push(rangeId) - } - - // Do not keep refs to document id we're removing - value.delete(id) - } - } -} - -/** - * Determine which reference result sets are linked via item edges. Choose a canonical - * reference result from each batch. Merge all data into the canonical result and remove - * all non-canonical results from the correlator (note: this leave unlinked results alone). - * Return a map from reference result identifier to the identifier of the canonical result. - * - * @param correlator The correlator with all vertices and edges inserted. - */ -function canonicalizeReferenceResults( - correlator: Correlator -): Map { - const canonicalReferenceResultIds = new Map() - - for (const referenceResultId of correlator.linkedReferenceResults.keys()) { - // Don't re-process the same set of linked reference results - if (canonicalReferenceResultIds.has(referenceResultId)) { - continue - } - - // Find all reachable items and order them deterministically - const linkedIds = Array.from(correlator.linkedReferenceResults.extractSet(referenceResultId)) - linkedIds.sort() - - // Choose arbitrary canonical id - const canonicalId = linkedIds[0] - const canonicalReferenceResult = mustGet(correlator.referenceData, canonicalId, 'referenceResult') - - for (const linkedId of linkedIds) { - // Link each id to its canonical representation. We do this for - // the `linkedId === canonicalId` case so we can reliably detect - // duplication at the start of this loop. - - canonicalReferenceResultIds.set(linkedId, canonicalId) - - if (linkedId !== canonicalId) { - // If it's a different identifier, then normalize all data from the linked result - // set into the canonical one. - for (const [documentId, rangeIds] of mustGet(correlator.referenceData, linkedId, 'referenceResult')) { - canonicalReferenceResult.getOrDefault(documentId).push(...rangeIds) - } - } - } - } - - // Remove all non-canonical but linked result sets - const keys = new Set(canonicalReferenceResultIds.keys()) - const vals = new Set(canonicalReferenceResultIds.values()) - for (const key of keys) { - if (!vals.has(key)) { - correlator.referenceData.delete(key) - } - } - - return canonicalReferenceResultIds -} -/** - * Flatten the definition result, reference result, hover results, and monikers of range - * and result set items by following next links in the graph. This needs to be run over - * each range before committing them to a document. - * - * @param correlator The correlator with all vertices and edges inserted. - * @param canonicalReferenceResultIds A map from reference result identifiers to its canonical identifier. - * @param id The item identifier. - * @param item The range or result set item. - */ -function canonicalizeItem( - correlator: Correlator, - canonicalReferenceResultIds: Map, - id: lsif.RangeId | ResultSetId, - item: sqliteModels.RangeData | ResultSetData -): void { - const monikers = new Set() - if (item.monikerIds.size > 0) { - // Find arbitrary moniker attached to item - const candidateMoniker = item.monikerIds.keys().next().value - - // Get all monikers reachable from this one - for (const monikerId of correlator.linkedMonikers.extractSet(candidateMoniker)) { - if (mustGet(correlator.monikerData, monikerId, 'moniker').kind !== lsif.MonikerKind.local) { - monikers.add(monikerId) - } - } - } - - const nextId = correlator.nextData.get(id) - if (nextId !== undefined) { - // If we have a next edge to a result set, get it and canonicalize it first. This - // will recursively look at any result that that it can reach that hasn't yet been - // canonicalized. - - const nextItem = mustGet(correlator.resultSetData, nextId, 'resultSet') - canonicalizeItem(correlator, canonicalReferenceResultIds, nextId, nextItem) - - // Add each moniker of the next set to this item - for (const monikerId of nextItem.monikerIds) { - monikers.add(monikerId) - } - - // If we do not have a definition, reference, or hover result, take the result - // value from the next item. - - if (item.definitionResultId === undefined) { - item.definitionResultId = nextItem.definitionResultId - } - - if (item.referenceResultId === undefined) { - item.referenceResultId = nextItem.referenceResultId - } - - if (item.hoverResultId === undefined) { - item.hoverResultId = nextItem.hoverResultId - } - } - - if (item.referenceResultId && canonicalReferenceResultIds.has(item.referenceResultId)) { - // If there is a canonical version of this reference result, use that instead - item.referenceResultId = canonicalReferenceResultIds.get(item.referenceResultId) - } - - // Update our moniker sets (our normalized sets and any monikers of our next item) - item.monikerIds = monikers - - // Remove the next edge so we don't traverse it a second time - correlator.nextData.delete(id) -} - -/** - * Create a self-contained document object from the data in the given correlator. This - * includes hover and moniker results, as well as identifiers to definition and reference - * results (but not the actual ranges). See result chunk table for details. - * - * @param correlator The correlator with all vertices and edges inserted. - * @param currentDocumentId The identifier of the document. - * @param path The path of the document. - */ -function gatherDocument( - correlator: Correlator, - currentDocumentId: sqliteModels.DocumentId, - path: string -): sqliteModels.DocumentData { - const document = { - path, - ranges: new Map(), - hoverResults: new Map(), - monikers: new Map(), - packageInformation: new Map(), - } - - const addHover = (id: sqliteModels.HoverResultId | undefined): void => { - if (id === undefined || document.hoverResults.has(id)) { - return - } - - // Add hover result to the document, if defined and not a duplicate - const data = mustGet(correlator.hoverData, id, 'hoverResult') - document.hoverResults.set(id, data) - } - - const addPackageInformation = (id: sqliteModels.PackageInformationId | undefined): void => { - if (id === undefined || document.packageInformation.has(id)) { - return - } - - // Add package information to the document, if defined and not a duplicate - const data = mustGet(correlator.packageInformationData, id, 'packageInformation') - document.packageInformation.set(id, data) - } - - const addMoniker = (id: sqliteModels.MonikerId | undefined): void => { - if (id === undefined || document.monikers.has(id)) { - return - } - - // Add moniker to the document, if defined and not a duplicate - const moniker = mustGet(correlator.monikerData, id, 'moniker') - document.monikers.set(id, moniker) - - // Add related package information to document - addPackageInformation(moniker.packageInformationId) - } - - for (const id of mustGet(correlator.containsData, currentDocumentId, 'contains')) { - const range = mustGet(correlator.rangeData, id, 'range') - addHover(range.hoverResultId) - for (const monikerId of range.monikerIds) { - addMoniker(monikerId) - } - - document.ranges.set(id, range) - } - - return document -} - -/** - * Return the value of `id`, or throw an exception if it is undefined. - * - * @param id The identifier. - */ -function assertId(id: T | undefined): T { - if (id !== undefined) { - return id - } - - throw new Error('Id is undefined') -} diff --git a/cmd/precise-code-intel/src/worker/conversion/paths.ts b/cmd/precise-code-intel/src/worker/conversion/paths.ts deleted file mode 100644 index 2291247c698..00000000000 --- a/cmd/precise-code-intel/src/worker/conversion/paths.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as path from 'path' -import RelateUrl from 'relateurl' - -/** - * Return the dirname of the given path. Returns empty string - * if the path denotes a file in the current directory. - */ -export function dirnameWithoutDot(pathname: string): string { - return path.dirname(pathname) === '.' ? '' : path.dirname(pathname) -} - -/** - * Construct a root-relative path within a dump. - * - * @param projectRoot The absolute URI to the dump root. - * @param documentURI The absolute URI to the document. - */ -export function relativePath(projectRoot: URL, documentURI: URL): string { - return RelateUrl.relate(`${projectRoot.href}/`, documentURI.href, { - defaultPorts: {}, - output: RelateUrl.PATH_RELATIVE, - removeRootTrailingSlash: false, - }) -} diff --git a/cmd/precise-code-intel/src/worker/metrics.ts b/cmd/precise-code-intel/src/worker/metrics.ts deleted file mode 100644 index d81fb44303f..00000000000 --- a/cmd/precise-code-intel/src/worker/metrics.ts +++ /dev/null @@ -1,29 +0,0 @@ -import promClient from 'prom-client' - -// -// Upload Conversion Metrics - -export const uploadConversionDurationHistogram = new promClient.Histogram({ - name: 'lsif_upload_conversion_duration_seconds', - help: 'Total time spent on upload conversions.', - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -export const uploadConversionDurationErrorsCounter = new promClient.Counter({ - name: 'lsif_upload_conversion_errors_total', - help: 'The number of errors that occurred while converting an LSIF upload.', -}) - -// -// Importer Metric - -export const databaseInsertionDurationHistogram = new promClient.Histogram({ - name: 'lsif_database_insertion_duration_seconds', - help: 'Total time spent on database insertions.', - buckets: [0.2, 0.5, 1, 2, 5, 10, 30], -}) - -export const databaseInsertionErrorsCounter = new promClient.Counter({ - name: 'lsif_database_insertion_errors_total', - help: 'The number of errors that occurred during a database insertion.', -}) diff --git a/cmd/precise-code-intel/src/worker/settings.ts b/cmd/precise-code-intel/src/worker/settings.ts deleted file mode 100644 index 8b15061f546..00000000000 --- a/cmd/precise-code-intel/src/worker/settings.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { readEnvInt } from '../shared/settings' - -/** Which port to run the metrics server on. Defaults to 3188. */ -export const METRICS_PORT = readEnvInt('METRICS_PORT', 3188) - -/** HTTP address for internal precise code intel bundle manager server. */ -export const PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL = - process.env.PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL || 'http://localhost:3187' - -/** Where on the file system to temporarily store LSIF uploads and SQLite files. This is NOT a persistent volume. */ -export const STORAGE_ROOT = process.env.LSIF_STORAGE_ROOT || 'lsif-storage' - -/** The interval (in seconds) to poll the database for unconverted uploads. */ -export const POLLING_INTERVAL = readEnvInt('POLLING_INTERVAL', 1) - -/** - * The target results per result chunk. This is used to determine the number of chunks - * created during conversion, but does not guarantee that the distribution of hash keys - * will wbe even. In practice, chunks are fairly evenly filled. - */ -export const RESULTS_PER_RESULT_CHUNK = readEnvInt('RESULTS_PER_RESULT_CHUNK', 500) - -/** The maximum number of result chunks that will be created during conversion. */ -export const MAX_NUM_RESULT_CHUNKS = readEnvInt('MAX_NUM_RESULT_CHUNKS', 1000) diff --git a/cmd/precise-code-intel/src/worker/worker.ts b/cmd/precise-code-intel/src/worker/worker.ts deleted file mode 100644 index 96a4db98cf3..00000000000 --- a/cmd/precise-code-intel/src/worker/worker.ts +++ /dev/null @@ -1,173 +0,0 @@ -import * as metrics from './metrics' -import * as path from 'path' -import * as settings from './settings' -import promClient from 'prom-client' -import { addTags, logAndTraceCall, TracingContext } from '../shared/tracing' -import { createLogger } from '../shared/logging' -import { createPostgresConnection } from '../shared/database/postgres' -import { ensureDirectory } from '../shared/paths' -import { instrument } from '../shared/metrics' -import { Logger } from 'winston' -import { waitForConfiguration } from '../shared/config/config' -import { UploadManager } from '../shared/store/uploads' -import * as pgModels from '../shared/models/pg' -import { convertDatabase } from './conversion/conversion' -import { pick } from 'lodash' -import AsyncPolling from 'async-polling' -import { DumpManager } from '../shared/store/dumps' -import { DependencyManager } from '../shared/store/dependencies' -import { EntityManager } from 'typeorm' -import { SRC_FRONTEND_INTERNAL } from '../shared/config/settings' -import { updateCommitsAndDumpsVisibleFromTip } from '../shared/visibility' -import { startExpressApp } from '../shared/api/init' -import * as uuid from 'uuid' -import got from 'got' -import { pipeline as _pipeline } from 'stream' -import { promisify } from 'util' -import * as fs from 'mz/fs' - -const pipeline = promisify(_pipeline) - -/** - * Runs the worker process that converts LSIF uploads. - * - * @param logger The logger instance. - */ -async function main(logger: Logger): Promise { - // Collect process metrics - promClient.collectDefaultMetrics({ prefix: 'lsif_' }) - - // Read configuration from frontend - const fetchConfiguration = await waitForConfiguration(logger) - - // Ensure storage roots exist - await ensureDirectory(settings.STORAGE_ROOT) - - // Create database connection and entity wrapper classes - const connection = await createPostgresConnection(fetchConfiguration(), logger) - const dumpManager = new DumpManager(connection) - const uploadManager = new UploadManager(connection) - const dependencyManager = new DependencyManager(connection) - - // Start metrics server - startExpressApp({ port: settings.METRICS_PORT, logger }) - - const convert = async (upload: pgModels.LsifUpload, entityManager: EntityManager): Promise => { - logger.debug('Selected upload to convert', { uploadId: upload.id }) - - // Tag tracing context with uploadId and arguments - const ctx = addTags({ logger }, { uploadId: upload.id, ...pick(upload, 'repository', 'commit', 'root') }) - - await instrument( - metrics.uploadConversionDurationHistogram, - metrics.uploadConversionDurationErrorsCounter, - (): Promise => - logAndTraceCall(ctx, 'Converting upload', async (ctx: TracingContext) => { - const sourcePath = path.join(settings.STORAGE_ROOT, uuid.v4()) - const targetPath = path.join(settings.STORAGE_ROOT, uuid.v4()) - const url = new URL(`/uploads/${upload.id}`, settings.PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL).href - - try { - await logAndTraceCall(ctx, 'Downloading raw dump from bundle manager', () => - pipeline(got.stream.get(url), fs.createWriteStream(sourcePath)) - ) - - // Convert the database and populate the cross-dump package data - await convertDatabase( - entityManager, - dependencyManager, - SRC_FRONTEND_INTERNAL, - upload, - sourcePath, - targetPath, - ctx - ) - - // Upload the database where it cna be found by the server - await logAndTraceCall(ctx, 'Uploading converted dump to bundle manager', () => - pipeline( - fs.createReadStream(targetPath), - got.stream.post( - new URL(`/dbs/${upload.id}`, settings.PRECISE_CODE_INTEL_BUNDLE_MANAGER_URL).href - ) - ) - ) - - // Remove overlapping dumps that would cause a unique index error once this upload has - // transitioned into the completed state. As this is done in a transaction, we do not - // delete the files on disk right away. These files will be cleaned up by a worker in - // a future cleanup task. - await dumpManager.deleteOverlappingDumps( - upload.repositoryId, - upload.commit, - upload.root, - upload.indexer, - { logger }, - entityManager - ) - - // Update the conversion state after we've written the dump database file as the - // next step assumes that the processed upload is present in the dumps views. The - // remainder of the task may still fail, in which case the entire transaction is - // rolled back, so we don't want to commit yet. - await uploadManager.markComplete(upload, entityManager) - - // Update visibility flag for this repository. - await updateCommitsAndDumpsVisibleFromTip({ - entityManager, - dumpManager, - frontendUrl: SRC_FRONTEND_INTERNAL, - repositoryId: upload.repositoryId, - commit: upload.commit, - ctx, - }) - - logger.info('Converted upload', { - repositoryId: upload.repositoryId, - commit: upload.commit, - root: upload.root, - }) - } finally { - // Remove local files - await unlinkQuiet(sourcePath) - await unlinkQuiet(targetPath) - } - }) - ) - } - - logger.debug('Polling database for unconverted uploads') - - AsyncPolling(async end => { - while (await uploadManager.dequeueAndConvert(convert, logger)) { - // Immediately poll again if we converted an upload - } - - end() - }, settings.POLLING_INTERVAL * 1000).run() -} - -// Initialize logger -const appLogger = createLogger('precise-code-intel-worker') - -// Launch! -main(appLogger).catch(error => { - appLogger.error('Failed to start process', { error }) - appLogger.on('finish', () => process.exit(1)) - appLogger.end() -}) - -/** - * Unlink a file and swallow ENOENT exceptions. - * - * @param filename The path of the file to unlink. - */ -async function unlinkQuiet(filename: string): Promise { - try { - await fs.unlink(filename) - } catch (error) { - if (!(error && error.code === 'ENOENT')) { - throw error - } - } -} diff --git a/cmd/precise-code-intel/test-data/lsif-go@ad3507cb.lsif.gz b/cmd/precise-code-intel/test-data/lsif-go@ad3507cb.lsif.gz deleted file mode 100644 index c7dbab569133cd088b50a4821519a0849843b14e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 220125 zcmV)pK%2iGiwFoHb#h(+17vk=a4u|fX=VV-y?uA%Hj+2`|NRu)&eJD$;&zMgWY3xN zoSCGP>1QV|r#qR+y>ZU2Y|(CQWXU7S{jziCe)fw3K(Y!Xf21hOy|bG{mOv0ys45f+ zKUDqm$#U`J#S{O@*^}MJyFz_@S8R912le-Aep#&47q`W3{`Gt}S6`{$%H?LQzMP#$ z=b`%MZoB!f;%fJLv)QR{ua~Rh#q;OSe=b$S=f(AQx%=&=rF|9rBZ-_qFZqpgwn zPyY3<|LdQIlmL)oN2|X1eYvK_s;~ON5Ip$5ig(4DJ$G5$ENT4Z)kZ(ibi5!=(J1PE zBhc?+b9H}fdcNN-_vGO7<$70a*Ynl$o6YTJ@m&3Lzbei(e^&GL&HenQc;!+VA3!A= zttB&RI!>C7i^WaBy1m-0ck|`CR9|iGcYm{Uv|RtKeDdN~)x~dhA1N5zR#8N~mb>|O zM>4Tmu2sW0K6`TYcD|ioDPBBz;ivyPds5Kodm02;BPtnsT~Xezc5jMZLt)-a;kx*+ zYp6>Nb@~PM!~P73L2fBn`>p&yOoj~+)SYeGf|QjQ+^%Tp}*{6~c@Op(3kS3Inu+9V~@co7>y@deJ3;LBBpB31o~>LvMOR z%G7596Cq(O33Pr>+1SzA)FFUDJW>EP8J0ReNl4)P1u*EB`#}NJB-)WMRS2??0vIGr z02NvN0+=6D0E?~uXR{Tu7xt?Wl0D9ooH;;9g~pk`c)W4YFU-Yk{t>8Y2f zwSKex=+RHxVt2n?o92omclWz<@9%FFi^{wCRpBkwFWcqqZL#pKx0_qm%-e0en_}l} zmkV!mty;~ON*Zsxb@860vOM=L)@u8~6{+7GAVuO6oU-Xarl3cWLSOn3t|B?V$IaLy z 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 F831Rsz|H^w diff --git a/cmd/precise-code-intel/tsconfig.json b/cmd/precise-code-intel/tsconfig.json deleted file mode 100644 index b065236673a..00000000000 --- a/cmd/precise-code-intel/tsconfig.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "extends": "@sourcegraph/tsconfig", - "references": [], - "compilerOptions": { - "allowJs": false, - "allowSyntheticDefaultImports": true, - "composite": true, - "emitDecoratorMetadata": true, - "esModuleInterop": true, - "experimentalDecorators": true, - "importHelpers": true, - "inlineSourceMap": true, - "lib": ["esnext", "dom", "dom.iterable"], - "module": "commonjs", - "moduleResolution": "node", - "noErrorTruncation": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "outDir": "out", - "resolveJsonModule": true, - "rootDir": "src", - "skipLibCheck": true, - "strict": true, - "target": "es2019", - "baseUrl": "*", - "paths": { - "*": ["src/@types/*", "*"], - }, - }, - "files": [], - "include": ["src"], - "exclude": ["node_modules"], -} diff --git a/cmd/precise-code-intel/worker/Dockerfile b/cmd/precise-code-intel/worker/Dockerfile deleted file mode 100644 index 7afbae237e1..00000000000 --- a/cmd/precise-code-intel/worker/Dockerfile +++ /dev/null @@ -1,55 +0,0 @@ -# keep this in sync with docker-images/prometheus/Dockerfile -FROM prom/prometheus:v2.15.2@sha256:35c52c0c2b76433bbfc44a5a7abc294c2f032ed80250be02b441db5dd91b203a AS prometheus - -FROM alpine:3.10@sha256:e4355b66995c96b4b468159fc5c7e3540fcef961189ca13fee877798649f531a AS precise-code-intel-builder - -RUN apk add --no-cache \ - nodejs-current=12.4.0-r0 \ - nodejs-npm=10.19.0-r0 - -RUN npm install -g yarn@1.17.3 - -COPY precise-code-intel/package.json precise-code-intel/yarn.lock precise-code-intel/tsconfig.json /precise-code-intel/ -RUN yarn --cwd /precise-code-intel -COPY precise-code-intel/src /precise-code-intel/src -RUN yarn --cwd /precise-code-intel run build - -FROM sourcegraph/alpine:3.10@sha256:4d05cd5669726fc38823e92320659a6d1ef7879e62268adec5df658a0bacf65c - -ARG COMMIT_SHA="unknown" -ARG DATE="unknown" -ARG VERSION="unknown" - -LABEL org.opencontainers.image.revision=${COMMIT_SHA} -LABEL org.opencontainers.image.created=${DATE} -LABEL org.opencontainers.image.version=${VERSION} -LABEL com.sourcegraph.github.url=https://github.com/sourcegraph/sourcegraph/commit/${COMMIT_SHA} - -# environment variables used by startup -ENV PROMETHEUS_STORAGE_DIR=/prometheus -ENV PROMETHEUS_CONFIGURATION_DIR=/prometheus_config - -# hadolint ignore=DL3018 -RUN apk update && apk add --no-cache \ - nodejs-current=12.4.0-r0 \ - tini - -USER root -RUN mkdir -p $PROMETHEUS_STORAGE_DIR && chown -R sourcegraph:sourcegraph $PROMETHEUS_STORAGE_DIR -RUN mkdir -p $PROMETHEUS_CONFIGURATION_DIR && chown -R sourcegraph:sourcegraph $PROMETHEUS_CONFIGURATION_DIR -# Ensures that a directory with the correct permissions exist in the image. Without this, in Docker Compose -# deployments the Docker daemon would first create the volume directory and it would be owned by `root` and -# then one of the precise-code-intel processes would be unable to create the `/lsif-storage` because it -# would be trying to do so in a directory owned by `root` as the user `sourcegraph`. And no, this is not -# dumb, this is just Docker: https://github.com/docker/compose/issues/3270#issuecomment-363478501. -RUN mkdir -p /lsif-storage && chown -R sourcegraph:sourcegraph /lsif-storage -USER sourcegraph - -COPY --from=precise-code-intel-builder /precise-code-intel /precise-code-intel -COPY ./supervisor /usr/local/bin/supervisor -COPY --from=prometheus /bin/prometheus /bin/prometheus -COPY ./precise-code-intel/worker/prometheus.yml $PROMETHEUS_CONFIGURATION_DIR/prometheus.yml - -EXPOSE 3188 9090 -ENV GO111MODULES=on LANG=en_US.utf8 LOG_LEVEL=debug -ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/supervisor"] diff --git a/cmd/precise-code-intel/worker/README.md b/cmd/precise-code-intel/worker/README.md deleted file mode 100644 index 21b9a23a454..00000000000 --- a/cmd/precise-code-intel/worker/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Precise code intelligence worker - -The docker image for this part of the application wraps a one or more workers [goreman](https://github.com/mattn/goreman) supervisor. By default, there is are two worker processes. The number of workers can be tuned with the environment variable `NUM_WORKERS`. - -### Prometheus metrics - -The precise-code-intel-worker exposes a metrics server (but nothing else interesting) on port 3188. It's possible to run multiple workers, but impossible for them all to serve metrics from the same port. Therefore, this container also includes a minimally-configured Prometheus process that will scrape metrics from all of the processes. It is suggested that you use [federation](https://prometheus.io/docs/prometheus/latest/federation/) to scrape all of the process metrics at once instead of scraping the individual ports directly. Doing so will ensure that scaling up or down the number of workers will not change the the required Prometheus configuration. diff --git a/cmd/precise-code-intel/worker/build.sh b/cmd/precise-code-intel/worker/build.sh deleted file mode 100755 index 2cdb2c9d9d7..00000000000 --- a/cmd/precise-code-intel/worker/build.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -cd "$(dirname "${BASH_SOURCE[0]}")/../../.." -set -eux - -OUTPUT=$(mktemp -d -t sgdockerbuild_XXXXXXX) -cleanup() { - rm -rf "$OUTPUT" -} -trap cleanup EXIT - -# Environment for building linux binaries -export GO111MODULE=on -export GOARCH=amd64 -export GOOS=linux -export CGO_ENABLED=0 - -cp -a ./cmd/precise-code-intel "$OUTPUT" - -echo "--- go build" -go build \ - -trimpath \ - -ldflags "-X github.com/sourcegraph/sourcegraph/internal/version.version=$VERSION" \ - -o "$OUTPUT/supervisor" github.com/sourcegraph/sourcegraph/cmd/precise-code-intel/worker - -echo "--- docker build" -docker build -f cmd/precise-code-intel/worker/Dockerfile -t "$IMAGE" "$OUTPUT" \ - --progress=plain \ - --build-arg COMMIT_SHA \ - --build-arg DATE \ - --build-arg VERSION diff --git a/cmd/precise-code-intel/worker/main.go b/cmd/precise-code-intel/worker/main.go deleted file mode 100644 index ea1d3f27e50..00000000000 --- a/cmd/precise-code-intel/worker/main.go +++ /dev/null @@ -1,86 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "log" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/sourcegraph/sourcegraph/internal/env" - "github.com/sourcegraph/sourcegraph/internal/goreman" -) - -var ( - workers = env.Get("NUM_WORKERS", "2", "the number of worker instances to run (defaults to one)") - - // Set in docker image - prometheusStorageDir = os.Getenv("PROMETHEUS_STORAGE_DIR") - prometheusConfigurationDir = os.Getenv("PROMETHEUS_CONFIGURATION_DIR") -) - -const workerPort = 3188 - -func main() { - numWorkers, err := strconv.ParseInt(workers, 10, 64) - if err != nil || numWorkers < 0 { - log.Fatalf("Invalid int %q for NUM_WORKERS: %s", workers, err) - } - - if err := ioutil.WriteFile( - filepath.Join(prometheusConfigurationDir, "targets.yml"), - []byte(makePrometheusTargets(numWorkers)), - 0644, - ); err != nil { - log.Fatalf("Writing prometheus config: %v", err) - } - - // This mirrors the behavior from cmd/start - if err := goreman.Start([]byte(makeProcfile(numWorkers)), goreman.Options{ - RPCAddr: "127.0.0.1:5005", - ProcDiedAction: goreman.Shutdown, - }); err != nil { - log.Fatalf("Starting goreman: %v", err) - } -} - -func makeProcfile(numWorkers int64) string { - procfile := []string{} - addProcess := func(name, command string) { - procfile = append(procfile, fmt.Sprintf("%s: %s", name, command)) - } - - for i := 0; i < int(numWorkers); i++ { - addProcess( - fmt.Sprintf("worker-%d", i), - fmt.Sprintf("env METRICS_PORT=%d node /precise-code-intel/out/worker/worker.js", workerPort+i), - ) - } - - addProcess("prometheus", fmt.Sprintf("prometheus '--storage.tsdb.path=%s' '--config.file=%s/prometheus.yml'", - prometheusStorageDir, - prometheusConfigurationDir, - )) - - return strings.Join(procfile, "\n") + "\n" -} - -func makePrometheusTargets(numWorkers int64) string { - content := []string{"---"} - addTarget := func(job string, port int) { - content = append(content, - "- labels:", - fmt.Sprintf(" job: %s", job), - " targets:", - fmt.Sprintf(" - 127.0.0.1:%d", port), - ) - } - - for i := 0; i < int(numWorkers); i++ { - addTarget("worker", workerPort+i) - } - - return strings.Join(content, "\n") + "\n" -} diff --git a/cmd/precise-code-intel/worker/prometheus.yml b/cmd/precise-code-intel/worker/prometheus.yml deleted file mode 100644 index d1f34c21933..00000000000 --- a/cmd/precise-code-intel/worker/prometheus.yml +++ /dev/null @@ -1,10 +0,0 @@ -global: - scrape_interval: 30s # Scrape services for updated metrics every 30s. Default is 1m. - evaluation_interval: 30s # Evaluate rules every 30s. Default is 1m. - # scrape_timeout is set to the global default (10s). - -scrape_configs: - - job_name: lsif - file_sd_configs: - - files: - - targets.yml diff --git a/cmd/precise-code-intel/yarn.lock b/cmd/precise-code-intel/yarn.lock deleted file mode 100644 index 1dafb378523..00000000000 --- a/cmd/precise-code-intel/yarn.lock +++ /dev/null @@ -1,5777 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== - dependencies: - "@babel/highlight" "^7.8.3" - -"@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz#d496799e5c12195b3602d0fddd77294e3e38e80e" - integrity sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.4" - "@babel/helpers" "^7.8.4" - "@babel/parser" "^7.8.4" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.4" - "@babel/types" "^7.8.3" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.0" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/generator@^7.8.4": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e" - integrity sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA== - dependencies: - "@babel/types" "^7.8.3" - jsesc "^2.5.1" - lodash "^4.17.13" - source-map "^0.5.0" - -"@babel/helper-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" - integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA== - dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-get-function-arity@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" - integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" - integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== - -"@babel/helper-split-export-declaration@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" - integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helpers@^7.8.4": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73" - integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w== - dependencies: - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.4" - "@babel/types" "^7.8.3" - -"@babel/highlight@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" - integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^4.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.7.5", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8" - integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw== - -"@babel/plugin-syntax-bigint@^7.0.0": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-object-rest-spread@^7.0.0": - version "7.2.0" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" - integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - -"@babel/template@^7.7.4", "@babel/template@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" - integrity sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.4": - version "7.8.4" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c" - integrity sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.4" - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.8.4" - "@babel/types" "^7.8.3" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.8.3": - version "7.8.3" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" - integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg== - dependencies: - esutils "^2.0.2" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - -"@cnakazawa/watch@^1.0.3": - version "1.0.3" - resolved "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" - integrity sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA== - dependencies: - exec-sh "^0.3.2" - minimist "^1.2.0" - -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b" - integrity sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg== - dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" - -"@istanbuljs/schema@^0.1.2": - version "0.1.2" - resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" - integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== - -"@jest/console@^25.2.6": - version "25.2.6" - resolved "https://registry.npmjs.org/@jest/console/-/console-25.2.6.tgz#f594847ec8aef3cf27f448abe97e76e491212e97" - integrity sha512-bGp+0PicZVCEhb+ifnW9wpKWONNdkhtJsRE7ap729hiAfTvCN6VhGx0s/l/V/skA2pnyqq+N/7xl9ZWfykDpsg== - dependencies: - "@jest/source-map" "^25.2.6" - chalk "^3.0.0" - jest-util "^25.2.6" - slash "^3.0.0" - -"@jest/core@^25.2.7": - version "25.2.7" - resolved "https://registry.npmjs.org/@jest/core/-/core-25.2.7.tgz#58d697687e94ee644273d15e4eed6a20e27187cd" - integrity sha512-Nd6ELJyR+j0zlwhzkfzY70m04hAur0VnMwJXVe4VmmD/SaQ6DEyal++ERQ1sgyKIKKEqRuui6k/R0wHLez4P+g== - dependencies: - "@jest/console" "^25.2.6" - "@jest/reporters" "^25.2.6" - "@jest/test-result" "^25.2.6" - "@jest/transform" "^25.2.6" - "@jest/types" "^25.2.6" - ansi-escapes "^4.2.1" - chalk "^3.0.0" - exit "^0.1.2" - graceful-fs "^4.2.3" - jest-changed-files "^25.2.6" - jest-config "^25.2.7" - jest-haste-map "^25.2.6" - jest-message-util "^25.2.6" - jest-regex-util "^25.2.6" - jest-resolve "^25.2.6" - jest-resolve-dependencies "^25.2.7" - jest-runner "^25.2.7" - jest-runtime "^25.2.7" - jest-snapshot "^25.2.7" - jest-util "^25.2.6" - jest-validate "^25.2.6" - jest-watcher "^25.2.7" - micromatch "^4.0.2" - p-each-series "^2.1.0" - realpath-native "^2.0.0" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/environment@^25.2.6": - version "25.2.6" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-25.2.6.tgz#8f7931e79abd81893ce88b7306f0cc4744835000" - integrity sha512-17WIw+wCb9drRNFw1hi8CHah38dXVdOk7ga9exThhGtXlZ9mK8xH4DjSB9uGDGXIWYSHmrxoyS6KJ7ywGr7bzg== - dependencies: - "@jest/fake-timers" "^25.2.6" - "@jest/types" "^25.2.6" - jest-mock "^25.2.6" - -"@jest/fake-timers@^25.2.6": - version "25.2.6" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.2.6.tgz#239dbde3f56badf7d05bcf888f5d669296077cad" - integrity sha512-A6qtDIA2zg/hVgUJJYzQSHFBIp25vHdSxW/s4XmTJAYxER6eL0NQdQhe4+232uUSviKitubHGXXirt5M7blPiA== - dependencies: - "@jest/types" "^25.2.6" - jest-message-util "^25.2.6" - jest-mock "^25.2.6" - jest-util "^25.2.6" - lolex "^5.0.0" - -"@jest/reporters@^25.2.6": - version "25.2.6" - resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-25.2.6.tgz#6d87e40fb15adb69e22bb83aa02a4d88b2253b5f" - integrity sha512-DRMyjaxcd6ZKctiXNcuVObnPwB1eUs7xrUVu0J2V0p5/aZJei5UM9GL3s/bmN4hRV8Mt3zXh+/9X2o0Q4ClZIA== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^25.2.6" - "@jest/test-result" "^25.2.6" - "@jest/transform" "^25.2.6" - "@jest/types" "^25.2.6" - chalk "^3.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.2" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.0" - jest-haste-map "^25.2.6" - jest-resolve "^25.2.6" - jest-util "^25.2.6" - jest-worker "^25.2.6" - slash "^3.0.0" - source-map "^0.6.0" - string-length "^3.1.0" - terminal-link "^2.0.0" - v8-to-istanbul "^4.0.1" - optionalDependencies: - node-notifier "^6.0.0" - -"@jest/source-map@^25.2.6": - version "25.2.6" - resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.6.tgz#0ef2209514c6d445ebccea1438c55647f22abb4c" - integrity sha512-VuIRZF8M2zxYFGTEhkNSvQkUKafQro4y+mwUxy5ewRqs5N/ynSFUODYp3fy1zCnbCMy1pz3k+u57uCqx8QRSQQ== - dependencies: - callsites "^3.0.0" - graceful-fs "^4.2.3" - source-map "^0.6.0" - -"@jest/test-result@^25.2.6": - version "25.2.6" - resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.6.tgz#f6082954955313eb96f6cabf9fb14f8017826916" - integrity sha512-gmGgcF4qz/pkBzyfJuVHo2DA24kIgVQ5Pf/VpW4QbyMLSegi8z+9foSZABfIt5se6k0fFj/3p/vrQXdaOgit0w== - dependencies: - "@jest/console" "^25.2.6" - "@jest/types" "^25.2.6" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^25.2.7": - version "25.2.7" - resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.2.7.tgz#e4331f7b4850e34289b9a5c8ec8a2c03b400da8f" - integrity sha512-s2uYGOXONDSTJQcZJ9A3Zkg3hwe53RlX1HjUNqjUy3HIqwgwCKJbnAKYsORPbhxXi3ARMKA7JNBi9arsTxXoYw== - dependencies: - "@jest/test-result" "^25.2.6" - jest-haste-map "^25.2.6" - jest-runner "^25.2.7" - jest-runtime "^25.2.7" - -"@jest/transform@^25.2.6": - version "25.2.6" - resolved "https://registry.npmjs.org/@jest/transform/-/transform-25.2.6.tgz#007fd946dedf12d2a9eb5d4154faf1991d5f61ff" - integrity sha512-rZnjCjZf9avPOf9q/w9RUZ9Uc29JmB53uIXNJmNz04QbDMD5cR/VjfikiMKajBsXe2vnFl5sJ4RTt+9HPicauQ== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^25.2.6" - babel-plugin-istanbul "^6.0.0" - chalk "^3.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.3" - jest-haste-map "^25.2.6" - jest-regex-util "^25.2.6" - jest-util "^25.2.6" - micromatch "^4.0.2" - pirates "^4.0.1" - realpath-native "^2.0.0" - slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" - -"@jest/types@^25.2.6": - version "25.2.6" - resolved "https://registry.npmjs.org/@jest/types/-/types-25.2.6.tgz#c12f44af9bed444438091e4b59e7ed05f8659cb6" - integrity sha512-myJTTV37bxK7+3NgKc4Y/DlQ5q92/NOwZsZ+Uch7OXdElxOg61QYc72fPYNAjlvbnJ2YvbXLamIsa9tj48BmyQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^15.0.0" - chalk "^3.0.0" - -"@sindresorhus/is@^2.0.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.0.tgz#6ad4ca610f696098e92954ab431ff83bea0ce13f" - integrity sha512-lXKXfypKo644k4Da4yXkPCrwcvn6SlUW2X2zFbuflKHNjf0w9htru01bo26uMhleMXsDmnZ12eJLdrAZa9MANg== - -"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0": - version "1.7.0" - resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.0.tgz#f90ffc52a2e519f018b13b6c4da03cbff36ebed6" - integrity sha512-qbk9AP+cZUsKdW1GJsBpxPKFmCJ0T8swwzVje3qFd+AkQb74Q/tiuzrdfFg8AD2g5HH/XbE/I8Uc1KYHVYWfhg== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^6.0.0": - version "6.0.0" - resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.0.tgz#b64b0faadfdd01a6dcf0c4dcdb78438d86fa7dbf" - integrity sha512-atR1J/jRXvQAb47gfzSK8zavXy7BcpnYq21ALon0U99etu99vsir0trzIO3wpeLtW+LLVY6X7EkfVTbjGSH8Ww== - dependencies: - "@sinonjs/commons" "^1.7.0" - -"@sinonjs/formatio@^5.0.1": - version "5.0.1" - resolved "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089" - integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ== - dependencies: - "@sinonjs/commons" "^1" - "@sinonjs/samsam" "^5.0.2" - -"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.0.3": - version "5.0.3" - resolved "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.0.3.tgz#86f21bdb3d52480faf0892a480c9906aa5a52938" - integrity sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ== - dependencies: - "@sinonjs/commons" "^1.6.0" - lodash.get "^4.4.2" - type-detect "^4.0.8" - -"@sinonjs/text-encoding@^0.7.1": - version "0.7.1" - resolved "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" - integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== - -"@sourcegraph/tsconfig@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@sourcegraph/tsconfig/-/tsconfig-4.0.1.tgz#5965ec41771d2ac5b23b6e0d919cee3e70485840" - integrity sha512-G/xsejsR84G5dj3kHJ7svKBo9E5tWl96rUHKP94Y2UDtA7BzUhAYbieM+b9ZUpIRt66h3+MlYbG5HK4UI2zDzw== - -"@szmarczak/http-timer@^4.0.0": - version "4.0.5" - resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" - integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== - dependencies: - defer-to-connect "^2.0.0" - -"@types/async-polling@0.0.3": - version "0.0.3" - resolved "https://registry.npmjs.org/@types/async-polling/-/async-polling-0.0.3.tgz#e7a7b1553f6d9a8e6dcb943ae9645d8fb18fbe61" - integrity sha512-SxW+bavddNMXQfTlp3loXeh1BXc4WJ2CbhN/SIwx1jvJlPDFcifDQ1bJwOLcwlt/D+zjfsEWdxVy4UPPbnLR9A== - -"@types/babel__core@^7.1.0": - version "7.1.3" - resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30" - integrity sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.0" - resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.0.tgz#f1ec1c104d1bb463556ecb724018ab788d0c172a" - integrity sha512-c1mZUu4up5cp9KROs/QAw0gTeHrw/x7m52LcnvMxxOZ03DmLwPV0MlGmlgzV3cnSdjhJOZsj7E7FHeioai+egw== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.0.2" - resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" - integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.0.7" - resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz#2496e9ff56196cc1429c72034e07eab6121b6f3f" - integrity sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw== - dependencies: - "@babel/types" "^7.3.0" - -"@types/bloomfilter@0.0.0": - version "0.0.0" - resolved "https://registry.npmjs.org/@types/bloomfilter/-/bloomfilter-0.0.0.tgz#fbfdf4fbf1d9f4775ee0ef952e58b138e5dae423" - integrity sha512-bZhGgidbIsN1sgI3tLwyxTpSa9Dkd9V5MrDU/O92OiqqEN+CNOxbyzPYAFOoBFw4VAh2EdzqT4XucKZEkrI0JQ== - -"@types/body-parser@*", "@types/body-parser@1.19.0": - version "1.19.0" - resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" - integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/cacheable-request@^6.0.1": - version "6.0.1" - resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" - integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "*" - "@types/node" "*" - "@types/responselike" "*" - -"@types/color-name@^1.1.1": - version "1.1.1" - resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" - integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== - -"@types/connect@*": - version "3.4.32" - resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28" - integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg== - dependencies: - "@types/node" "*" - -"@types/events@*": - version "3.0.0" - resolved "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" - integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== - -"@types/express-serve-static-core@*": - version "4.17.2" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz#f6f41fa35d42e79dbf6610eccbb2637e6008a0cf" - integrity sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg== - dependencies: - "@types/node" "*" - "@types/range-parser" "*" - -"@types/express-winston@3.0.4": - version "3.0.4" - resolved "https://registry.npmjs.org/@types/express-winston/-/express-winston-3.0.4.tgz#0818399a094374f16c39bcba24ebcea3891791c6" - integrity sha512-fCvI4nTs/oYhq7jACnPurepOPMKz//bNuCwMIRCcESix9jEuky93H5KwkyKeEPGNZfQzR3zCUbeGWs6/jvV7Fg== - dependencies: - "@types/express" "*" - "@types/logform" "*" - winston "^3.0.0" - -"@types/express@*", "@types/express@4.17.4": - version "4.17.4" - resolved "https://registry.npmjs.org/@types/express/-/express-4.17.4.tgz#e78bf09f3f530889575f4da8a94cd45384520aac" - integrity sha512-DO1L53rGqIDUEvOjJKmbMEQ5Z+BM2cIEPy/eV3En+s166Gz+FeuzRerxcab757u/U4v4XF4RYrZPmqKa+aY/2w== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "*" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/glob@*": - version "7.1.1" - resolved "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" - integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== - dependencies: - "@types/events" "*" - "@types/minimatch" "*" - "@types/node" "*" - -"@types/got@9.6.10": - version "9.6.10" - resolved "https://registry.npmjs.org/@types/got/-/got-9.6.10.tgz#5f34e9f249a13e06cfe0015b08f55b4b114bb645" - integrity sha512-owBY1cgHUIXjObzY+vs+J9Cpw0czvfksJX+qEkgxRojFutFq7n1tKoj6Ekg57DhvXMk0vGQ7FbinvS9I/1wxcg== - dependencies: - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.0" - -"@types/http-cache-semantics@*": - version "4.0.0" - resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" - integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.1" - resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" - integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== - -"@types/istanbul-lib-report@*": - version "1.1.1" - resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#e5471e7fa33c61358dd38426189c037a58433b8c" - integrity sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^1.1.1": - version "1.1.1" - resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a" - integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA== - dependencies: - "@types/istanbul-lib-coverage" "*" - "@types/istanbul-lib-report" "*" - -"@types/jaeger-client@3.15.3": - version "3.15.3" - resolved "https://registry.npmjs.org/@types/jaeger-client/-/jaeger-client-3.15.3.tgz#d9bee71dc7441c46b1e1e06826f657c6859de03d" - integrity sha512-Hby2o9/oj3KzeVwoSzP/A7fA2jGfpnxEsZISZvsk75ZckqIJJovx7rAwSoj22yGAyu3vGo43Ad0K/YImEO1dXw== - dependencies: - opentracing "~0.14.3" - prom-client "~11.3.0" - -"@types/jest@25.2.1": - version "25.2.1" - resolved "https://registry.npmjs.org/@types/jest/-/jest-25.2.1.tgz#9544cd438607955381c1bdbdb97767a249297db5" - integrity sha512-msra1bCaAeEdkSyA0CZ6gW1ukMIvZ5YoJkdXw/qhQdsuuDlFTcEUrUw8CLCPt2rVRUfXlClVvK2gvPs9IokZaA== - dependencies: - jest-diff "^25.2.1" - pretty-format "^25.2.1" - -"@types/json-schema@^7.0.3": - version "7.0.3" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" - integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== - -"@types/json5@0.0.30": - version "0.0.30" - resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.30.tgz#44cb52f32a809734ca562e685c6473b5754a7818" - integrity sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA== - -"@types/keyv@*": - version "3.1.1" - resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" - integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== - dependencies: - "@types/node" "*" - -"@types/lodash@4.14.150": - version "4.14.150" - resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.150.tgz#649fe44684c3f1fcb6164d943c5a61977e8cf0bd" - integrity sha512-kMNLM5JBcasgYscD9x/Gvr6lTAv2NVgsKtet/hm93qMyf/D1pt+7jeEZklKJKxMVmXjxbRVQQGfqDSfipYCO6w== - -"@types/logform@*", "@types/logform@1.2.0": - version "1.2.0" - resolved "https://registry.npmjs.org/@types/logform/-/logform-1.2.0.tgz#4ead916c7eb1ee99d726bfa849b6a2ee5ea50e3e" - integrity sha512-E8cLzW+PmqHI//4HLR+ZvE3cyzqVjsncYBx1O14P07oVJj3ezhmiL/azlgkXKLFyCWAeKsPQdjHNg/NEhBF5Ow== - -"@types/mime@*": - version "2.0.1" - resolved "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" - integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw== - -"@types/minimatch@*": - version "3.0.3" - resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== - -"@types/mz@2.7.0": - version "2.7.0" - resolved "https://registry.npmjs.org/@types/mz/-/mz-2.7.0.tgz#3ef27f457c4c3e8b197ca2670ee41d6f4effddf2" - integrity sha512-Q5TZYMKnH0hdV5fNstmMWL2LLw5eRRtTd73KNtsZxoQ2gtCQyET5X79uERUEwGneuxPglg441I7OSY00+9CkSw== - dependencies: - "@types/node" "*" - -"@types/node@*": - version "12.7.11" - resolved "https://registry.npmjs.org/@types/node/-/node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446" - integrity sha512-Otxmr2rrZLKRYIybtdG/sgeO+tHY20GxeDjcGmUnmmlCWyEnv2a2x1ZXBo3BTec4OiTXMQCiazB8NMBf0iRlFw== - -"@types/on-finished@2.3.1": - version "2.3.1" - resolved "https://registry.npmjs.org/@types/on-finished/-/on-finished-2.3.1.tgz#4537f9f2b47b3ba0b92c14a4bcc0f755aeda3484" - integrity sha512-mzVYaYcFs5Jd2n/O6uYIRUsFRR1cHyZLRvkLCU0E7+G5WhY0qBDAR5fUCeZbvecYOSh9ikhlesyi2UfI8B9ckQ== - dependencies: - "@types/node" "*" - -"@types/prettier@^1.19.0": - version "1.19.1" - resolved "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" - integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== - -"@types/qs@*": - version "6.9.1" - resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.1.tgz#937fab3194766256ee09fcd40b781740758617e7" - integrity sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw== - -"@types/range-parser@*": - version "1.2.3" - resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" - integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== - -"@types/relateurl@0.2.28": - version "0.2.28" - resolved "https://registry.npmjs.org/@types/relateurl/-/relateurl-0.2.28.tgz#6bda7db8653fa62643f5ee69e9f69c11a392e3a6" - integrity sha1-a9p9uGU/piZD9e5p6facEaOS46Y= - -"@types/responselike@*": - version "1.0.0" - resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" - integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== - dependencies: - "@types/node" "*" - -"@types/retry@^0.12.0": - version "0.12.0" - resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== - -"@types/rimraf@*": - version "2.0.2" - resolved "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e" - integrity sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ== - dependencies: - "@types/glob" "*" - "@types/node" "*" - -"@types/rmfr@2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@types/rmfr/-/rmfr-2.0.0.tgz#38236d832291c5c14a43f972d9aa8b25bf0f81f4" - integrity sha512-gDyAn7J4d9kIFHtnNWy8J9hI3W6810brUCukkBxAS0UyMnAv9vMeFqx66WJHMPZwW6h7Sl4/njhQBxOTIosoxA== - dependencies: - "@types/rimraf" "*" - -"@types/serve-static@*": - version "1.13.3" - resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz#eb7e1c41c4468272557e897e9171ded5e2ded9d1" - integrity sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g== - dependencies: - "@types/express-serve-static-core" "*" - "@types/mime" "*" - -"@types/sinon@9.0.0": - version "9.0.0" - resolved "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.0.tgz#5b70a360f55645dd64f205defd2a31b749a59799" - integrity sha512-v2TkYHkts4VXshMkcmot/H+ERZ2SevKa10saGaJPGCJ8vh3lKrC4u663zYEeRZxep+VbG6YRDtQ6gVqw9dYzPA== - dependencies: - "@types/sinonjs__fake-timers" "*" - -"@types/sinonjs__fake-timers@*": - version "6.0.1" - resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz#681df970358c82836b42f989188d133e218c458e" - integrity sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA== - -"@types/stack-utils@^1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" - integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== - -"@types/stream-throttle@0.1.0": - version "0.1.0" - resolved "https://registry.npmjs.org/@types/stream-throttle/-/stream-throttle-0.1.0.tgz#8807746990431b68ec897e86cf14832f3025950e" - integrity sha512-rwH5ZUkJwOMvy+7VJoz8CoQbBGfTGmhHgYRaSW4iwLfX0sFut07E/8ldszrh7AN4J0vfuGrWvL/tQPPx5o2AkA== - dependencies: - "@types/node" "*" - -"@types/tough-cookie@*": - version "2.3.5" - resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz#9da44ed75571999b65c37b60c9b2b88db54c585d" - integrity sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg== - -"@types/triple-beam@1.3.0": - version "1.3.0" - resolved "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.0.tgz#56d9a2d9e4fbd7bc363825d2521868537e4606ee" - integrity sha512-tl34wMtk3q+fSdRSJ+N83f47IyXLXPPuLjHm7cmAx0fE2Wml2TZCQV3FmQdSR5J6UEGV3qafG054e0cVVFCqPA== - -"@types/uuid@7.0.3": - version "7.0.3" - resolved "https://registry.npmjs.org/@types/uuid/-/uuid-7.0.3.tgz#45cd03e98e758f8581c79c535afbd4fc27ba7ac8" - integrity sha512-PUdqTZVrNYTNcIhLHkiaYzoOIaUi5LFg/XLerAdgvwQrUCx+oSbtoBze1AMyvYbcwzUSNC+Isl58SM4Sm/6COw== - -"@types/yallist@3.0.1": - version "3.0.1" - resolved "https://registry.npmjs.org/@types/yallist/-/yallist-3.0.1.tgz#3f43dac55d779e3058ba10334659d9fbb373807c" - integrity sha512-nky1uSaFye8rZctRgEd+hC0rNZuDDH49DwOHdSIvgiRZPERVvcEFfV3kUReE60GMFNjG1z8x9ncSb+WKj3cMcA== - -"@types/yargs-parser@*": - version "13.1.0" - resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228" - integrity sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg== - -"@types/yargs@^15.0.0": - version "15.0.3" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.3.tgz#41453a0bc7ab393e995d1f5451455638edbd2baf" - integrity sha512-XCMQRK6kfpNBixHLyHUsGmXrpEmFFxzMrcnSXFMziHd8CoNJo8l16FkHyQq4x+xbM7E2XL83/O78OD8u+iZTdQ== - dependencies: - "@types/yargs-parser" "*" - -abab@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/abab/-/abab-2.0.2.tgz#a2fba1b122c69a85caa02d10f9270c7219709a9d" - integrity sha512-2scffjvioEmNz0OyDSLGWDfKCVwaKc6l9Pm9kOIREU13ClXZvHpg/nRL5xyjSSSLhOnXqft2HpsAzNEEA8cFFg== - -abbrev@1: - version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - -acorn-globals@^4.3.2: - version "4.3.4" - resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" - integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== - dependencies: - acorn "^6.0.1" - acorn-walk "^6.0.1" - -acorn-walk@^6.0.1: - version "6.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" - integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== - -acorn@^6.0.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" - integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== - -acorn@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" - integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== - -ajv@^6.5.5: - version "6.10.2" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" - integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== - dependencies: - fast-deep-equal "^2.0.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-color@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/ansi-color/-/ansi-color-0.2.1.tgz#3e75c037475217544ed763a8db5709fa9ae5bf9a" - integrity sha1-PnXAN0dSF1RO12Oo21cJ+prlv5o= - -ansi-escapes@^4.2.1: - version "4.3.0" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" - integrity sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg== - dependencies: - type-fest "^0.8.1" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" - integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== - dependencies: - "@types/color-name" "^1.1.1" - color-convert "^2.0.1" - -any-promise@^1.0.0: - version "1.3.0" - resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -anymatch@^3.0.3: - version "3.1.1" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -app-root-path@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" - integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== - -append-type@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/append-type/-/append-type-1.0.2.tgz#a492f350e81ddcb46b787fc605becf6dd8bccbf6" - integrity sha512-hac740vT/SAbrFBLgLIWZqVT5PUAcGTWS5UkDDhr+OCizZSw90WKw6sWAEgGaYd2viIblggypMXwpjzHXOvAQg== - -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" - integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -array-includes@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" - integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" - -array-to-sentence@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/array-to-sentence/-/array-to-sentence-1.1.0.tgz#c804956dafa53232495b205a9452753a258d39fc" - integrity sha1-yASVba+lMjJJWyBalFJ1OiWNOfw= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -array.prototype.flat@^1.2.1: - version "1.2.3" - resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" - integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -assert-valid-glob-opts@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assert-valid-glob-opts/-/assert-valid-glob-opts-1.0.0.tgz#ab9b5438ec5e929f5bb08201819affb1227f730a" - integrity sha512-/mttty5Xh7wE4o7ttKaUpBJl0l04xWe3y6muy1j27gyzSsnceK0AYU9owPtUoL9z8+9hnPxztmuhdFZ7jRoyWw== - dependencies: - glob-option-error "^1.0.0" - validate-glob-opts "^1.0.0" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - -async-middleware@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/async-middleware/-/async-middleware-1.2.1.tgz#d97bcd7124a6a2502c42fda21290cf5bb5d563e4" - integrity sha1-2XvNcSSmolAsQv2iEpDPW7XVY+Q= - -async-polling@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/async-polling/-/async-polling-0.2.1.tgz#366175d72dda951fb1c148d0d9edf7412cb02d78" - integrity sha1-NmF11y3alR+xwUjQ2e33QSywLXg= - -async@^2.6.1: - version "2.6.3" - resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -atob@^2.1.1: - version "2.1.2" - resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.8.0" - resolved "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" - integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== - -babel-jest@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-25.2.6.tgz#fe67ff4d0db3626ca8082da8881dd5e84e07ae75" - integrity sha512-MDJOAlwtIeIQiGshyX0d2PxTbV73xZMpNji40ivVTPQOm59OdRR9nYCkffqI7ugtsK4JR98HgNKbDbuVf4k5QQ== - dependencies: - "@jest/transform" "^25.2.6" - "@jest/types" "^25.2.6" - "@types/babel__core" "^7.1.0" - babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^25.2.6" - chalk "^3.0.0" - slash "^3.0.0" - -babel-plugin-istanbul@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" - integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^4.0.0" - test-exclude "^6.0.0" - -babel-plugin-jest-hoist@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.2.6.tgz#2af07632b8ac7aad7d414c1e58425d5fc8e84909" - integrity sha512-qE2xjMathybYxjiGFJg0mLFrz0qNp83aNZycWDY/SuHiZNq+vQfRQtuINqyXyue1ELd8Rd+1OhFSLjms8msMbw== - dependencies: - "@types/babel__traverse" "^7.0.6" - -babel-preset-jest@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.2.6.tgz#5d3f7c99e2a8508d61775c9d68506d143b7f71b5" - integrity sha512-Xh2eEAwaLY9+SyMt/xmGZDnXTW/7pSaBPG0EMo7EuhvosFKVWYB6CqwYD31DaEQuoTL090oDZ0FEqygffGRaSQ== - dependencies: - "@babel/plugin-syntax-bigint" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - babel-plugin-jest-hoist "^25.2.6" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -base64-js@^1.0.2: - version "1.3.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" - integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - 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" - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -bintrees@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz#0e655c9b9c2435eaab68bf4027226d2b55a34524" - integrity sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ= - -bloomfilter@^0.0.18: - version "0.0.18" - resolved "https://registry.npmjs.org/bloomfilter/-/bloomfilter-0.0.18.tgz#6d55d34f0a214b235287b4eac9203ac623413dab" - integrity sha512-CbnyHE78gY1tpXS/Ap+B0RJxKdRWCDzjBnX97UJSG8rdLv1PK8GiTWc/CCQyWu6PWVD4lUceeFrqC6Mf3nMgOA== - -body-parser@1.19.0, body-parser@^1.19.0: - version "1.19.0" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - 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" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1: - version "2.3.2" - resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - 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" - -braces@^3.0.1: - version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -browser-process-hrtime@^0.1.2: - version "0.1.3" - resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" - integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== - -browser-resolve@^1.11.3: - version "1.11.3" - resolved "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" - integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== - dependencies: - resolve "1.1.7" - -bser@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/bser/-/bser-2.1.0.tgz#65fc784bf7f87c009b973c12db6546902fa9c7b5" - integrity sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg== - dependencies: - node-int64 "^0.4.0" - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -buffer-writer@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" - integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== - -buffer@^5.1.0: - version "5.4.3" - resolved "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115" - integrity sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - -bufrw@^1.2.1: - version "1.3.0" - resolved "https://registry.npmjs.org/bufrw/-/bufrw-1.3.0.tgz#28d6cfdaf34300376836310f5c31d57eeb40c8fa" - integrity sha512-jzQnSbdJqhIltU9O5KUiTtljP9ccw2u5ix59McQy4pV2xGhVLhRZIndY8GIrgh5HjXa6+QJ9AQhOd2QWQizJFQ== - dependencies: - ansi-color "^0.2.1" - error "^7.0.0" - hexer "^1.5.0" - xtend "^4.0.0" - -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - 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" - -cacheable-lookup@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.0.tgz#33b1e56f17507f5cf9bb46075112d65473fb7713" - integrity sha512-s2piO6LvA7xnL1AR03wuEdSx3BZT3tIJpZ56/lcJwzO/6DTJZlTs7X3lrvPxk6d1PlDe6PrVe2TjlUIZNFglAQ== - dependencies: - keyv "^4.0.0" - -cacheable-request@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" - integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^4.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^2.0.0" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase@^5.0.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -capture-exit@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" - integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== - dependencies: - rsvp "^4.8.4" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@^1.1.1: - version "1.1.3" - resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chownr@^1.1.1: - version "1.1.3" - resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" - integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== - -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -cli-highlight@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.1.tgz#2180223d51618b112f4509cf96e4a6c750b07e97" - integrity sha512-0y0VlNmdD99GXZHYnvrQcmHxP8Bi6T00qucGgBgGv4kJ0RyDthNnnFPupHV7PYv/OXSVk+azFbOeaW6+vGmx9A== - dependencies: - chalk "^2.3.0" - highlight.js "^9.6.0" - mz "^2.4.0" - parse5 "^4.0.0" - yargs "^13.0.0" - -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== - dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.0" - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -collect-v8-coverage@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz#150ee634ac3650b71d9c985eb7f608942334feb1" - integrity sha512-VKIhJgvk8E1W28m5avZ2Gv2Ruv5YiF56ug2oclvaG9md69BuZImMG2sk9g7QNKLUbtYAKQjXjYxbYZVUlMMKmQ== - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0, color-convert@^1.9.1: - version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@^1.0.0, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@3.0.x: - version "3.0.0" - resolved "https://registry.npmjs.org/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a" - integrity sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w== - dependencies: - color-convert "^1.9.1" - color-string "^1.5.2" - -colornames@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz#f8889030685c7c4ff9e2a559f5077eb76a816f96" - integrity sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y= - -colors@^1.2.1: - version "1.4.0" - resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -colorspace@1.1.x: - version "1.1.2" - resolved "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz#e0128950d082b86a2168580796a0aa5d6c68d8c5" - integrity sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ== - dependencies: - color "3.0.x" - text-hex "1.0.x" - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@^2.2.0: - version "2.20.3" - resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" - integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= - -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -copyfiles@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/copyfiles/-/copyfiles-2.2.0.tgz#d9fc6c06f299337fb7eeb7ea5887e9d7188d9d47" - integrity sha512-iJbHJI+8OKqsq+4JF0rqgRkZzo++jqO6Wf4FUU1JM41cJF6JcY5968XyF4tm3Kkm7ZOMrqlljdm8N9oyY5raGw== - dependencies: - glob "^7.0.5" - minimatch "^3.0.3" - mkdirp "^0.5.1" - noms "0.0.0" - through2 "^2.0.1" - yargs "^13.2.4" - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -crc-32@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" - integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== - dependencies: - exit-on-epipe "~1.0.1" - printj "~1.1.0" - -cross-spawn@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.0: - version "7.0.1" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" - integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -cssom@^0.4.1: - version "0.4.4" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.2.0.tgz#e4c44debccd6b7911ed617a4395e5754bba59992" - integrity sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA== - dependencies: - cssom "~0.3.6" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -data-urls@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" - integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== - dependencies: - abab "^2.0.0" - whatwg-mimetype "^2.2.0" - whatwg-url "^7.0.0" - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.2.6: - version "3.2.6" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.0, debug@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -decompress-response@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz#7849396e80e3d1eba8cb2f75ef4930f76461cb0f" - integrity sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw== - dependencies: - mimic-response "^2.0.0" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= - -deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - -defer-to-connect@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1" - integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg== - -define-properties@^1.1.2, define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -delay@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/delay/-/delay-4.3.0.tgz#efeebfb8f545579cb396b3a722443ec96d14c50e" - integrity sha512-Lwaf3zVFDMBop1yDuFZ19F9WyGcZcGacsbdlZtWjQmM50tOcMntm1njF/Nb/Vjij3KaSvCF+sEYGKrrjObu2NA== - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -diagnostics@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz#cab6ac33df70c9d9a727490ae43ac995a769b22a" - integrity sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ== - dependencies: - colorspace "1.1.x" - enabled "1.0.x" - kuler "1.0.x" - -diff-sequences@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" - integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== - -diff@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - -domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== - dependencies: - webidl-conversions "^4.0.2" - -dotenv@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" - integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -duplexer@~0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" - integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -enabled@1.0.x: - version "1.0.2" - resolved "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93" - integrity sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M= - dependencies: - env-variable "0.0.x" - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -env-variable@0.0.x: - version "0.0.5" - resolved "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz#913dd830bef11e96a039c038d4130604eba37f88" - integrity sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA== - -error-ex@^1.2.0: - version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -error@7.0.2, error@^7.0.0: - version "7.0.2" - resolved "https://registry.npmjs.org/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02" - integrity sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI= - dependencies: - string-template "~0.2.1" - xtend "~4.0.0" - -es-abstract@^1.12.0, es-abstract@^1.17.0-next.1, es-abstract@^1.5.1, es-abstract@^1.7.0: - version "1.17.0" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1" - integrity sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug== - 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" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escodegen@^1.11.1: - version "1.13.0" - resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.13.0.tgz#c7adf9bd3f3cc675bb752f202f79a720189cab29" - integrity sha512-eYk2dCkxR07DsHA/X2hRBj0CFAZeri/LyDMc0C8JT1Hqi6JnVpMhJ7XFITbb0+yZS3lVkaPL2oCkZ3AVmeVbMw== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -eslint-import-resolver-node@^0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" - integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== - dependencies: - debug "^2.6.9" - resolve "^1.5.0" - -eslint-module-utils@^2.4.1: - version "2.5.0" - resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.0.tgz#cdf0b40d623032274ccd2abd7e64c4e524d6e19c" - integrity sha512-kCo8pZaNz2dsAW7nCUjuVoI11EBXXpIzfNxmaoLhXoRDOnqXLC4iSGVRdZPhOitfbdEfMEfKOiENaK6wDPZEGw== - dependencies: - debug "^2.6.9" - pkg-dir "^2.0.0" - -eslint-plugin-import@^2.20.2: - version "2.20.2" - resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz#91fc3807ce08be4837141272c8b99073906e588d" - integrity sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg== - dependencies: - array-includes "^3.0.3" - array.prototype.flat "^1.2.1" - contains-path "^0.1.0" - debug "^2.6.9" - doctrine "1.5.0" - eslint-import-resolver-node "^0.3.2" - eslint-module-utils "^2.4.1" - has "^1.0.3" - minimatch "^3.0.4" - object.values "^1.1.0" - read-pkg-up "^2.0.0" - resolve "^1.12.0" - -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -estraverse@^4.2.0: - version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -event-stream@=3.3.4: - version "3.3.4" - resolved "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" - integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= - dependencies: - duplexer "~0.1.1" - from "~0" - map-stream "~0.1.0" - pause-stream "0.0.11" - split "0.3" - stream-combiner "~0.0.4" - through "~2.3.1" - -exec-sh@^0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b" - integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg== - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - 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" - -execa@^3.2.0: - version "3.4.0" - resolved "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" - integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - p-finally "^2.0.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - -exit-on-epipe@~1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" - integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== - -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - 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" - -expect@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/expect/-/expect-25.2.7.tgz#509b79f47502835f4071ff3ecc401f2eaecca709" - integrity sha512-yA+U2Ph0MkMsJ9N8q5hs9WgWI6oJYfecdXta6LkP/alY/jZZL1MHlJ2wbLh60Ucqf3G+51ytbqV3mlGfmxkpNw== - dependencies: - "@jest/types" "^25.2.6" - ansi-styles "^4.0.0" - jest-get-type "^25.2.6" - jest-matcher-utils "^25.2.7" - jest-message-util "^25.2.6" - jest-regex-util "^25.2.6" - -express-opentracing@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/express-opentracing/-/express-opentracing-0.1.1.tgz#5a6244c5d4dd41117d1805922fb08d21328f7a4d" - integrity sha512-urVTRTn/Y9/oSjy/P+IOWOl2Db0MnLqS7qYicHWk7vbL6ld+TNRp0DRx5G7cK2mLQ+/Ovpt8c4WLyG97VmtEdA== - dependencies: - opentracing "^0.14.0" - -express-validator@^6.4.0: - version "6.4.0" - resolved "https://registry.npmjs.org/express-validator/-/express-validator-6.4.0.tgz#634f96b60d53112409e270c038ab818a36f56e47" - integrity sha512-Fs+x0yDOSiUV+o5jIRloMyBxqpSzJiMM8KQW1IRVv2l49F6ATU0F9uPa+3K6vXNlLlhUjauv2FCGLFPMaNr24w== - dependencies: - lodash "^4.17.15" - validator "^12.1.0" - -express-winston@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/express-winston/-/express-winston-4.0.3.tgz#7160ddc6eb8efaa1bfc95a246124b05286096699" - integrity sha512-qzLLaTYAhajzfbR1d/hKT+N4kEoqDXC9wBqth0ygPg+DrM4QRipXDHLkIkaKSeiQ1L/ulezrT+T7lrKS+OcT7g== - dependencies: - chalk "^2.4.1" - lodash "^4.17.15" - -express@^4.17.1: - version "4.17.1" - resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== - 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" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - 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" - -extsprintf@1.3.0, extsprintf@^1.2.0: - version "1.3.0" - resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= - -fast-levenshtein@~2.0.4: - version "2.0.6" - resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -fast-safe-stringify@^2.0.4: - version "2.0.7" - resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" - integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== - -fb-watchman@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" - integrity sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg= - dependencies: - bser "^2.0.0" - -fecha@^2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd" - integrity sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg== - -figlet@^1.1.1: - version "1.2.4" - resolved "https://registry.npmjs.org/figlet/-/figlet-1.2.4.tgz#2d2f48b61a77418ba6cbd9db2e25da27efee16d0" - integrity sha512-mv8YA9RruB4C5QawPaD29rEVx3N97ZTyNrE4DAfbhuo6tpcMdKnPVo8MlyT3RP5uPcg5M14bEJBq7kjFf4kAWg== - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - 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" - -find-up@^2.0.0, find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@^2.5.0: - version "2.5.1" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" - integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -from@~0: - version "0.1.7" - resolved "https://registry.npmjs.org/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" - integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= - -fs-minipass@^1.2.5: - version "1.2.7" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" - integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - 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" - -gensync@^1.0.0-beta.1: - version "1.0.0-beta.1" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" - integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== - -get-caller-file@^2.0.1: - version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.0.0, get-stream@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" - integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== - dependencies: - pump "^3.0.0" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -glob-option-error@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/glob-option-error/-/glob-option-error-1.0.0.tgz#57cc65def9c7d5c1461baf13129bb5403cff6176" - integrity sha1-V8xl3vnH1cFGG68TEpu1QDz/YXY= - -glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.4: - version "7.1.6" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - 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" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -got@^10.7.0: - version "10.7.0" - resolved "https://registry.npmjs.org/got/-/got-10.7.0.tgz#62889dbcd6cca32cd6a154cc2d0c6895121d091f" - integrity sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg== - dependencies: - "@sindresorhus/is" "^2.0.0" - "@szmarczak/http-timer" "^4.0.0" - "@types/cacheable-request" "^6.0.1" - cacheable-lookup "^2.0.0" - cacheable-request "^7.0.1" - decompress-response "^5.0.0" - duplexer3 "^0.1.4" - get-stream "^5.0.0" - lowercase-keys "^2.0.0" - mimic-response "^2.1.0" - p-cancelable "^2.0.0" - p-event "^4.0.0" - responselike "^2.0.0" - to-readable-stream "^2.0.0" - type-fest "^0.10.0" - -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.2.3: - version "4.2.3" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" - integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== - -growly@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" - integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.0: - version "5.1.3" - resolved "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" - integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== - dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-symbols@^1.0.0, has-symbols@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" - integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hexer@^1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/hexer/-/hexer-1.5.0.tgz#b86ce808598e8a9d1892c571f3cedd86fc9f0653" - integrity sha1-uGzoCFmOip0YksVx887dhvyfBlM= - dependencies: - ansi-color "^0.2.1" - minimist "^1.1.0" - process "^0.10.0" - xtend "^4.0.0" - -highlight.js@^9.6.0: - version "9.15.10" - resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2" - integrity sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw== - -hosted-git-info@^2.1.4: - version "2.8.4" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546" - integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ== - -html-encoding-sniffer@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" - integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== - dependencies: - whatwg-encoding "^1.0.1" - -html-escaper@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" - integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== - -http-cache-semantics@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz#495704773277eeef6e43f9ab2c2c7d259dda25c5" - integrity sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew== - -http-errors@1.7.2, http-errors@~1.7.2: - version "1.7.2" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -human-signals@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - -iconv-lite@0.4.24, iconv-lite@^0.4.4: - version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ieee754@^1.1.4: - version "1.1.13" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== - -ignore-walk@^3.0.1: - version "3.0.2" - resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.2.tgz#99d83a246c196ea5c93ef9315ad7b0819c35069b" - integrity sha512-EXyErtpHbn75ZTsOADsfx6J/FPo6/5cjev46PXrcTpd8z3BoRkXgYu9/JVqrI7tusjmwCZutGeRJeU0Wo1e4Cw== - dependencies: - minimatch "^3.0.4" - -import-local@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" - integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -indexed-filter@^1.0.0: - version "1.0.3" - resolved "https://registry.npmjs.org/indexed-filter/-/indexed-filter-1.0.3.tgz#7911439191cac588188464640a8db4f6b324973d" - integrity sha512-oBIzs6EARNMzrLgVg20fK52H19WcRHBiukiiEkw9rnnI//8rinEBMLrYdwEfJ9d4K7bjV1L6nSGft6H/qzHNgQ== - dependencies: - append-type "^1.0.1" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@~1.3.0: - version "1.3.5" - resolved "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - -inspect-with-kind@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/inspect-with-kind/-/inspect-with-kind-1.0.5.tgz#fce151d4ce89722c82ca8e9860bb96f9167c316c" - integrity sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g== - dependencies: - kind-of "^6.0.2" - -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - -ipaddr.js@1.9.0: - version "1.9.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" - integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-callable@^1.1.4, is-callable@^1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" - integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== - -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= - -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-regex@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" - integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== - dependencies: - has "^1.0.3" - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" - integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== - -is-symbol@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" - integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== - dependencies: - has-symbols "^1.0.0" - -is-typedarray@^1.0.0, is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" - integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -istanbul-lib-coverage@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" - integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== - -istanbul-lib-instrument@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" - integrity sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg== - 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" - -istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" - integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.0.tgz#d4d16d035db99581b6194e119bbf36c963c5eb70" - integrity sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - -jaeger-client@^3.17.2: - version "3.17.2" - resolved "https://registry.npmjs.org/jaeger-client/-/jaeger-client-3.17.2.tgz#92cf26752c5c66f3e66adf595cdde2f548cc0804" - integrity sha512-19YloSidmKbrXHgecLWod8eXo7rm2ieUnsfg0ripTFGRCW5v2OWE96Gte4/tOQG/8N+T39VoLU2nMBdjbdMUJg== - dependencies: - node-int64 "^0.4.0" - opentracing "^0.13.0" - thriftrw "^3.5.0" - uuid "^3.2.1" - xorshift "^0.2.0" - -jest-changed-files@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.2.6.tgz#7d569cd6b265b1a84db3914db345d9c452f26b71" - integrity sha512-F7l2m5n55jFnJj4ItB9XbAlgO+6umgvz/mdK76BfTd2NGkvGf9x96hUXP/15a1K0k14QtVOoutwpRKl360msvg== - dependencies: - "@jest/types" "^25.2.6" - execa "^3.2.0" - throat "^5.0.0" - -jest-cli@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-25.2.7.tgz#515b61fee402c397ffa8d570532f7b039c3159f4" - integrity sha512-OOAZwY4Jkd3r5WhVM5L3JeLNFaylvHUczMLxQDVLrrVyb1Cy+DNJ6MVsb5TLh6iBklB42m5TOP+IbOgKGGOtMw== - dependencies: - "@jest/core" "^25.2.7" - "@jest/test-result" "^25.2.6" - "@jest/types" "^25.2.6" - chalk "^3.0.0" - exit "^0.1.2" - import-local "^3.0.2" - is-ci "^2.0.0" - jest-config "^25.2.7" - jest-util "^25.2.6" - jest-validate "^25.2.6" - prompts "^2.0.1" - realpath-native "^2.0.0" - yargs "^15.3.1" - -jest-config@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-25.2.7.tgz#a14e5b96575987ce913dd9fc20ac8cd4b35a8c29" - integrity sha512-rIdPPXR6XUxi+7xO4CbmXXkE6YWprvlKc4kg1SrkCL2YV5m/8MkHstq9gBZJ19Qoa3iz/GP+0sTG/PcIwkFojg== - dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^25.2.7" - "@jest/types" "^25.2.6" - babel-jest "^25.2.6" - chalk "^3.0.0" - deepmerge "^4.2.2" - glob "^7.1.1" - jest-environment-jsdom "^25.2.6" - jest-environment-node "^25.2.6" - jest-get-type "^25.2.6" - jest-jasmine2 "^25.2.7" - jest-regex-util "^25.2.6" - jest-resolve "^25.2.6" - jest-util "^25.2.6" - jest-validate "^25.2.6" - micromatch "^4.0.2" - pretty-format "^25.2.6" - realpath-native "^2.0.0" - -jest-diff@^25.2.1, jest-diff@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-25.2.6.tgz#a6d70a9ab74507715ea1092ac513d1ab81c1b5e7" - integrity sha512-KuadXImtRghTFga+/adnNrv9s61HudRMR7gVSbP35UKZdn4IK2/0N0PpGZIqtmllK9aUyye54I3nu28OYSnqOg== - dependencies: - chalk "^3.0.0" - diff-sequences "^25.2.6" - jest-get-type "^25.2.6" - pretty-format "^25.2.6" - -jest-docblock@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.2.6.tgz#4b09f1e7b7d6b3f39242ef3647ac7106770f722b" - integrity sha512-VAYrljEq0upq0oERfIaaNf28gC6p9gORndhHstCYF8NWGNQJnzoaU//S475IxfWMk4UjjVmS9rJKLe5Jjjbixw== - dependencies: - detect-newline "^3.0.0" - -jest-each@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-each/-/jest-each-25.2.6.tgz#026f6dea2ccc443c35cea793265620aab1b278b6" - integrity sha512-OgQ01VINaRD6idWJOhCYwUc5EcgHBiFlJuw+ON2VgYr7HLtMFyCcuo+3mmBvuLUH4QudREZN7cDCZviknzsaJQ== - dependencies: - "@jest/types" "^25.2.6" - chalk "^3.0.0" - jest-get-type "^25.2.6" - jest-util "^25.2.6" - pretty-format "^25.2.6" - -jest-environment-jsdom@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.2.6.tgz#b7ae41c6035905b8e58d63a8f63cf8eaa00af279" - integrity sha512-/o7MZIhGmLGIEG5j7r5B5Az0umWLCHU+F5crwfbm0BzC4ybHTJZOQTFQWhohBg+kbTCNOuftMcqHlVkVduJCQQ== - dependencies: - "@jest/environment" "^25.2.6" - "@jest/fake-timers" "^25.2.6" - "@jest/types" "^25.2.6" - jest-mock "^25.2.6" - jest-util "^25.2.6" - jsdom "^15.2.1" - -jest-environment-node@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.2.6.tgz#ad4398432867113f474d94fe37b071ed04b30f3d" - integrity sha512-D1Ihj14fxZiMHGeTtU/LunhzSI+UeBvlr/rcXMTNyRMUMSz2PEhuqGbB78brBY6Dk3FhJDk7Ta+8reVaGjLWhA== - dependencies: - "@jest/environment" "^25.2.6" - "@jest/fake-timers" "^25.2.6" - "@jest/types" "^25.2.6" - jest-mock "^25.2.6" - jest-util "^25.2.6" - semver "^6.3.0" - -jest-get-type@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" - integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== - -jest-haste-map@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.6.tgz#4aa6bcfa15310afccdb9ca77af58a98add8cedb8" - integrity sha512-nom0+fnY8jwzelSDQnrqaKAcDZczYQvMEwcBjeL3PQ4MlcsqeB7dmrsAniUw/9eLkngT5DE6FhnenypilQFsgA== - dependencies: - "@jest/types" "^25.2.6" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.3" - jest-serializer "^25.2.6" - jest-util "^25.2.6" - jest-worker "^25.2.6" - micromatch "^4.0.2" - sane "^4.0.3" - walker "^1.0.7" - which "^2.0.2" - optionalDependencies: - fsevents "^2.1.2" - -jest-jasmine2@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.2.7.tgz#55ff87f8f462ef0e2f16fd19430b8be8bcebef0e" - integrity sha512-HeQxEbonp8fUvik9jF0lkU9ab1u5TQdIb7YSU9Fj7SxWtqHNDGyCpF6ZZ3r/5yuertxi+R95Ba9eA91GMQ38eA== - dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^25.2.6" - "@jest/source-map" "^25.2.6" - "@jest/test-result" "^25.2.6" - "@jest/types" "^25.2.6" - chalk "^3.0.0" - co "^4.6.0" - expect "^25.2.7" - is-generator-fn "^2.0.0" - jest-each "^25.2.6" - jest-matcher-utils "^25.2.7" - jest-message-util "^25.2.6" - jest-runtime "^25.2.7" - jest-snapshot "^25.2.7" - jest-util "^25.2.6" - pretty-format "^25.2.6" - throat "^5.0.0" - -jest-leak-detector@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.2.6.tgz#68fbaf651142292b03e30641f33e15af9b8c62b1" - integrity sha512-n+aJUM+j/x1kIaPVxzerMqhAUuqTU1PL5kup46rXh+l9SP8H6LqECT/qD1GrnylE1L463/0StSPkH4fUpkuEjA== - dependencies: - jest-get-type "^25.2.6" - pretty-format "^25.2.6" - -jest-matcher-utils@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.2.7.tgz#53fad3c11fc42e92e374306df543026712c957a3" - integrity sha512-jNYmKQPRyPO3ny0KY1I4f0XW4XnpJ3Nx5ovT4ik0TYDOYzuXJW40axqOyS61l/voWbVT9y9nZ1THL1DlpaBVpA== - dependencies: - chalk "^3.0.0" - jest-diff "^25.2.6" - jest-get-type "^25.2.6" - pretty-format "^25.2.6" - -jest-message-util@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.6.tgz#9d5523bebec8cd9cdef75f0f3069d6ec9a2252df" - integrity sha512-Hgg5HbOssSqOuj+xU1mi7m3Ti2nwSQJQf/kxEkrz2r2rp2ZLO1pMeKkz2WiDUWgSR+APstqz0uMFcE5yc0qdcg== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^25.2.6" - "@types/stack-utils" "^1.0.1" - chalk "^3.0.0" - micromatch "^4.0.2" - slash "^3.0.0" - stack-utils "^1.0.1" - -jest-mock@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-25.2.6.tgz#8df66eaa55a713d0f2a7dfb4f14507289d24dfa3" - integrity sha512-vc4nibavi2RGPdj/MyZy/azuDjZhpYZLvpfgq1fxkhbyTpKVdG7CgmRVKJ7zgLpY5kuMjTzDYA6QnRwhsCU+tA== - dependencies: - "@jest/types" "^25.2.6" - -jest-pnp-resolver@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" - integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== - -jest-regex-util@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz#d847d38ba15d2118d3b06390056028d0f2fd3964" - integrity sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw== - -jest-resolve-dependencies@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.2.7.tgz#9ca4c62d67cce031a27fa5d5705b4b5b5c029d23" - integrity sha512-IrnMzCAh11Xd2gAOJL+ThEW6QO8DyqNdvNkQcaCticDrOAr9wtKT7yT6QBFFjqKFgjjvaVKDs59WdgUhgYnHnQ== - dependencies: - "@jest/types" "^25.2.6" - jest-regex-util "^25.2.6" - jest-snapshot "^25.2.7" - -jest-resolve@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.2.6.tgz#84694ead5da13c2890ac04d4a78699ba937f3896" - integrity sha512-7O61GVdcAXkLz/vNGKdF+00A80/fKEAA47AEXVNcZwj75vEjPfZbXDaWFmAQCyXj4oo9y9dC9D+CLA11t8ieGw== - dependencies: - "@jest/types" "^25.2.6" - browser-resolve "^1.11.3" - chalk "^3.0.0" - jest-pnp-resolver "^1.2.1" - realpath-native "^2.0.0" - resolve "^1.15.1" - -jest-runner@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-25.2.7.tgz#3676c01dc0104caa8a0ebb8507df382c88f2a1e2" - integrity sha512-RFEr71nMrtNwcpoHzie5+fe1w3JQCGMyT2xzNwKe3f88+bK+frM2o1v24gEcPxQ2QqB3COMCe2+1EkElP+qqqQ== - dependencies: - "@jest/console" "^25.2.6" - "@jest/environment" "^25.2.6" - "@jest/test-result" "^25.2.6" - "@jest/types" "^25.2.6" - chalk "^3.0.0" - exit "^0.1.2" - graceful-fs "^4.2.3" - jest-config "^25.2.7" - jest-docblock "^25.2.6" - jest-haste-map "^25.2.6" - jest-jasmine2 "^25.2.7" - jest-leak-detector "^25.2.6" - jest-message-util "^25.2.6" - jest-resolve "^25.2.6" - jest-runtime "^25.2.7" - jest-util "^25.2.6" - jest-worker "^25.2.6" - source-map-support "^0.5.6" - throat "^5.0.0" - -jest-runtime@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.2.7.tgz#cb10e695d036671a83aec3a3803163c354043ac9" - integrity sha512-Gw3X8KxTTFylu2T/iDSNKRUQXQiPIYUY0b66GwVYa7W8wySkUljKhibQHSq0VhmCAN7vRBEQjlVQ+NFGNmQeBw== - dependencies: - "@jest/console" "^25.2.6" - "@jest/environment" "^25.2.6" - "@jest/source-map" "^25.2.6" - "@jest/test-result" "^25.2.6" - "@jest/transform" "^25.2.6" - "@jest/types" "^25.2.6" - "@types/yargs" "^15.0.0" - chalk "^3.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.3" - jest-config "^25.2.7" - jest-haste-map "^25.2.6" - jest-message-util "^25.2.6" - jest-mock "^25.2.6" - jest-regex-util "^25.2.6" - jest-resolve "^25.2.6" - jest-snapshot "^25.2.7" - jest-util "^25.2.6" - jest-validate "^25.2.6" - realpath-native "^2.0.0" - slash "^3.0.0" - strip-bom "^4.0.0" - yargs "^15.3.1" - -jest-serializer@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.6.tgz#3bb4cc14fe0d8358489dbbefbb8a4e708ce039b7" - integrity sha512-RMVCfZsezQS2Ww4kB5HJTMaMJ0asmC0BHlnobQC6yEtxiFKIxohFA4QSXSabKwSggaNkqxn6Z2VwdFCjhUWuiQ== - -jest-snapshot@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.2.7.tgz#7eeafeef4dcbda1c47c8503d2bf5212b6430aac6" - integrity sha512-Rm8k7xpGM4tzmYhB6IeRjsOMkXaU8/FOz5XlU6oYwhy53mq6txVNqIKqN1VSiexzpC80oWVxVDfUDt71M6XPOA== - dependencies: - "@babel/types" "^7.0.0" - "@jest/types" "^25.2.6" - "@types/prettier" "^1.19.0" - chalk "^3.0.0" - expect "^25.2.7" - jest-diff "^25.2.6" - jest-get-type "^25.2.6" - jest-matcher-utils "^25.2.7" - jest-message-util "^25.2.6" - jest-resolve "^25.2.6" - make-dir "^3.0.0" - natural-compare "^1.4.0" - pretty-format "^25.2.6" - semver "^6.3.0" - -jest-util@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-25.2.6.tgz#3c1c95cdfd653126728b0ed861a86610e30d569c" - integrity sha512-gpXy0H5ymuQ0x2qgl1zzHg7LYHZYUmDEq6F7lhHA8M0eIwDB2WteOcCnQsohl9c/vBKZ3JF2r4EseipCZz3s4Q== - dependencies: - "@jest/types" "^25.2.6" - chalk "^3.0.0" - is-ci "^2.0.0" - make-dir "^3.0.0" - -jest-validate@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-25.2.6.tgz#ab3631fb97e242c42b09ca53127abe0b12e9125e" - integrity sha512-a4GN7hYbqQ3Rt9iHsNLFqQz7HDV7KiRPCwPgo5nqtTIWNZw7gnT8KchG+Riwh+UTSn8REjFCodGp50KX/fRNgQ== - dependencies: - "@jest/types" "^25.2.6" - camelcase "^5.3.1" - chalk "^3.0.0" - jest-get-type "^25.2.6" - leven "^3.1.0" - pretty-format "^25.2.6" - -jest-watcher@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.2.7.tgz#01db4332d34d14c03c9ef22255125a3b07f997bc" - integrity sha512-RdHuW+f49tahWtluTnUdZ2iPliebleROI2L/J5phYrUS6DPC9RB3SuUtqYyYhGZJsbvRSuLMIlY/cICJ+PIecw== - dependencies: - "@jest/test-result" "^25.2.6" - "@jest/types" "^25.2.6" - ansi-escapes "^4.2.1" - chalk "^3.0.0" - jest-util "^25.2.6" - string-length "^3.1.0" - -jest-worker@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.6.tgz#d1292625326794ce187c38f51109faced3846c58" - integrity sha512-FJn9XDUSxcOR4cwDzRfL1z56rUofNTFs539FGASpd50RHdb6EVkhxQqktodW2mI49l+W3H+tFJDotCHUQF6dmA== - dependencies: - merge-stream "^2.0.0" - supports-color "^7.0.0" - -jest@^25.2.7: - version "25.2.7" - resolved "https://registry.npmjs.org/jest/-/jest-25.2.7.tgz#3929a5f35cdd496f7756876a206b99a94e1e09ae" - integrity sha512-XV1n/CE2McCikl4tfpCY950RytHYvxdo/wvtgmn/qwA8z1s16fuvgFL/KoPrrmkqJTaPMUlLVE58pwiaTX5TdA== - dependencies: - "@jest/core" "^25.2.7" - import-local "^3.0.2" - jest-cli "^25.2.7" - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -jsdom@^15.2.1: - version "15.2.1" - resolved "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" - integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== - dependencies: - abab "^2.0.0" - acorn "^7.1.0" - acorn-globals "^4.3.2" - array-equal "^1.0.0" - cssom "^0.4.1" - cssstyle "^2.0.0" - data-urls "^1.1.0" - domexception "^1.0.1" - escodegen "^1.11.1" - html-encoding-sniffer "^1.0.2" - nwsapi "^2.2.0" - parse5 "5.1.0" - pn "^1.1.0" - request "^2.88.0" - request-promise-native "^1.0.7" - saxes "^3.1.9" - symbol-tree "^3.2.2" - tough-cookie "^3.0.1" - w3c-hr-time "^1.0.1" - w3c-xmlserializer "^1.1.2" - webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^7.0.0" - ws "^7.0.0" - xml-name-validator "^3.0.0" - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= - dependencies: - jsonify "~0.0.0" - -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json5@^2.1.0, json5@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" - integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== - dependencies: - minimist "^1.2.0" - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -just-extend@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc" - integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw== - -keyv@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.0.tgz#2d1dab694926b2d427e4c74804a10850be44c12f" - integrity sha512-U7ioE8AimvRVLfw4LffyOIRhL2xVgmE8T22L6i0BucSnBUyv4w+I7VN/zVZwRKHOI6ZRUcdMdWHQ8KSUvGpEog== - dependencies: - json-buffer "3.0.1" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== - -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - -kuler@1.0.x: - version "1.0.1" - resolved "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz#ef7c784f36c9fb6e16dd3150d152677b2b0228a6" - integrity sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ== - dependencies: - colornames "^1.1.1" - -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -limiter@^1.0.5: - version "1.1.5" - resolved "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2" - integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== - -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash.get@^4.4.2: - version "4.4.2" - resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= - -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= - -lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: - version "4.17.15" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - -logform@^2.1.1, logform@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz#957155ebeb67a13164069825ce67ddb5bb2dd360" - integrity sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ== - dependencies: - colors "^1.2.1" - fast-safe-stringify "^2.0.4" - fecha "^2.3.3" - ms "^2.1.1" - triple-beam "^1.3.0" - -lolex@^5.0.0: - version "5.1.2" - resolved "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" - integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== - dependencies: - "@sinonjs/commons" "^1.7.0" - -long@^2.4.0: - version "2.4.0" - resolved "https://registry.npmjs.org/long/-/long-2.4.0.tgz#9fa180bb1d9500cdc29c4156766a1995e1f4524f" - integrity sha1-n6GAux2VAM3CnEFWdmoZleH0Uk8= - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lsif-protocol@0.4.3: - version "0.4.3" - resolved "https://registry.npmjs.org/lsif-protocol/-/lsif-protocol-0.4.3.tgz#e528acc5d056b86b071dc242047a27182f7ddddf" - integrity sha512-VePb+EbdturiWmGw1dh7G7z7OmhT6aBzqU42cqJLEk2QTMTbnN6ySIp1IiffNmD9jLX3NAtNSz1c85zpcZN6pw== - dependencies: - vscode-languageserver-protocol "^3.14.1" - -make-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz#1b5f39f6b9270ed33f9f054c5c0f84304989f801" - integrity sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw== - dependencies: - semver "^6.0.0" - -makeerror@1.0.x: - version "1.0.11" - resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" - integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= - dependencies: - tmpl "1.0.x" - -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-stream@~0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" - integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - 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" - -micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== - dependencies: - braces "^3.0.1" - picomatch "^2.0.5" - -mime-db@1.40.0: - version "1.40.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" - integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== - -mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.24" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" - integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== - dependencies: - mime-db "1.40.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -mimic-response@^2.0.0, mimic-response@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" - integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== - -minimatch@^3.0.3, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@^1.1.0, minimist@^1.1.1, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= - -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minizlib@^1.2.1: - version "1.3.3" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1, ms@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -mz@^2.4.0, mz@^2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" - integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== - dependencies: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" - -nan@^2.12.1: - version "2.14.0" - resolved "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - 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" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -needle@^2.2.1: - version "2.4.0" - resolved "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" - integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -nise@^4.0.1: - version "4.0.3" - resolved "https://registry.npmjs.org/nise/-/nise-4.0.3.tgz#9f79ff02fa002ed5ffbc538ad58518fa011dc913" - integrity sha512-EGlhjm7/4KvmmE6B/UFsKh7eHykRl9VH+au8dduHLCyWUO/hr7+N+WtTvDUwc9zHuM1IaIJs/0lQ6Ag1jDkQSg== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@sinonjs/fake-timers" "^6.0.0" - "@sinonjs/text-encoding" "^0.7.1" - just-extend "^4.0.2" - path-to-regexp "^1.7.0" - -nock@^12.0.2: - version "12.0.2" - resolved "https://registry.npmjs.org/nock/-/nock-12.0.2.tgz#47617b34738e026b29d2294b4579e35b27e6a4d3" - integrity sha512-pTckyfP8QHvwXP/oX+zQuSIL3S/mWTd84ba4pOGZlS/FgRZyljv4C3ZyOjgMilvkydSaERML/aJEF13EBUuDTQ== - dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - lodash "^4.17.13" - propagate "^2.0.0" - -node-cleanup@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz#7ac19abd297e09a7f72a71545d951b517e4dde2c" - integrity sha1-esGavSl+Caf3KnFUXZUbUX5N3iw= - -node-int64@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= - -node-modules-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" - integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= - -node-notifier@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz#cea319e06baa16deec8ce5cd7f133c4a46b68e12" - integrity sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw== - dependencies: - growly "^1.3.0" - is-wsl "^2.1.1" - semver "^6.3.0" - shellwords "^0.1.1" - which "^1.3.1" - -node-pre-gyp@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" - integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q== - 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" - -noms@0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859" - integrity sha1-2o69nzr51nYJGbJ9nNyAkqczKFk= - dependencies: - inherits "^2.0.1" - readable-stream "~1.0.31" - -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= - dependencies: - abbrev "1" - osenv "^0.1.4" - -normalize-package-data@^2.3.2: - version "2.5.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== - -npm-bundled@^1.0.1: - version "1.0.6" - resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" - integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== - -npm-packlist@^1.1.6: - version "1.4.4" - resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" - integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -npm-run-path@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - 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@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -nwsapi@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-inspect@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" - integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== - -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" - -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -object.values@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" - integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.12.0" - function-bind "^1.1.1" - has "^1.0.3" - -on-finished@^2.3.0, on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -one-time@0.0.4: - version "0.0.4" - resolved "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e" - integrity sha1-+M33eISCb+Tf+T46nMN7HkSAdC4= - -onetime@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" - integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== - dependencies: - mimic-fn "^2.1.0" - -opentracing@^0.13.0: - version "0.13.0" - resolved "https://registry.npmjs.org/opentracing/-/opentracing-0.13.0.tgz#6a341442f09d7d866bc11ed03de1e3828e3d6aab" - integrity sha1-ajQUQvCdfYZrwR7QPeHjgo49aqs= - -opentracing@^0.14.0, opentracing@^0.14.4, opentracing@~0.14.3: - version "0.14.4" - resolved "https://registry.npmjs.org/opentracing/-/opentracing-0.14.4.tgz#a113408ea740da3a90fde5b3b0011a375c2e4268" - integrity sha512-nNnZDkUNExBwEpb7LZaeMeQgvrlO8l4bgY/LvGNZCR0xG/dGWqHqjKrAmR5GUoYo0FIz38kxasvA1aevxWs2CA== - -optionator@^0.8.1: - version "0.8.2" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -p-cancelable@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" - integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== - -p-each-series@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" - integrity sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ== - -p-event@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/p-event/-/p-event-4.1.0.tgz#e92bb866d7e8e5b732293b1c8269d38e9982bf8e" - integrity sha512-4vAd06GCsgflX4wHN1JqrMzBh/8QZ4j+rzp0cd2scXRwuBEv+QR3wrVA5aLhWDLw4y2WgDKvzWF3CCLmVM1UgA== - dependencies: - p-timeout "^2.0.1" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-finally@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" - integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.2.2" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" - integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== - dependencies: - p-try "^2.0.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-retry@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz#ea9066c6b44f23cab4cd42f6147cdbbc6604da5d" - integrity sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA== - dependencies: - "@types/retry" "^0.12.0" - retry "^0.12.0" - -p-timeout@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" - integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== - dependencies: - p-finally "^1.0.0" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -packet-reader@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" - integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== - -parent-require@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz#746a167638083a860b0eef6732cb27ed46c32977" - integrity sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc= - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - -parse5@5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" - integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== - -parse5@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" - integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path-to-regexp@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" - integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30= - dependencies: - isarray "0.0.1" - -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= - dependencies: - pify "^2.0.0" - -pause-stream@0.0.11: - version "0.0.11" - resolved "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" - integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= - dependencies: - through "~2.3" - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -pg-connection-string@0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" - integrity sha1-2hhHsglA5C7hSSvq9l1J2RskXfc= - -pg-int8@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" - integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== - -pg-packet-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/pg-packet-stream/-/pg-packet-stream-1.1.0.tgz#e45c3ae678b901a2873af1e17b92d787962ef914" - integrity sha512-kRBH0tDIW/8lfnnOyTwKD23ygJ/kexQVXZs7gEyBljw4FYqimZFxnMMx50ndZ8In77QgfGuItS5LLclC2TtjYg== - -pg-pool@^2.0.10: - version "2.0.10" - resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.10.tgz#842ee23b04e86824ce9d786430f8365082d81c4a" - integrity sha512-qdwzY92bHf3nwzIUcj+zJ0Qo5lpG/YxchahxIN8+ZVmXqkahKXsnl2aiJPHLYN9o5mB/leG+Xh6XKxtP7e0sjg== - -pg-types@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" - integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== - dependencies: - pg-int8 "1.0.1" - postgres-array "~2.0.0" - postgres-bytea "~1.0.0" - postgres-date "~1.0.4" - postgres-interval "^1.1.0" - -pg@^7.18.2: - version "7.18.2" - resolved "https://registry.npmjs.org/pg/-/pg-7.18.2.tgz#4e219f05a00aff4db6aab1ba02f28ffa4513b0bb" - integrity sha512-Mvt0dGYMwvEADNKy5PMQGlzPudKcKKzJds/VbOeZJpb6f/pI3mmoXX0JksPgI3l3JPP/2Apq7F36O63J7mgveA== - dependencies: - buffer-writer "2.0.0" - packet-reader "1.0.0" - pg-connection-string "0.1.3" - pg-packet-stream "^1.1.0" - pg-pool "^2.0.10" - pg-types "^2.1.0" - pgpass "1.x" - semver "4.3.2" - -pgpass@1.x: - version "1.0.2" - resolved "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" - integrity sha1-Knu0G2BltnkH6R2hsHwYR8h3swY= - dependencies: - split "^1.0.0" - -picomatch@^2.0.4, picomatch@^2.0.5: - version "2.2.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" - integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pirates@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== - dependencies: - node-modules-regexp "^1.0.0" - -pkg-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" - integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= - dependencies: - find-up "^2.1.0" - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -pn@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" - integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -postgres-array@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" - integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== - -postgres-bytea@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" - integrity sha1-AntTPAqokOJtFy1Hz5zOzFIazTU= - -postgres-date@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.4.tgz#1c2728d62ef1bff49abdd35c1f86d4bdf118a728" - integrity sha512-bESRvKVuTrjoBluEcpv2346+6kgB7UlnqWZsnbnCccTNq/pqfj1j6oBaN5+b/NrDXepYUT/HKadqv3iS9lJuVA== - -postgres-interval@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" - integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== - dependencies: - xtend "^4.0.0" - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -pretty-format@^25.2.1, pretty-format@^25.2.6: - version "25.2.6" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.6.tgz#542a1c418d019bbf1cca2e3620443bc1323cb8d7" - integrity sha512-DEiWxLBaCHneffrIT4B+TpMvkV9RNvvJrd3lY9ew1CEQobDzEXmYT1mg0hJhljZty7kCc10z13ohOFAE8jrUDg== - dependencies: - "@jest/types" "^25.2.6" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^16.12.0" - -printj@~1.1.0: - version "1.1.2" - resolved "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" - integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.10.0: - version "0.10.1" - resolved "https://registry.npmjs.org/process/-/process-0.10.1.tgz#842457cc51cfed72dc775afeeafb8c6034372725" - integrity sha1-hCRXzFHP7XLcd1r+6vuMYDQ3JyU= - -prom-client@^12.0.0: - version "12.0.0" - resolved "https://registry.npmjs.org/prom-client/-/prom-client-12.0.0.tgz#9689379b19bd3f6ab88a9866124db9da3d76c6ed" - integrity sha512-JbzzHnw0VDwCvoqf8y1WDtq4wSBAbthMB1pcVI/0lzdqHGJI3KBJDXle70XK+c7Iv93Gihqo0a5LlOn+g8+DrQ== - dependencies: - tdigest "^0.1.1" - -prom-client@~11.3.0: - version "11.3.0" - resolved "https://registry.npmjs.org/prom-client/-/prom-client-11.3.0.tgz#fe93f360182f1ec1921722efc211a6c0e68e0253" - integrity sha512-OqSf5WOvpGZXkfqPXUHNHpjrbEE/q8jxjktO0i7zg1cnULAtf0ET67/J5R4e4iA4MZx2260tzTzSFSWgMdTZmQ== - dependencies: - tdigest "^0.1.1" - -prompts@^2.0.1: - version "2.2.1" - resolved "https://registry.npmjs.org/prompts/-/prompts-2.2.1.tgz#f901dd2a2dfee080359c0e20059b24188d75ad35" - integrity sha512-VObPvJiWPhpZI6C5m60XOzTfnYg/xc/an+r9VYymj9WJW3B/DIH+REzjpAACPf8brwPeP+7vz3bIim3S+AaMjw== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.3" - -propagate@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" - integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== - -proxy-addr@~2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" - integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.9.0" - -ps-tree@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" - integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== - dependencies: - event-stream "=3.3.4" - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -psl@^1.1.24, psl@^1.1.28: - version "1.7.0" - resolved "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" - integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -qs@6.7.0: - version "6.7.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -react-is@^16.12.0: - version "16.12.0" - resolved "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" - integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== - -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - -readable-stream@^2.0.6, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.6" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - 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" - -readable-stream@^3.1.1: - version "3.4.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" - integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readable-stream@~1.0.31: - version "1.0.34" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -realpath-native@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" - integrity sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q== - -reflect-metadata@^0.1.13: - version "0.1.13" - resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" - integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -relateurl@^0.2.7: - version "0.2.7" - resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -request-promise-core@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" - integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== - dependencies: - lodash "^4.17.15" - -request-promise-native@^1.0.7: - version "1.0.8" - resolved "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" - integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== - dependencies: - request-promise-core "1.1.3" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - -request@^2.87.0, request@^2.88.0: - version "2.88.0" - resolved "https://registry.npmjs.org/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" - integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== - 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.0" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.4.3" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@1.1.7: - version "1.1.7" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= - -resolve@^1.10.0, resolve@^1.12.0, resolve@^1.15.1, resolve@^1.3.2, resolve@^1.5.0: - version "1.15.1" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" - integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== - dependencies: - path-parse "^1.0.6" - -responselike@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" - integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== - dependencies: - lowercase-keys "^2.0.0" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= - -rimraf@^2.6.1, rimraf@^2.6.2: - version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -rimraf@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.1.tgz#48d3d4cb46c80d388ab26cd61b1b466ae9ae225a" - integrity sha512-IQ4ikL8SjBiEDZfk+DFVwqRK8md24RWMEJkdSlgNLkyyAImcjf8SWvU1qFMDOb4igBClbTQ/ugPqXcRwdFTxZw== - dependencies: - glob "^7.1.3" - -rmfr@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/rmfr/-/rmfr-2.0.0.tgz#8a42e81332550b3f0019b8fb8ab245bea81b6d1c" - integrity sha512-nQptLCZeyyJfgbpf2x97k5YE8vzDn7bhwx9NlvODdhgbU0mL1ruh71X0HYdRaOEvWC7Cr+SfV0p5p+Ib5yOl7A== - dependencies: - assert-valid-glob-opts "^1.0.0" - glob "^7.1.2" - graceful-fs "^4.1.11" - inspect-with-kind "^1.0.4" - rimraf "^2.6.2" - -rsvp@^4.8.4: - version "4.8.5" - resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - -safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sane@^4.0.3: - version "4.1.0" - resolved "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" - integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== - dependencies: - "@cnakazawa/watch" "^1.0.3" - anymatch "^2.0.0" - capture-exit "^2.0.0" - exec-sh "^0.3.2" - execa "^1.0.0" - fb-watchman "^2.0.0" - micromatch "^3.1.4" - minimist "^1.1.1" - walker "~1.0.5" - -sax@>=0.6.0, sax@^1.2.4: - version "1.2.4" - resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -saxes@^3.1.9: - version "3.1.11" - resolved "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" - integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== - dependencies: - xmlchars "^2.1.1" - -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@4.3.2: - version "4.3.2" - resolved "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" - integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c= - -semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -send@0.17.1: - version "0.17.1" - resolved "https://registry.npmjs.org/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - -set-blocking@^2.0.0, set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - -sha.js@^2.4.11: - version "2.4.11" - resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shellwords@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" - integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= - dependencies: - is-arrayish "^0.3.1" - -sinon@^9.0.1: - version "9.0.1" - resolved "https://registry.npmjs.org/sinon/-/sinon-9.0.1.tgz#dbb18f7d8f5835bcf91578089c0a97b2fffdd73b" - integrity sha512-iTTyiQo5T94jrOx7X7QLBZyucUJ2WvL9J13+96HMfm2CGoJYbIPqRfl6wgNcqmzk0DI28jeGx5bUTXizkrqBmg== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@sinonjs/fake-timers" "^6.0.0" - "@sinonjs/formatio" "^5.0.1" - "@sinonjs/samsam" "^5.0.3" - diff "^4.0.2" - nise "^4.0.1" - supports-color "^7.1.0" - -sisteransi@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.3.tgz#98168d62b79e3a5e758e27ae63c4a053d748f4eb" - integrity sha512-SbEG75TzH8G7eVXFSN5f9EExILKfly7SUvVY5DhhYLvfhKqhDFY0OzevWa/zwak0RLRfWS5AvfMWpd9gJvr5Yg== - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - 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" - -source-map-resolve@^0.5.0: - version "0.5.2" - resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" - integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== - dependencies: - atob "^2.1.1" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-support@^0.5.16, source-map-support@^0.5.6: - version "0.5.16" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= - -source-map@^0.5.0, source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@^0.7.3: - version "0.7.3" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - -spdx-correct@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" - integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" - integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== - -spdx-expression-parse@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" - integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.5" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" - integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -split@0.3: - version "0.3.3" - resolved "https://registry.npmjs.org/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" - integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= - dependencies: - through "2" - -split@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" - integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== - dependencies: - through "2" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -sqlite3@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.1.tgz#539a42e476640796578e22d589b3283c28055242" - integrity sha512-CvT5XY+MWnn0HkbwVKJAyWEMfzpAPwnTiB3TobA5Mri44SrTovmmh499NPQP+gatkeOipqPlBLel7rn4E/PCQg== - dependencies: - nan "^2.12.1" - node-pre-gyp "^0.11.0" - request "^2.87.0" - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -stack-trace@0.0.x: - version "0.0.10" - resolved "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= - -stack-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" - integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - -stream-combiner@~0.0.4: - version "0.0.4" - resolved "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" - integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= - dependencies: - duplexer "~0.1.1" - -stream-throttle@^0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz#add57c8d7cc73a81630d31cd55d3961cfafba9c3" - integrity sha1-rdV8jXzHOoFjDTHNVdOWHPr7qcM= - dependencies: - commander "^2.2.0" - limiter "^1.0.5" - -string-argv@^0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738" - integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA== - -string-length@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" - integrity sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA== - dependencies: - astral-regex "^1.0.0" - strip-ansi "^5.2.0" - -string-template@~0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" - integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= - -string-width@^1.0.1, "string-width@^1.0.2 || 2": - version "1.0.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" - integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -string.prototype.trimleft@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" - integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - -string.prototype.trimright@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" - integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - -string_decoder@^1.1.1, string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.0.0, supports-color@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" - integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== - dependencies: - has-flag "^4.0.0" - -supports-hyperlinks@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.0.0.tgz#b1b94a159e9df00b0a554b2d5f0e0a89690334b0" - integrity sha512-bFhn0MQ8qefLyJ3K7PpHiPUTuTVPWw6RXfaMeV6xgJLXtBbszyboz1bvGTVv4R0YpQm2DqlXXn0fFHhxUHVE5w== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - -symbol-tree@^3.2.2: - version "3.2.4" - resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -tar@^4: - version "4.4.13" - resolved "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== - 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" - -tdigest@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz#2e3cb2c39ea449e55d1e6cd91117accca4588021" - integrity sha1-Ljyyw56kSeVdHmzZEReszKRYgCE= - dependencies: - bintrees "1.0.1" - -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" - -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== - dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" - minimatch "^3.0.4" - -text-hex@1.0.x: - version "1.0.0" - resolved "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" - integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== - -thenify-all@^1.0.0: - version "1.6.0" - resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" - integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= - dependencies: - thenify ">= 3.1.0 < 4" - -"thenify@>= 3.1.0 < 4": - version "3.3.0" - resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" - integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= - dependencies: - any-promise "^1.0.0" - -thriftrw@^3.5.0: - version "3.11.3" - resolved "https://registry.npmjs.org/thriftrw/-/thriftrw-3.11.3.tgz#2cef6b4d089b7ba6275198b86582881582907d45" - integrity sha512-mnte80Go5MCfYyOQ9nk6SljaEicCXlwLchupHR+/zlx0MKzXwAiyt38CHjLZVvKtoyEzirasXuNYtkEjgghqCw== - dependencies: - bufrw "^1.2.1" - error "7.0.2" - long "^2.4.0" - -throat@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" - integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== - -through2@^2.0.1: - version "2.0.5" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through@2, through@~2.3, through@~2.3.1: - version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-readable-stream@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz#82880316121bea662cdc226adb30addb50cb06e8" - integrity sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w== - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - -tough-cookie@^2.3.3, tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - -tough-cookie@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" - integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== - dependencies: - ip-regex "^2.1.0" - psl "^1.1.28" - punycode "^2.1.1" - -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" - integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= - dependencies: - punycode "^2.1.0" - -triple-beam@^1.2.0, triple-beam@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" - integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== - -tsc-watch@^4.2.3: - version "4.2.3" - resolved "https://registry.npmjs.org/tsc-watch/-/tsc-watch-4.2.3.tgz#630aaf1ed8bbfb6fbcbddb9bef96054bed217c4b" - integrity sha512-M1Lo37+ggVfavGX3ObUVMz9QBH7moqq2RlmBdxnz6a6etwecetznZ/ZgYOi2c9HQ4Ki2qStj7V9J/gSf0rThig== - dependencies: - cross-spawn "^5.1.0" - node-cleanup "^2.1.2" - ps-tree "^1.2.0" - string-argv "^0.1.1" - strip-ansi "^4.0.0" - -tslib@^1.9.0: - version "1.10.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" - integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -type-detect@4.0.8, type-detect@^4.0.8: - version "4.0.8" - resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.10.0: - version "0.10.0" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz#7f06b2b9fbfc581068d1341ffabd0349ceafc642" - integrity sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - -typeorm@^0.2.24: - version "0.2.24" - resolved "https://registry.npmjs.org/typeorm/-/typeorm-0.2.24.tgz#cd0fbd907326873a96c98e290fca49c589f0ffa8" - integrity sha512-L9tQv6nNLRyh+gex/qc8/CyLs8u0kXKqk1OjYGF13k/KOg6N2oibwkuGgv0FuoTGYx2ta2NmqvuMUAMrHIY5ew== - dependencies: - app-root-path "^3.0.0" - buffer "^5.1.0" - chalk "^2.4.2" - cli-highlight "^2.0.0" - debug "^4.1.1" - dotenv "^6.2.0" - glob "^7.1.2" - js-yaml "^3.13.1" - mkdirp "^0.5.1" - reflect-metadata "^0.1.13" - sha.js "^2.4.11" - tslib "^1.9.0" - xml2js "^0.4.17" - yargonaut "^1.1.2" - yargs "^13.2.1" - -typescript-json-schema@^0.42.0: - version "0.42.0" - resolved "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.42.0.tgz#695f212a72d91d47c0605371dc697597b7817c1b" - integrity sha512-9WO+lVmlph7Ecb7lPd9tU84XFUQh44kpAf3cWe/Ym4G5EKw/SS6XGpi1DZDthvxqkIdNSDlWi7FhKfxuIV/3yw== - dependencies: - "@types/json-schema" "^7.0.3" - glob "~7.1.4" - json-stable-stringify "^1.0.1" - typescript "^3.5.3" - yargs "^14.0.0" - -typescript@^3.5.3, typescript@^3.7.2: - version "3.7.2" - resolved "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" - integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -util.promisify@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@^3.2.1, uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - -v8-to-istanbul@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.0.1.tgz#d6a2a3823b8ff49bdf2167ff2a45d82dff81d02f" - integrity sha512-x0yZvZAkjJwdD3fPiJzYP37aod0ati4LlmD2RmpKjqewjKAov/u/ytZ8ViIZb07cN4cePKzl9ijiUi7C1LQ8hQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - source-map "^0.7.3" - -validate-glob-opts@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/validate-glob-opts/-/validate-glob-opts-1.0.2.tgz#ef9f98977d965537ea4f51fa7d5799e9c6ebca91" - integrity sha512-3PKjRQq/R514lUcG9OEiW0u9f7D4fP09A07kmk1JbNn2tfeQdAHhlT+A4dqERXKu2br2rrxSM3FzagaEeq9w+A== - dependencies: - array-to-sentence "^1.1.0" - indexed-filter "^1.0.0" - inspect-with-kind "^1.0.4" - is-plain-obj "^1.1.0" - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -validator@^12.1.0: - version "12.2.0" - resolved "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz#660d47e96267033fd070096c3b1a6f2db4380a0a" - integrity sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ== - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vscode-jsonrpc@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" - integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== - -vscode-languageserver-protocol@^3.14.1, vscode-languageserver-protocol@^3.15.3: - version "3.15.3" - resolved "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz#3fa9a0702d742cf7883cb6182a6212fcd0a1d8bb" - integrity sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw== - dependencies: - vscode-jsonrpc "^5.0.1" - vscode-languageserver-types "3.15.1" - -vscode-languageserver-types@3.15.1: - version "3.15.1" - resolved "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de" - integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ== - -vscode-languageserver@^6.1.1: - version "6.1.1" - resolved "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-6.1.1.tgz#d76afc68172c27d4327ee74332b468fbc740d762" - integrity sha512-DueEpkUAkD5XTR4MLYNr6bQIp/UFR0/IPApgXU3YfCBCB08u2sm9hRCs6DxYZELkk++STPjpcjksR2H8qI3cDQ== - dependencies: - vscode-languageserver-protocol "^3.15.3" - -w3c-hr-time@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" - integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= - dependencies: - browser-process-hrtime "^0.1.2" - -w3c-xmlserializer@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" - integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== - dependencies: - domexception "^1.0.1" - webidl-conversions "^4.0.2" - xml-name-validator "^3.0.0" - -walker@^1.0.7, walker@~1.0.5: - version "1.0.7" - resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" - integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= - dependencies: - makeerror "1.0.x" - -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - -whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - -whatwg-url@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd" - integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@^1.2.9, which@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - -winston-transport@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz#df68c0c202482c448d9b47313c07304c2d7c2c66" - integrity sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A== - dependencies: - readable-stream "^2.3.6" - triple-beam "^1.2.0" - -winston@^3.0.0, winston@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz#63061377976c73584028be2490a1846055f77f07" - integrity sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw== - dependencies: - async "^2.6.1" - diagnostics "^1.1.1" - is-stream "^1.1.0" - logform "^2.1.1" - one-time "0.0.4" - readable-stream "^3.1.1" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.3.0" - -wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write-file-atomic@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz#558328352e673b5bb192cf86500d60b230667d4b" - integrity sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw== - dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" - -ws@^7.0.0: - version "7.2.1" - resolved "https://registry.npmjs.org/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e" - integrity sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A== - -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - -xml2js@^0.4.17: - version "0.4.22" - resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz#4fa2d846ec803237de86f30aa9b5f70b6600de02" - integrity sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw== - dependencies: - sax ">=0.6.0" - util.promisify "~1.0.0" - xmlbuilder "~11.0.0" - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== - -xmlchars@^2.1.1: - version "2.2.0" - resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - -xorshift@^0.2.0: - version "0.2.1" - resolved "https://registry.npmjs.org/xorshift/-/xorshift-0.2.1.tgz#fcd82267e9351c13f0fb9c73307f25331d29c63a" - integrity sha1-/NgiZ+k1HBPw+5xzMH8lMx0pxjo= - -xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yallist@^3.0.0, yallist@^3.0.3: - version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yargonaut@^1.1.2: - version "1.1.4" - resolved "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz#c64f56432c7465271221f53f5cc517890c3d6e0c" - integrity sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA== - dependencies: - chalk "^1.1.1" - figlet "^1.1.1" - parent-require "^1.0.0" - -yargs-parser@^13.1.1: - version "13.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" - integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^15.0.0: - version "15.0.0" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz#cdd7a97490ec836195f59f3f4dbe5ea9e8f75f08" - integrity sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^18.1.1: - version "18.1.2" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz#2f482bea2136dbde0861683abea7756d30b504f1" - integrity sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs@^13.0.0, yargs@^13.2.1, yargs@^13.2.4: - version "13.3.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.1" - -yargs@^14.0.0: - version "14.2.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-14.2.0.tgz#f116a9242c4ed8668790b40759b4906c276e76c3" - integrity sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg== - dependencies: - cliui "^5.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^15.0.0" - -yargs@^15.3.1: - version "15.3.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" - integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== - 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.1" diff --git a/cmd/server/build.sh b/cmd/server/build.sh index 04e00a92ef3..0e9e6f1573d 100755 --- a/cmd/server/build.sh +++ b/cmd/server/build.sh @@ -83,9 +83,6 @@ cp -a ./cmd/symbols/.ctags.d "$OUTPUT" cp -a ./cmd/symbols/ctags-install-alpine.sh "$OUTPUT" cp -a ./dev/libsqlite3-pcre/install-alpine.sh "$OUTPUT/libsqlite3-pcre-install-alpine.sh" -echo "--- precise code intel" -cp -a ./cmd/precise-code-intel "$OUTPUT" - echo "--- monitoring generation" pushd monitoring && go generate && popd diff --git a/dev/ci/yarn-run.sh b/dev/ci/yarn-run.sh index ae9dfb84f1e..9eb214df86c 100755 --- a/dev/ci/yarn-run.sh +++ b/dev/ci/yarn-run.sh @@ -5,7 +5,6 @@ set -e echo "--- yarn" # mutex is necessary since CI runs various yarn installs in parallel yarn --mutex network --frozen-lockfile --network-timeout 60000 -yarn --mutex network --cwd cmd/precise-code-intel --frozen-lockfile --network-timeout 60000 yarn --mutex network --cwd dev/release --frozen-lockfile --network-timeout 60000 for cmd in "$@"; do diff --git a/dev/foreach-ts-project.sh b/dev/foreach-ts-project.sh index 10613f31cb6..0ed1ca0fcd7 100755 --- a/dev/foreach-ts-project.sh +++ b/dev/foreach-ts-project.sh @@ -16,7 +16,6 @@ DIRS=( browser packages/sourcegraph-extension-api packages/@sourcegraph/extension-api-types - cmd/precise-code-intel dev/release ) diff --git a/dev/licenses-npm.sh b/dev/licenses-npm.sh index 4fdb35d78d8..b847e2cad97 100755 --- a/dev/licenses-npm.sh +++ b/dev/licenses-npm.sh @@ -9,8 +9,4 @@ FAIL_ON='UNKNOWN;GPL-1.0-only;GPL-1.0-or-later;GPL-2.0-only;GPL-2.0-or-later;GPL { # Webapp, native integrations and browser extension ./node_modules/.bin/license-checker --production --csv --failOn "$FAIL_ON" - # LSIF - pushd cmd/precise-code-intel >/dev/null - ../../node_modules/.bin/license-checker --production --csv --failOn "$FAIL_ON" | tail -n +2 - popd >/dev/null } | uniq >ThirdPartyLicensesNpm.csv diff --git a/dev/start.sh b/dev/start.sh index 9ba60d683c2..e5cf5b95a06 100755 --- a/dev/start.sh +++ b/dev/start.sh @@ -117,11 +117,6 @@ if [[ -n "$yarn_pid" ]]; then wait "$yarn_pid" fi -# Install precise code intel dependencies -pushd ./cmd/precise-code-intel 1>/dev/null -yarn --no-progress -popd 1>/dev/null - # Increase ulimit (not needed on Windows/WSL) # shellcheck disable=SC2015 type ulimit >/dev/null && ulimit -n 10000 || true diff --git a/doc/dev/architecture/index.md b/doc/dev/architecture/index.md index 416475aa8c1..b0a550426c4 100644 --- a/doc/dev/architecture/index.md +++ b/doc/dev/architecture/index.md @@ -34,7 +34,6 @@ Our backend is composed of multiple services: - Most are Go services found in the [cmd](https://sourcegraph.com/github.com/sourcegraph/sourcegraph/-/tree/cmd) folder. - [Syntect server](https://sourcegraph.com/github.com/sourcegraph/syntect_server) is our syntax highlighting service written in Rust. It is not horizontally scalable so only 1 replica is supported. -- [Precise code intel system](https://github.com/sourcegraph/sourcegraph/tree/master/cmd/precise-code-intel) provides precise code intelligence based on the LSIF data format. It is written in TypeScript. - [zoekt-indexserver](https://sourcegraph.com/github.com/sourcegraph/zoekt/-/tree/cmd/zoekt-sourcegraph-indexserver) and [zoekt-webserver](https://sourcegraph.com/github.com/sourcegraph/zoekt/-/tree/cmd/zoekt-webserver) provide indexed search. They are written in Go. ## Infrastructure diff --git a/doc/dev/tech_stack.md b/doc/dev/tech_stack.md index 127963c02ea..6c8ea1aacf2 100644 --- a/doc/dev/tech_stack.md +++ b/doc/dev/tech_stack.md @@ -33,10 +33,6 @@ Why do we put up with this pain? As of October 2019, Syntect continues to be the - [Syntect vs. VS Code syntax highlighting ](https://docs.google.com/document/d/1MqqEgihKzRehdDS_k9kb8t_p8vROCymC2FWn1Yvj6Ng/edit) -### Precise code intel system - -The processes in the [precise code intel](https://sourcegraph.com/github.com/sourcegraph/sourcegraph/-/tree/cmd/precise-code-intel) system were written in TypeScript so we could directly depend on the official [LSIF type definitions that are published by Microsoft as a TypeScript interface](https://github.com/microsoft/lsif-node/blob/master/protocol/src/protocol.ts). - ### LSIF generators and language servers LSIF generators and language servers should usually be written in the language that they are designed to analyze for two reasons: diff --git a/jest.config.js b/jest.config.js index 74aeaaa5938..cbf5f403b97 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,11 +5,5 @@ const config = require('./jest.config.base') /** @type {jest.InitialOptions} */ module.exports = { - projects: [ - 'browser/jest.config.js', - 'shared/jest.config.js', - 'web/jest.config.js', - 'cmd/precise-code-intel/jest.config.js', - '.storybook/jest.config.js', - ], + projects: ['browser/jest.config.js', 'shared/jest.config.js', 'web/jest.config.js', '.storybook/jest.config.js'], } diff --git a/renovate.json b/renovate.json index ed59bfbf113..691b0f269d8 100644 --- a/renovate.json +++ b/renovate.json @@ -15,10 +15,6 @@ { "packageNames": ["@octokit/rest", "@slack/web-api", "googleapis"], "reviewers": ["beyang"] - }, - { - "paths": ["cmd/precise-code-intel/*"], - "reviewers": ["team:code-intel"] } ] } diff --git a/tsconfig.json b/tsconfig.json index a23afab07a4..646584f8bce 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,6 @@ { "path": "web/src/regression" }, { "path": "browser" }, { "path": "browser/src/e2e" }, - { "path": "cmd/precise-code-intel" }, { "path": "schema" }, { "path": "packages/sourcegraph-extension-api" }, { "path": "packages/@sourcegraph/extension-api-types" },