diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 272ea1648d7b..ca4e73cedb5f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -421,3 +421,36 @@ jobs: run: | cd packages/utils yarn test:package + + job_node_integration_tests: + name: Node SDK Integration Tests (${{ matrix.node }}) + needs: job_build + runs-on: ubuntu-latest + timeout-minutes: 10 + continue-on-error: true + strategy: + matrix: + node: [10, 12, 14, 16] + steps: + - name: Check out current commit (${{ github.sha }}) + uses: actions/checkout@v2 + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + - name: Check dependency cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Check build cache + uses: actions/cache@v2 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: ${{ env.BUILD_CACHE_KEY }} + - name: Run integration tests + env: + NODE_VERSION: ${{ matrix.node }} + run: | + cd packages/node-integration-tests + yarn test diff --git a/package.json b/package.json index df059e449faf..4567aa2786cc 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "lint:eslint": "lerna run --parallel lint:eslint", "prepublishOnly": "lerna run --stream --concurrency 1 prepublishOnly", "postpublish": "make publish-docs && lerna run --stream --concurrency 1 postpublish", - "test": "lerna run --ignore @sentry-internal/browser-integration-tests --stream --concurrency 1 --sort test", + "test": "lerna run --ignore @sentry-internal/browser-integration-tests @sentry-internal/node-integration-tests --stream --concurrency 1 --sort test", "test-ci": "ts-node ./scripts/test.ts" }, "volta": { @@ -43,6 +43,7 @@ "packages/minimal", "packages/nextjs", "packages/node", + "packages/node-integration-tests", "packages/react", "packages/serverless", "packages/tracing", diff --git a/packages/node-integration-tests/.eslintrc.js b/packages/node-integration-tests/.eslintrc.js new file mode 100644 index 000000000000..fdcde4fa0f14 --- /dev/null +++ b/packages/node-integration-tests/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + env: { + node: true, + jest: true, + }, + extends: ['../../.eslintrc.js'], + parserOptions: { + sourceType: 'module', + }, +}; diff --git a/packages/node-integration-tests/jest.config.js b/packages/node-integration-tests/jest.config.js new file mode 100644 index 000000000000..8edea137a75b --- /dev/null +++ b/packages/node-integration-tests/jest.config.js @@ -0,0 +1,10 @@ +const config = { + transform: { + '^.+\\.ts$': 'ts-jest', + }, + testEnvironment: 'node', + testMatch: ['**/test.ts'], + moduleFileExtensions: ['js', 'ts'], +}; + +module.exports = config; diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json new file mode 100644 index 000000000000..fc60787e5dea --- /dev/null +++ b/packages/node-integration-tests/package.json @@ -0,0 +1,21 @@ +{ + "name": "@sentry-internal/node-integration-tests", + "version": "6.18.2", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "private": true, + "scripts": { + "lint": "run-s lint:prettier lint:eslint", + "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", + "lint:prettier": "prettier --check \"{suites,utils}/**/*.ts\"", + "type-check": "tsc", + "test": "jest --detectOpenHandles --runInBand --forceExit" + }, + "dependencies": { + "express": "^4.17.3", + "nock": "^13.1.0", + "portfinder": "^1.0.28" + } +} diff --git a/packages/node-integration-tests/suites/public-api/capture-exception/scenario.ts b/packages/node-integration-tests/suites/public-api/capture-exception/scenario.ts new file mode 100644 index 000000000000..b2a41905210c --- /dev/null +++ b/packages/node-integration-tests/suites/public-api/capture-exception/scenario.ts @@ -0,0 +1,8 @@ +import * as Sentry from '@sentry/node'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', +}); + +Sentry.captureException(new Error('Captured Error')); diff --git a/packages/node-integration-tests/suites/public-api/capture-exception/test.ts b/packages/node-integration-tests/suites/public-api/capture-exception/test.ts new file mode 100644 index 000000000000..3911d6b57c85 --- /dev/null +++ b/packages/node-integration-tests/suites/public-api/capture-exception/test.ts @@ -0,0 +1,17 @@ +import { assertSentryEvent, getEventRequest, runServer } from '../../../utils'; + +test('should send captureException', async () => { + const url = await runServer(__dirname); + const requestBody = await getEventRequest(url); + + assertSentryEvent(requestBody, { + exception: { + values: [ + { + type: 'Error', + value: 'Captured Error', + }, + ], + }, + }); +}); diff --git a/packages/node-integration-tests/suites/public-api/capture-message/scenario.ts b/packages/node-integration-tests/suites/public-api/capture-message/scenario.ts new file mode 100644 index 000000000000..e358175e8702 --- /dev/null +++ b/packages/node-integration-tests/suites/public-api/capture-message/scenario.ts @@ -0,0 +1,8 @@ +import * as Sentry from '@sentry/node'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', +}); + +Sentry.captureMessage('Message'); diff --git a/packages/node-integration-tests/suites/public-api/capture-message/test.ts b/packages/node-integration-tests/suites/public-api/capture-message/test.ts new file mode 100644 index 000000000000..0b15bed6204a --- /dev/null +++ b/packages/node-integration-tests/suites/public-api/capture-message/test.ts @@ -0,0 +1,11 @@ +import { assertSentryEvent, getEventRequest, runServer } from '../../../utils'; + +test('should send captureMessage', async () => { + const url = await runServer(__dirname); + const requestBody = await getEventRequest(url); + + assertSentryEvent(requestBody, { + message: 'Message', + level: 'info', + }); +}); diff --git a/packages/node-integration-tests/suites/tracing/manual-tracing-namespace-import/scenario.ts b/packages/node-integration-tests/suites/tracing/manual-tracing-namespace-import/scenario.ts new file mode 100644 index 000000000000..58c0a83955f0 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/manual-tracing-namespace-import/scenario.ts @@ -0,0 +1,12 @@ +import * as Sentry from '@sentry/node'; +import * as _ from '@sentry/tracing'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, +}); + +const transaction = Sentry.startTransaction({ name: 'test_transaction_1' }); + +transaction.finish(); diff --git a/packages/node-integration-tests/suites/tracing/manual-tracing-namespace-import/test.ts b/packages/node-integration-tests/suites/tracing/manual-tracing-namespace-import/test.ts new file mode 100644 index 000000000000..0df4b0d45611 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/manual-tracing-namespace-import/test.ts @@ -0,0 +1,12 @@ +import { assertSentryTransaction, getEnvelopeRequest, runServer } from '../../../utils'; + +test.skip('should send a manually started transaction when @sentry/tracing is imported using namespace import.', async () => { + const url = await runServer(__dirname); + const envelope = await getEnvelopeRequest(url); + + expect(envelope).toHaveLength(3); + + assertSentryTransaction(envelope[2], { + transaction: 'test_transaction_1', + }); +}); diff --git a/packages/node-integration-tests/suites/tracing/manual-tracing-unnamed-import/scenario.ts b/packages/node-integration-tests/suites/tracing/manual-tracing-unnamed-import/scenario.ts new file mode 100644 index 000000000000..c5134123351b --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/manual-tracing-unnamed-import/scenario.ts @@ -0,0 +1,14 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import '@sentry/tracing'; + +import * as Sentry from '@sentry/node'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, +}); + +const transaction = Sentry.startTransaction({ name: 'test_transaction_1' }); + +transaction.finish(); diff --git a/packages/node-integration-tests/suites/tracing/manual-tracing-unnamed-import/test.ts b/packages/node-integration-tests/suites/tracing/manual-tracing-unnamed-import/test.ts new file mode 100644 index 000000000000..db4caddd9bce --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/manual-tracing-unnamed-import/test.ts @@ -0,0 +1,12 @@ +import { assertSentryTransaction, getEnvelopeRequest, runServer } from '../../../utils'; + +test('should send a manually started transaction when @sentry/tracing is imported using unnamed import.', async () => { + const url = await runServer(__dirname); + const envelope = await getEnvelopeRequest(url); + + expect(envelope).toHaveLength(3); + + assertSentryTransaction(envelope[2], { + transaction: 'test_transaction_1', + }); +}); diff --git a/packages/node-integration-tests/tsconfig.json b/packages/node-integration-tests/tsconfig.json new file mode 100644 index 000000000000..87d045dbc42d --- /dev/null +++ b/packages/node-integration-tests/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "esModuleInterop": true, + "types": ["jest", "node"] + }, + "include": ["**/*.ts", "jest.config.js"], + "exclude": ["node_modules"] +} diff --git a/packages/node-integration-tests/utils/defaults/server.ts b/packages/node-integration-tests/utils/defaults/server.ts new file mode 100644 index 000000000000..3cf8cadab65a --- /dev/null +++ b/packages/node-integration-tests/utils/defaults/server.ts @@ -0,0 +1,5 @@ +import express from 'express'; + +const app = express(); + +export default app; diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts new file mode 100644 index 000000000000..73c5cdfc9191 --- /dev/null +++ b/packages/node-integration-tests/utils/index.ts @@ -0,0 +1,81 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { Express } from 'express'; +import * as http from 'http'; +import nock from 'nock'; +import * as path from 'path'; +import { getPortPromise } from 'portfinder'; + +const assertSentryEvent = (actual: Record, expected: Record): void => { + expect(actual).toMatchObject({ + event_id: expect.any(String), + timestamp: expect.any(Number), + ...expected, + }); +}; + +const assertSentryTransaction = (actual: Record, expected: Record): void => { + expect(actual).toMatchObject({ + event_id: expect.any(String), + timestamp: expect.any(Number), + start_timestamp: expect.any(Number), + spans: expect.any(Array), + type: 'transaction', + ...expected, + }); +}; + +const parseEnvelope = (body: string): Array> => { + return body.split('\n').map(e => JSON.parse(e)); +}; + +const getEventRequest = async (url: string): Promise> => { + return new Promise(resolve => { + nock('https://dsn.ingest.sentry.io') + .post('/api/1337/store/', body => { + resolve(body); + return true; + }) + .reply(200); + + http.get(url); + }); +}; + +const getEnvelopeRequest = async (url: string): Promise>> => { + return new Promise(resolve => { + nock('https://dsn.ingest.sentry.io') + .post('/api/1337/envelope/', body => { + const envelope = parseEnvelope(body); + resolve(envelope); + return true; + }) + .reply(200); + + http.get(url); + }); +}; + +async function runServer(testDir: string, serverPath?: string, scenarioPath?: string): Promise { + const port = await getPortPromise(); + const url = `http://localhost:${port}/test`; + const defaultServerPath = path.resolve(process.cwd(), 'utils', 'defaults', 'server'); + + await new Promise(resolve => { + // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access + const app = require(serverPath || defaultServerPath).default as Express; + + app.get('/test', async () => { + require(scenarioPath || `${testDir}/scenario`); + + setTimeout(() => server.close(), 500); + }); + + const server = app.listen(port, () => { + resolve(); + }); + }); + + return url; +} + +export { assertSentryEvent, assertSentryTransaction, parseEnvelope, getEventRequest, getEnvelopeRequest, runServer }; diff --git a/yarn.lock b/yarn.lock index 66e4ed7ea594..763d2fe14cf5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4467,6 +4467,14 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + acorn-globals@^4.1.0, acorn-globals@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" @@ -6019,7 +6027,7 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" -body-parser@^1.18.3, body-parser@^1.19.0: +body-parser@1.19.2, body-parser@^1.18.3, body-parser@^1.19.0: version "1.19.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== @@ -7774,6 +7782,13 @@ content-disposition@0.5.3: dependencies: safe-buffer "5.1.2" +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -7889,6 +7904,11 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + cookie@^0.4.1, cookie@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" @@ -10528,6 +10548,42 @@ express@^4.10.7, express@^4.16.4, express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" +express@^4.17.3: + version "4.17.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" + integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.19.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.4.2" + 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.7" + qs "6.9.7" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.17.2" + serve-static "1.14.2" + setprototypeof "1.2.0" + 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.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -11075,6 +11131,11 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -15390,7 +15451,7 @@ mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.26, m dependencies: mime-db "1.47.0" -mime-types@^2.1.27: +mime-types@^2.1.27, mime-types@~2.1.34: version "2.1.34" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== @@ -15653,7 +15714,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -15727,6 +15788,11 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -15833,6 +15899,16 @@ nock@^13.0.4, nock@^13.0.5: lodash.set "^4.3.2" propagate "^2.0.0" +nock@^13.1.0: + version "13.2.4" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.4.tgz#43a309d93143ee5cdcca91358614e7bde56d20e1" + integrity sha512-8GPznwxcPNCH/h8B+XZcKjYPXnUV5clOKCjAqyjsiqA++MpNx9E9+t8YPp0MbThO+KauRo7aZJ1WuIZmOrT2Ug== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash.set "^4.3.2" + propagate "^2.0.0" + node-environment-flags@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" @@ -17781,6 +17857,14 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.1" +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -19007,7 +19091,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -19180,6 +19264,25 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" +send@0.17.2: + version "0.17.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== + 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.8.1" + mime "1.6.0" + ms "2.1.3" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + serialize-javascript@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" @@ -19204,6 +19307,16 @@ serve-static@1.14.1: parseurl "~1.3.3" send "0.17.1" +serve-static@1.14.2: + version "1.14.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" + integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.2" + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"