diff --git a/.eslintrc.js b/.eslintrc.js index d7ffbc94..e0fc9fa0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -29,6 +29,8 @@ module.exports = { 'import/newline-after-import': 'error', 'import/no-duplicates': 'error', 'no-multiple-empty-lines': 'error', + 'lines-between-class-members': 'off', + '@typescript-eslint/lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true, exceptAfterOverload: true }], }, settings: { 'import/parsers': { diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 73eee9ea..278938e9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,6 +9,11 @@ jobs: # Setup environment and checkout the project master - name: Setup Node.js environment uses: actions/setup-node@v1.4.2 + with: + registry-url: "https://npm.pkg.github.com" + scope: "@superindustries" + always-auth: true + - name: Checkout uses: actions/checkout@v2 @@ -27,6 +32,8 @@ jobs: # Install and run tests - name: Install dependencies run: yarn install + env: + NODE_AUTH_TOKEN: ${{ secrets.SUPERFACE_BOT_PAT }} - name: Test run: yarn test @@ -36,6 +43,11 @@ jobs: # Setup environment and checkout the project master - name: Setup Node.js environment uses: actions/setup-node@v1.4.2 + with: + registry-url: "https://npm.pkg.github.com" + scope: "@superindustries" + always-auth: true + - name: Checkout uses: actions/checkout@v2 @@ -54,6 +66,8 @@ jobs: # Install and run lint - name: Install dependencies run: yarn install + env: + NODE_AUTH_TOKEN: ${{ secrets.SUPERFACE_BOT_PAT }} - name: Lint run: yarn lint @@ -63,6 +77,11 @@ jobs: # Setup environment and checkout the project master - name: Setup Node.js environment uses: actions/setup-node@v1.4.2 + with: + registry-url: "https://npm.pkg.github.com" + scope: "@superindustries" + always-auth: true + - name: Checkout uses: actions/checkout@v2 @@ -81,6 +100,8 @@ jobs: # Install and run license checker - name: Install dependencies run: yarn install + env: + NODE_AUTH_TOKEN: ${{ secrets.SUPERFACE_BOT_PAT }} - name: Install License checker run: | yarn global add license-checker diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2fef2854..b4549bf2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -20,6 +20,8 @@ jobs: # Install dependencies and run test - name: Install dependencies run: yarn install + env: + NODE_AUTH_TOKEN: ${{ secrets.SUPERFACE_BOT_PAT }} - name: Test run: yarn test diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..ac891f80 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@superindustries:registry=https://npm.pkg.github.com diff --git a/package.json b/package.json index ced2022a..d2a95222 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "types": "dist/index.d.ts", "repository": "https://github.com/superindustries/superface.git", "author": "Superface Team", - "license": "MIT", "private": false, "files": [ "dist/**/*" @@ -40,12 +39,16 @@ "eslint-plugin-simple-import-sort": "^5.0.3", "jest": "^26.0.1", "microbundle": "^0.12.0", + "mockttp": "^0.20.3", "prettier": "^2.0.5", "rimraf": "^3.0.2", "ts-jest": "^26.1.0", - "typescript": "^3.9.2" + "typescript": "^3.9.6" }, "dependencies": { + "@superindustries/language": "^0.0.10", + "cross-fetch": "^3.0.5", + "isomorphic-form-data": "^2.0.0", "vm2": "^3.9.2" } } diff --git a/src/client/interpreter/Sandbox.ts b/src/client/interpreter/Sandbox.ts index 35bcd76f..fb780daa 100644 --- a/src/client/interpreter/Sandbox.ts +++ b/src/client/interpreter/Sandbox.ts @@ -1,10 +1,17 @@ import { VM } from 'vm2'; +import { Variables } from '../../internal/interpreter/interfaces'; + export const SCRIPT_TIMEOUT = 100; -export function evalScript(js: string): unknown { +export function evalScript( + js: string, + variableDefinitions?: Variables +): string { const vm = new VM({ - sandbox: {}, + sandbox: { + ...variableDefinitions, + }, compiler: 'javascript', wasm: false, eval: false, @@ -21,7 +28,7 @@ export function evalScript(js: string): unknown { delete global.require // Forbidden delete global.process // Forbidden delete global.console // Forbidden/useless - + delete global.setTimeout delete global.setInterval delete global.setImmediate @@ -50,5 +57,5 @@ export function evalScript(js: string): unknown { ` ); - return vm.run(`'use strict'; ${js}`); + return vm.run(`'use strict';${js}`); } diff --git a/src/index.test.ts b/src/index.test.ts deleted file mode 100644 index 61529d31..00000000 --- a/src/index.test.ts +++ /dev/null @@ -1,5 +0,0 @@ -describe('sanity check', () => { - it('works', () => { - expect(true).toBeTruthy(); - }); -}); diff --git a/src/internal/http.test.ts b/src/internal/http.test.ts new file mode 100644 index 00000000..0533af07 --- /dev/null +++ b/src/internal/http.test.ts @@ -0,0 +1,39 @@ +import { getLocal } from 'mockttp'; + +import { HttpClient } from './http'; + +const mockServer = getLocal(); + +describe('HttpClient', () => { + beforeEach(async () => { + await mockServer.start(); + }); + + afterEach(async () => { + await mockServer.stop(); + }); + + it('gets basic response', async () => { + await mockServer.get('/valid').thenJson(200, { response: 'valid' }); + const url = mockServer.urlFor('/valid'); + const response = await HttpClient.request(url, { + method: 'get', + accept: 'application/json', + }); + expect(response.statusCode).toEqual(200); + expect(response.body).toEqual({ response: 'valid' }); + }); + + it('gets error response', async () => { + await mockServer + .get('/invalid') + .thenJson(404, { error: { message: 'Not found' } }); + const url = mockServer.urlFor('/invalid'); + const response = await HttpClient.request(url, { + method: 'get', + accept: 'application/json', + }); + expect(response.statusCode).toEqual(404); + expect(response.body).toEqual({ error: { message: 'Not found' } }); + }); +}); diff --git a/src/internal/http.ts b/src/internal/http.ts new file mode 100644 index 00000000..24364940 --- /dev/null +++ b/src/internal/http.ts @@ -0,0 +1,195 @@ +import 'isomorphic-form-data'; + +import fetch, { Headers } from 'cross-fetch'; + +import { evalScript } from '../client/interpreter/Sandbox'; +import { Variables } from './interpreter/interfaces'; + +export interface HttpResponse { + statusCode: number; + body: unknown; + headers: Record; +} + +const AUTH_HEADER_NAME = 'Authorization'; +const JSON_CONTENT = 'application/json'; +const URLENCODED_CONTENT = 'application/x-www-form-urlencoded'; +const FORMDATA_CONTENT = 'multipart/form-data'; + +const variablesToStrings = (variables?: Variables): Record => { + const result: Record = {}; + + if (variables) { + for (const [key, value] of Object.entries(variables)) { + result[key] = typeof value === 'string' ? value : JSON.stringify(value); + } + } + + return result; +}; + +const queryParameters = (parameters?: Record): string => { + if (parameters && Object.keys(parameters).length) { + return '?' + new URLSearchParams(parameters).toString(); + } + + return ''; +}; + +const basicAuth = (auth?: { username: string; password: string }): string => { + if (!auth || !auth.username || !auth.password) { + throw new Error('Missing credentials for Basic Auth!'); + } + + return ( + 'Basic ' + + Buffer.from(`${auth.username}:${auth.password}`).toString('base64') + ); +}; + +const bearerAuth = (auth?: { token: string }): string => { + if (!auth || !auth.token) { + throw new Error('Missing token for Bearer Auth!'); + } + + return `Bearer ${auth.token}`; +}; + +const formData = (data?: Record): FormData => { + const formData = new FormData(); + + if (data) { + Object.entries(data).forEach(([key, value]) => formData.append(key, value)); + } + + return formData; +}; + +const createUrl = ( + inputUrl: string, + parameters: { + baseUrl?: string; + pathParameters?: Variables; + queryParameters?: Record; + } +): string => { + const query = queryParameters(parameters.queryParameters); + const isRelative = /^\/[^/]/.test(inputUrl); + + if (isRelative && !parameters.baseUrl) { + throw new Error('Relative URL specified, but base URL not provided!'); + } + + let url = isRelative ? `${parameters.baseUrl}${inputUrl}` : inputUrl; + + if (parameters.pathParameters) { + const pathParameters = Object.keys(parameters.pathParameters); + const replacements: string[] = []; + + const regex = RegExp('{(.*?)}', 'g'); + let replacement: RegExpExecArray | null; + while ((replacement = regex.exec(url)) !== null) { + replacements.push(replacement[1]); + } + + const missingKeys = replacements.filter( + key => !pathParameters.includes(key) + ); + + if (missingKeys.length) { + throw new Error( + `Values for URL replacement keys not found: ${missingKeys.join(', ')}` + ); + } + + for (const param of pathParameters) { + url = url.replace( + `{${param}}`, + evalScript(param, parameters.pathParameters) + ); + } + } + + return `${url}${query}`; +}; + +export const HttpClient = { + request: async ( + url: string, + parameters: { + method: string; + headers?: Variables; + queryParameters?: Variables; + body?: Variables; + contentType?: string; + accept?: string; + security?: 'basic' | 'bearer' | 'other'; + basic?: { username: string; password: string }; + bearer?: { token: string }; + baseUrl?: string; + pathParameters?: Variables; + } + ): Promise => { + const headers = new Headers(variablesToStrings(parameters?.headers)); + headers.append('Accept', parameters.accept ?? '*/*'); + + const params: RequestInit = { + headers, + method: parameters.method, + }; + + if ( + parameters.body && + ['post', 'put', 'patch'].includes(parameters.method.toLowerCase()) + ) { + if (parameters.contentType === JSON_CONTENT) { + headers.append('Content-Type', JSON_CONTENT); + params.body = JSON.stringify(parameters.body); + } else if (parameters.contentType === URLENCODED_CONTENT) { + headers.append('Content-Type', URLENCODED_CONTENT); + params.body = new URLSearchParams(variablesToStrings(parameters.body)); + } else if (parameters.contentType === FORMDATA_CONTENT) { + headers.append('Content-Type', FORMDATA_CONTENT); + params.body = formData(variablesToStrings(parameters.body)); + } else { + throw new Error(`Unknown content type: ${parameters.contentType}`); + } + } + + if (parameters.security === 'basic') { + headers.append(AUTH_HEADER_NAME, basicAuth(parameters.basic)); + } else if (parameters.security === 'bearer') { + headers.append(AUTH_HEADER_NAME, bearerAuth(parameters.bearer)); + } + + const response = await fetch( + encodeURI( + createUrl(url, { + baseUrl: parameters.baseUrl, + pathParameters: parameters.pathParameters, + queryParameters: variablesToStrings(parameters.queryParameters), + }) + ), + params + ); + + let body: unknown; + + if (parameters.accept === JSON_CONTENT) { + body = await response.json(); + } else { + body = await response.text(); + } + + const responseHeaders: Record = {}; + response.headers.forEach((key, value) => { + responseHeaders[key] = value; + }); + + return { + statusCode: response.status, + body, + headers: responseHeaders, + }; + }, +}; diff --git a/src/internal/interpreter/interfaces.ts b/src/internal/interpreter/interfaces.ts new file mode 100644 index 00000000..45a22e9c --- /dev/null +++ b/src/internal/interpreter/interfaces.ts @@ -0,0 +1,59 @@ +import { + EvalDefinitionNode, + HTTPOperationDefinitionNode, + IterationDefinitionNode, + JSExpressionNode, + MapASTNode, + MapDefinitionNode, + MapDocumentNode, + MapExpressionDefinitionNode, + MapNode, + MapProfileIdNode, + NetworkOperationDefinitionNode, + OperationCallDefinitionNode, + OperationDefinitionNode, + OutcomeDefinitionNode, + ProviderNode, + StepDefinitionNode, + VariableExpressionDefinitionNode, +} from '@superindustries/language'; + +export type Variables = { + [key: string]: string | Variables | undefined; +}; + +export interface MapVisitor { + visit(node: MapASTNode): Promise | unknown; + visitEvalDefinitionNode(node: EvalDefinitionNode): Promise | unknown; + visitHTTPOperationDefinitionNode( + node: HTTPOperationDefinitionNode + ): Promise | unknown; + visitIterationDefinitionNode( + node: IterationDefinitionNode + ): Promise | unknown; + visitJSExpressionNode(node: JSExpressionNode): Promise | unknown; + visitMapDefinitionNode(node: MapDefinitionNode): Promise | unknown; + visitMapDocumentNode(node: MapDocumentNode): Promise | unknown; + visitMapExpressionDefinitionNode( + node: MapExpressionDefinitionNode + ): Promise | unknown; + visitMapNode(node: MapNode): Promise | unknown; + visitNetworkOperationDefinitionNode( + node: NetworkOperationDefinitionNode + ): Promise | unknown; + visitOperationCallDefinitionNode( + node: OperationCallDefinitionNode + ): Promise | unknown; + visitOperationDefinitionNode( + node: OperationDefinitionNode + ): Promise | unknown; + visitOutcomeDefinitionNode( + node: OutcomeDefinitionNode + ): Promise | unknown; + visitProfileIdNode(node: MapProfileIdNode): Promise | unknown; + visitProviderNode(node: ProviderNode): Promise | unknown; + visitStepDefinitionNode(node: StepDefinitionNode): Promise | unknown; + visitVariableExpressionDefinitionNode( + node: VariableExpressionDefinitionNode + ): Promise | unknown; +} diff --git a/src/internal/interpreter/map-interpreter.test.ts b/src/internal/interpreter/map-interpreter.test.ts new file mode 100644 index 00000000..6a304adf --- /dev/null +++ b/src/internal/interpreter/map-interpreter.test.ts @@ -0,0 +1,2013 @@ +import { MapASTNode } from '@superindustries/language'; +import { getLocal } from 'mockttp'; + +import { MapInterpereter } from './map-interpreter'; + +const mockServer = getLocal(); + +describe('MapInterpreter', () => { + beforeEach(async () => { + await mockServer.start(); + }); + + afterEach(async () => { + await mockServer.stop(); + }); + + it('should fail with invalid AST', async () => { + const interpreter = new MapInterpereter({}); + await expect( + async () => + await interpreter.visit(({ kind: 'Invalid' } as unknown) as MapASTNode) + ).rejects.toThrow(); + }); + + it('should execute minimal Eval definition', async () => { + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + returnDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: '12', + }, + }, + ], + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 12 }); + }); + + it('should fail on undefined usecase', async () => { + const interpreter = new MapInterpereter({ usecase: 'nonexistent' }); + await expect( + async () => + await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + returnDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: '12', + }, + }, + ], + }, + }, + }, + ], + }, + ], + }) + ).rejects.toThrow('Usecase not found.'); + }); + + // This should not happen in practice, as the AST will be validated beforehand + it('should fail when none of result/return/set are defined', async () => { + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + await expect( + async () => + await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + }, + }, + }, + ], + }, + ], + }) + ).rejects.toThrow('Something went very wrong, this should not happen!'); + }); + + it('should execute Eval definition with variables', async () => { + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'x', + right: { + kind: 'JSExpression', + expression: '7', + }, + }, + ], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + returnDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'x + 5', + }, + }, + ], + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 12 }); + }); + + it('should correctly resolve variable scope', async () => { + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'x', + right: { + kind: 'JSExpression', + expression: '8', + }, + }, + ], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'x', + right: { + kind: 'JSExpression', + expression: '7', + }, + }, + ], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + returnDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'x + 5', + }, + }, + ], + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 12 }); + }); + + it('should run predefined operation', async () => { + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'OperationCallDefinition', + arguments: [], + operationName: 'my beloved operation', + successOutcomeDefinition: { + kind: 'OutcomeDefinition', + returnDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'variableFromOperation', + }, + }, + ], + }, + }, + }, + ], + }, + { + kind: 'OperationDefinition', + operationName: 'my beloved operation', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'step', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + setDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'variableFromOperation', + right: { + kind: 'JSExpression', + expression: '12', + }, + }, + ], + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 12 }); + }); + + it('should throw when trying to run undefined operation', async () => { + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + await expect( + async () => + await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'OperationCallDefinition', + arguments: [], + operationName: 'my beloved operation', + successOutcomeDefinition: { + kind: 'OutcomeDefinition', + returnDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'variableFromOperation', + }, + }, + ], + }, + }, + }, + ], + }, + { + kind: 'OperationDefinition', + operationName: 'my not-so-beloved operation', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'step', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + setDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'variableFromOperation', + right: { + kind: 'JSExpression', + expression: '12', + }, + }, + ], + }, + }, + }, + ], + }, + ], + }) + ).rejects.toThrow('Operation my beloved operation not found'); + }); + + it('should call an API', async () => { + await mockServer.get('/twelve').thenJson(200, { data: 12 }); + const url = mockServer.urlFor('/twelve'); + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url, + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'other', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 12 }); + }); + + it('should call an API with relative URL', async () => { + await mockServer.get('/twelve').thenJson(200, { data: 12 }); + const baseUrl = mockServer.urlFor('/twelve').replace('/twelve', ''); + const interpreter = new MapInterpereter({ usecase: 'testCase', baseUrl }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url: '/twelve', + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'other', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 12 }); + }); + + it('should throw when calling an API with relative URL but not providing baseUrl', async () => { + await mockServer.get('/twelve').thenJson(200, { data: 12 }); + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + await expect( + async () => + await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url: '/twelve', + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'other', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }) + ).rejects.toThrow('Relative URL specified, but base URL not provided!'); + }); + + it('should call an API with path parameters', async () => { + await mockServer.get('/twelve/2').thenJson(200, { data: 144 }); + const url = mockServer.urlFor('/twelve'); + const interpreter = new MapInterpereter({ + usecase: 'testCase', + input: { page: '2' }, + }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'settingStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + setDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'page', + right: { + kind: 'JSExpression', + expression: 'input.page', + }, + }, + ], + }, + }, + }, + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'httpCallStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url: `${url}/{page}`, + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'other', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 144 }); + }); + + it('should throw when calling an API with path parameters and some are missing', async () => { + const interpreter = new MapInterpereter({ + usecase: 'testCase', + }); + + await expect( + async () => + await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'settingStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + setDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'page', + right: { + kind: 'JSExpression', + expression: '2', + }, + }, + ], + }, + }, + }, + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'httpCallStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url: `//some.url/{missing}/{page}/{alsoMissing}`, + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'other', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }) + ).rejects.toThrow( + 'Values for URL replacement keys not found: missing, alsoMissing' + ); + }); + + it('should call an API with parameters', async () => { + await mockServer + .get('/twelve') + .withQuery({ page: 2 }) + .thenJson(200, { data: 144 }); + const url = mockServer.urlFor('/twelve'); + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'pageNumber', + right: { + kind: 'JSExpression', + expression: '2', + }, + }, + ], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url, + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'other', + queryParametersDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'page', + right: { + kind: 'JSExpression', + expression: 'pageNumber', + }, + }, + ], + }, + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 144 }); + }); + + it('should call an API with input', async () => { + await mockServer + .get('/twelve') + .withQuery({ page: 2 }) + .thenJson(200, { data: 144 }); + const url = mockServer.urlFor('/twelve'); + const interpreter = new MapInterpereter({ + usecase: 'testCase', + input: { page: '2' }, + }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'pageNumber', + right: { + kind: 'JSExpression', + expression: 'input.page', + }, + }, + ], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url, + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'other', + queryParametersDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'page', + right: { + kind: 'JSExpression', + expression: 'pageNumber', + }, + }, + ], + }, + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 144 }); + }); + + it('should call an API with parameters and POST request', async () => { + await mockServer + .post('/checkBody') + .withJsonBody({ anArray: [1, 2, 3] }) + .withHeaders({ someheader: 'hello' }) + .thenJson(201, { bodyOk: true, headerOk: true }); + const url = mockServer.urlFor('/checkBody'); + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url, + method: 'POST', + responseDefinition: { + statusCode: 201, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [ + { + kind: 'MapExpressionsDefinition', + left: 'anArray', + right: { + kind: 'JSExpression', + expression: '[1, 2, 3]', + }, + }, + ], + headers: [ + { + kind: 'VariableExpressionsDefinition', + left: 'SomeHeader', + right: { + kind: 'JSExpression', + expression: '"hello"', + }, + }, + ], + security: 'other', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ + result: { + headerOk: true, + bodyOk: true, + }, + }); + }); + + it('should run multi step operation', async () => { + await mockServer + .get('/first') + .thenJson(200, { firstStep: { someVar: 12 } }); + await mockServer.get('/second').thenJson(200, { secondStep: 5 }); + const url1 = mockServer.urlFor('/first'); + const url2 = mockServer.urlFor('/second'); + const interpreter = new MapInterpereter({ usecase: 'multistep' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'multistep', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'firstStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url: url1, + method: 'get', + requestDefinition: { + contentType: 'application/json', + queryParametersDefinition: [], + security: 'other', + headers: [], + body: [], + }, + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + setDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'someVariable', + right: { + kind: 'JSExpression', + expression: 'body.firstStep.someVar', + }, + }, + ], + }, + }, + }, + }, + }, + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'secondStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url: url2, + method: 'get', + requestDefinition: { + contentType: 'application/json', + queryParametersDefinition: [], + security: 'other', + headers: [], + body: [], + }, + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + setDefinition: [ + { + kind: 'VariableExpressionsDefinition', + left: 'someOtherVariable', + right: { + kind: 'JSExpression', + expression: 'body.secondStep', + }, + }, + ], + }, + }, + }, + }, + }, + { + kind: 'StepDefinition', + condition: { + kind: 'JSExpression', + expression: `typeof someOtherVariable !== 'undefined' && someOtherVariable && someOtherVariable < 10 && typeof someVariable !== undefined && someVariable && someVariable > 10`, + }, + variableExpressionsDefinition: [], + stepName: 'thirdStep', + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'someVariable * someOtherVariable', + }, + }, + ], + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 12 * 5 }); + }); + + it('should call an API with Basic auth', async () => { + await mockServer + .get('/basic') + .withHeaders({ Authorization: 'Basic bmFtZTpwYXNzd29yZA==' }) + .thenJson(200, { data: 12 }); + const url = mockServer.urlFor('/basic'); + const interpreter = new MapInterpereter({ + usecase: 'testCase', + auth: { basic: { username: 'name', password: 'password' } }, + }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url, + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'basic', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 12 }); + }); + + it('should call an API with Bearer auth', async () => { + await mockServer + .get('/bearer') + .withHeaders({ Authorization: 'Bearer SuperSecret' }) + .thenJson(200, { data: 12 }); + const url = mockServer.urlFor('/bearer'); + const interpreter = new MapInterpereter({ + usecase: 'testCase', + auth: { bearer: { token: 'SuperSecret' } }, + }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url, + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'bearer', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 12 }); + }); + + it('should call an API with multipart/form-data body', async () => { + await mockServer.post('/formdata').thenCallback(request => { + if ( + request.body.text && + request.body.text.includes('formData') && + request.body.text.includes('myFormData') && + request.body.text.includes('is') && + request.body.text.includes('present') + ) { + return { + json: { data: 12 }, + status: 201, + }; + } + + return { json: { failed: true }, statusCode: 400 }; + }); + const url = mockServer.urlFor('/formdata'); + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url, + method: 'POST', + responseDefinition: { + statusCode: 201, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'multipart/form-data', + body: [ + { + kind: 'MapExpressionsDefinition', + left: 'formData', + right: { + kind: 'JSExpression', + expression: '"myFormData"', + }, + }, + { + kind: 'MapExpressionsDefinition', + left: 'is', + right: { + kind: 'JSExpression', + expression: '"present"', + }, + }, + ], + headers: [], + security: 'other', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: 12 }); + }); + + it('should throw on an API with Basic auth, but without credentials', async () => { + const interpreter = new MapInterpereter({ + usecase: 'testCase', + }); + await expect( + async () => + await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url: '/unimportant', + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'basic', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }) + ).rejects.toThrow(); + }); + + it('should throw on an API with Bearer auth, but without credentials', async () => { + const interpreter = new MapInterpereter({ + usecase: 'testCase', + }); + await expect( + async () => + await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url: '/unimportant', + method: 'GET', + responseDefinition: { + statusCode: 200, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/json', + body: [], + headers: [], + security: 'bearer', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }) + ).rejects.toThrow(); + }); + + it('should call an API with application/x-www-form-urlencoded', async () => { + await mockServer + .post('/urlencoded') + .withForm({ form: 'is', o: 'k' }) + .thenJson(201, { data: 12 }); + const url = mockServer.urlFor('/urlencoded'); + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'NetworkOperationDefinition', + definition: { + kind: 'HTTPOperationDefinition', + variableExpressionsDefinition: [], + url, + method: 'POST', + responseDefinition: { + statusCode: 201, + contentType: 'application/json', + contentLanguage: 'en-US', + outcomeDefinition: { + kind: 'OutcomeDefinition', + resultDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result', + right: { + kind: 'JSExpression', + expression: 'body.data', + }, + }, + ], + }, + }, + requestDefinition: { + contentType: 'application/x-www-form-urlencoded', + body: [ + { + kind: 'MapExpressionsDefinition', + left: 'form', + right: { + kind: 'JSExpression', + expression: '"is"', + }, + }, + { + kind: 'MapExpressionsDefinition', + left: 'o', + right: { + kind: 'JSExpression', + expression: '"k"', + }, + }, + ], + headers: [], + security: 'other', + queryParametersDefinition: [], + }, + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ + result: 12, + }); + }); + + it('should execute Eval definition with nested result', async () => { + const interpreter = new MapInterpereter({ usecase: 'testCase' }); + const result = await interpreter.visit({ + kind: 'MapDocument', + map: { + kind: 'Map', + profileId: { + kind: 'ProfileId', + profileId: 'hello!', + }, + provider: { + kind: 'Provider', + providerId: 'hi!', + }, + }, + definitions: [ + { + kind: 'MapDefinition', + mapName: 'testMap', + usecaseName: 'testCase', + variableExpressionsDefinition: [], + stepsDefinition: [ + { + kind: 'StepDefinition', + variableExpressionsDefinition: [], + stepName: 'oneAndOnlyStep', + condition: { + kind: 'JSExpression', + expression: 'true', + }, + iterationDefinition: { + kind: 'IterationDefinition', + }, + run: { + kind: 'EvalDefinition', + outcomeDefinition: { + kind: 'OutcomeDefinition', + returnDefinition: [ + { + kind: 'MapExpressionsDefinition', + left: 'result.which.is.nested', + right: { + kind: 'JSExpression', + expression: '12', + }, + }, + ], + }, + }, + }, + ], + }, + ], + }); + + expect(result).toEqual({ result: { which: { is: { nested: 12 } } } }); + }); +}); diff --git a/src/internal/interpreter/map-interpreter.ts b/src/internal/interpreter/map-interpreter.ts new file mode 100644 index 00000000..32bd73ff --- /dev/null +++ b/src/internal/interpreter/map-interpreter.ts @@ -0,0 +1,423 @@ +import { + EvalDefinitionNode, + HTTPOperationDefinitionNode, + isMapDefinitionNode, + isOperationDefinitionNode, + IterationDefinitionNode, + JSExpressionNode, + MapASTNode, + MapDefinitionNode, + MapDocumentNode, + MapExpressionDefinitionNode, + MapNode, + MapProfileIdNode, + NetworkOperationDefinitionNode, + OperationCallDefinitionNode, + OperationDefinitionNode, + OutcomeDefinitionNode, + ProviderNode, + StepDefinitionNode, + VariableExpressionDefinitionNode, +} from '@superindustries/language'; + +import { evalScript } from '../../client/interpreter/Sandbox'; +import { HttpClient } from '../http'; +import { MapVisitor, Variables } from './interfaces'; + +function assertUnreachable(node: never): never; +function assertUnreachable(node: MapASTNode): never { + throw new Error(`Invalid Node kind: ${node.kind}`); +} + +export interface MapParameters { + usecase?: string; + auth?: { + basic?: { + username: string; + password: string; + }; + bearer?: { + token: string; + }; + }; + baseUrl?: string; + input?: Variables; +} + +export const mergeVariables = ( + left: Variables, + right: Variables +): Variables => { + const result: Variables = {}; + + for (const key of Object.keys(left)) { + result[key] = left[key]; + } + for (const key of Object.keys(right)) { + const l = left[key]; + const r = right[key]; + if (r && typeof r !== 'string' && typeof l === 'object') { + result[key] = mergeVariables(l, r); + } else { + result[key] = right[key]; + } + } + + return result; +}; + +export class MapInterpereter implements MapVisitor { + private variableStack: Variables[] = []; + private operations: OperationDefinitionNode[] = []; + private operationScopedVariables: Record = {}; + private operationScope: string | undefined; + private mapScopedVariables: Record = {}; + private mapScope: string | undefined; + + constructor(private readonly parameters: MapParameters) {} + + async visit( + node: OutcomeDefinitionNode | HTTPOperationDefinitionNode + ): Promise; + async visit( + node: VariableExpressionDefinitionNode | MapExpressionDefinitionNode + ): Promise; + async visit(node: MapASTNode): Promise; + async visit(node: MapASTNode): Promise { + switch (node.kind) { + case 'EvalDefinition': + return this.visitEvalDefinitionNode(node); + case 'HTTPOperationDefinition': + return this.visitHTTPOperationDefinitionNode(node); + case 'IterationDefinition': + return this.visitIterationDefinitionNode(node); + case 'JSExpression': + return this.visitJSExpressionNode(node); + case 'Map': + return this.visitMapNode(node); + case 'MapDefinition': + return this.visitMapDefinitionNode(node); + case 'MapDocument': + return this.visitMapDocumentNode(node); + case 'MapExpressionsDefinition': + return this.visitMapExpressionDefinitionNode(node); + case 'NetworkOperationDefinition': + return this.visitNetworkOperationDefinitionNode(node); + case 'OperationCallDefinition': + return this.visitOperationCallDefinitionNode(node); + case 'OperationDefinition': + return this.visitOperationDefinitionNode(node); + case 'OutcomeDefinition': + return this.visitOutcomeDefinitionNode(node); + case 'ProfileId': + return this.visitProfileIdNode(node); + case 'Provider': + return this.visitProviderNode(node); + case 'StepDefinition': + return this.visitStepDefinitionNode(node); + case 'VariableExpressionsDefinition': + return this.visitVariableExpressionDefinitionNode(node); + + default: + assertUnreachable(node); + } + } + + async visitEvalDefinitionNode( + node: EvalDefinitionNode + ): Promise { + return await this.visit(node.outcomeDefinition); + } + + async visitHTTPOperationDefinitionNode( + node: HTTPOperationDefinitionNode + ): Promise { + const variables = await this.processVariableExpressions( + node.variableExpressionsDefinition + ); + this.variableStack.push(variables); + + const queryParameters = await this.processVariableExpressions( + node.requestDefinition.queryParametersDefinition + ); + + const body = await this.processMapExpressions(node.requestDefinition.body); + + const headers = await this.processVariableExpressions( + node.requestDefinition.headers + ); + + const response = await HttpClient.request(node.url, { + queryParameters, + method: node.method, + body, + headers, + contentType: node.requestDefinition.contentType, + accept: node.responseDefinition.contentType, + security: node.requestDefinition.security, + basic: this.parameters.auth?.basic, + bearer: this.parameters.auth?.bearer, + baseUrl: this.parameters.baseUrl, + pathParameters: this.mapScope + ? this.mapScopedVariables[this.mapScope] + : undefined, + }); + + this.variableStack.push({ + body: response.body as string, + headers: response.headers, + }); + + return await this.visit(node.responseDefinition.outcomeDefinition); + } + + visitIterationDefinitionNode(_node: IterationDefinitionNode): never { + throw new Error('Method not implemented.'); + } + + visitJSExpressionNode(node: JSExpressionNode): string { + return evalScript(node.expression, this.variables); + } + + async visitMapDefinitionNode( + node: MapDefinitionNode + ): Promise { + this.mapScope = node.mapName; + + let result: string | Variables | undefined; + for (const step of node.stepsDefinition) { + const condition = await this.visit(step.condition); + + if (condition) { + const variables = await this.processVariableExpressions( + node.variableExpressionsDefinition + ); + + this.variableStack.push(variables); + const stepResult = await this.visit(step); + this.variableStack.pop(); + + if (stepResult) { + result = stepResult; + } + } + } + + this.mapScope = undefined; + + return result; + } + + async visitMapDocumentNode( + node: MapDocumentNode + ): Promise { + this.operations = node.definitions.filter(isOperationDefinitionNode); + + const operation = node.definitions + .filter(isMapDefinitionNode) + .find(definition => definition.usecaseName === this.parameters.usecase); + + if (!operation) { + throw new Error('Usecase not found.'); + } + + return await this.visit(operation); + } + + async visitMapExpressionDefinitionNode( + node: MapExpressionDefinitionNode + ): Promise { + const value = await this.visit(node.right); + const path = node.left.split('.'); + const result: Variables = {}; + let current: Variables = result; + + for (let i = 0; i < path.length; ++i) { + if (i !== path.length - 1) { + current = current[path[i]] = {}; + } else { + current[path[i]] = value; + } + } + + return result; + } + + visitMapNode(_node: MapNode): never { + throw new Error('Method not implemented.'); + } + + visitNetworkOperationDefinitionNode( + node: NetworkOperationDefinitionNode + ): Promise { + return this.visit(node.definition); + } + + async visitOperationCallDefinitionNode( + node: OperationCallDefinitionNode + ): Promise { + const operation = this.operations.find( + operation => operation.operationName === node.operationName + ); + + if (!operation) { + throw new Error(`Operation ${node.operationName} not found!`); + } + + let result = await this.visit(operation); + + this.operationScope = operation.operationName; + + if (!result) { + result = await this.visit(node.successOutcomeDefinition); + } + + this.operationScope = undefined; + + return result; + } + + async visitOperationDefinitionNode( + node: OperationDefinitionNode + ): Promise { + this.operationScope = node.operationName; + + let result: string | Variables | undefined; + for (const step of node.stepsDefinition) { + const condition = await this.visit(step.condition); + + if (condition) { + const variables = await this.processVariableExpressions( + node.variableExpressionsDefinition + ); + + this.variableStack.push(variables); + const stepResult = await this.visit(step); + this.variableStack.pop(); + + if (stepResult) { + result = stepResult; + } + } + } + + this.operationScope = undefined; + + return result; + } + + async visitOutcomeDefinitionNode( + node: OutcomeDefinitionNode + ): Promise { + if (node.returnDefinition) { + return await this.processMapExpressions(node.returnDefinition); + } else if (node.setDefinition) { + if (this.operationScope) { + this.operationScopedVariables[this.operationScope] = { + ...(this.operationScopedVariables[this.operationScope] ?? {}), + ...(await this.processVariableExpressions(node.setDefinition)), + }; + + return undefined; + } else if (this.mapScope) { + this.mapScopedVariables[this.mapScope] = { + ...(this.mapScopedVariables[this.mapScope] ?? {}), + ...(await this.processVariableExpressions(node.setDefinition)), + }; + + return undefined; + } + } else if (node.resultDefinition) { + return await this.processMapExpressions(node.resultDefinition); + } + throw new Error('Something went very wrong, this should not happen!'); + } + + visitProfileIdNode(_node: MapProfileIdNode): never { + throw new Error('Method not implemented.'); + } + + visitProviderNode(_node: ProviderNode): never { + throw new Error('Method not implemented.'); + } + + async visitStepDefinitionNode( + node: StepDefinitionNode + ): Promise { + const variables = await this.processVariableExpressions( + node.variableExpressionsDefinition + ); + + this.variableStack.push(variables); + const result = await this.visit(node.run); + this.variableStack.pop(); + + return result; + } + + async visitVariableExpressionDefinitionNode( + node: VariableExpressionDefinitionNode + ): Promise { + return { + [node.left]: await this.visit(node.right), + }; + } + + private get variables(): Variables { + let variables = this.variableStack.reduce( + (acc, variableDefinition) => ({ + ...acc, + ...variableDefinition, + }), + {} + ); + + if (this.mapScope && this.mapScopedVariables[this.mapScope]) { + variables = { + ...variables, + ...this.mapScopedVariables[this.mapScope], + }; + } + + if ( + this.operationScope && + this.operationScopedVariables[this.operationScope] + ) { + variables = { + ...variables, + ...this.operationScopedVariables[this.operationScope], + }; + } + + variables = { + ...variables, + input: this.parameters.input ?? {}, + }; + + return variables; + } + + private async processVariableExpressions( + expressions: VariableExpressionDefinitionNode[] + ): Promise { + let variables: Variables = {}; + for (const expression of expressions) { + const result = await this.visit(expression); + variables = { ...variables, ...result }; + } + + return variables; + } + + private async processMapExpressions( + expressions: MapExpressionDefinitionNode[] + ): Promise { + let variables: Variables = {}; + for (const expression of expressions) { + const result = await this.visit(expression); + variables = mergeVariables(variables, result); + } + + return variables; + } +} diff --git a/src/module.d.ts b/src/module.d.ts new file mode 100644 index 00000000..b205bf42 --- /dev/null +++ b/src/module.d.ts @@ -0,0 +1 @@ +declare module 'isomorphic-form-data'; diff --git a/tsconfig.json b/tsconfig.json index 2f25ec1e..a4803380 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "declaration": true, "downlevelIteration": true, + "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "lib": [], "module": "ESNext", diff --git a/yarn.lock b/yarn.lock index 7dce95d1..4036f806 100644 --- a/yarn.lock +++ b/yarn.lock @@ -854,6 +854,11 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@httptoolkit/httpolyglot@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@httptoolkit/httpolyglot/-/httpolyglot-0.2.0.tgz#42833eb2bd0627c4cf0ab816da8e81318b015f71" + integrity sha512-oTOzauUIkt0UA/mPsuKakhr1HT7kKDJrhpiBR8ApUcLSdWVSincQ1lSi89gRczxBiaimhOnEtzZ8KGBMNDxuvQ== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b" @@ -1106,6 +1111,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@superindustries/language@^0.0.10": + version "0.0.10" + resolved "https://npm.pkg.github.com/download/@superindustries/language/0.0.10/b6c603cb1837a40eb7bb969b0241842586b82005a276a619134ac696f20f8adc#e62fc060b790ca0252ce59f2b221cde4999f4771" + integrity sha512-5Tk4h8DVkqo7qHx3/JFzhml2lkft9dLgcxKa/Y3SjGOYypgNk1d8LJvLNAQrp+0dmWBQzJkb7u0ZgnA6k2Y0qQ== + "@types/babel__core@^7.1.7": version "7.1.7" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" @@ -1139,11 +1149,33 @@ dependencies: "@babel/types" "^7.3.0" +"@types/body-parser@*": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" + integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/connect@*": + version "3.4.33" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" + integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== + dependencies: + "@types/node" "*" + +"@types/cors@^2.8.1": + version "2.8.6" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.6.tgz#cfaab33c49c15b1ded32f235111ce9123009bd02" + integrity sha512-invOmosX0DqbpA+cE2yoHGUlF/blyf7nB0OGYBBiH27crcVm5NmFaZkLP4Ta1hGaesckCi5lVLlydNJCxkTOSg== + dependencies: + "@types/express" "*" + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -1159,6 +1191,25 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== +"@types/express-serve-static-core@*": + version "4.17.8" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.8.tgz#b8f7b714138536742da222839892e203df569d1c" + integrity sha512-1SJZ+R3Q/7mLkOD9ewCBDYD2k0WyZQtWYqF/2VvoNN2/uhI49J9CDN4OAm+wGMA0DbArA4ef27xl4+JwMtGggw== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.0.33": + version "4.17.7" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.7.tgz#42045be6475636d9801369cd4418ef65cdb0dd59" + integrity sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/graceful-fs@^4.1.2": version "4.1.3" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f" @@ -1204,11 +1255,28 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/mime@*": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.2.tgz#857a118d8634c84bba7ae14088e4508490cd5da5" + integrity sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q== + +"@types/node-forge@^0.9.1": + version "0.9.4" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.9.4.tgz#3a81edafed5022eba8e63338ef4b04def04c61f1" + integrity sha512-uFhaKXdhhrLNzfNhXbXJqDwF3jXMzN9qfkdW+IAMnAfwqNZhBcE/cciMITLT0Sg6ls6JYHo3xVWNXAG1g9tm8A== + dependencies: + "@types/node" "*" + "@types/node@*": version "14.0.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.1.tgz#5d93e0a099cd0acd5ef3d5bde3c086e1f49ff68c" integrity sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA== +"@types/node@^10.12.9": + version "10.17.27" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.27.tgz#391cb391c75646c8ad2a7b6ed3bbcee52d1bdf19" + integrity sha512-J0oqm9ZfAXaPdwNXMMgAhylw5fhmXkToJd06vuDUSAgEDZ/n/69/69UmyBZbc+zT34UnShuDSBqvim3SPnozJg== + "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -1229,6 +1297,16 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== +"@types/qs@*": + version "6.9.3" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.3.tgz#b755a0934564a200d3efdf88546ec93c369abd03" + integrity sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + "@types/resolve@0.0.8": version "0.0.8" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" @@ -1236,6 +1314,14 @@ dependencies: "@types/node" "*" +"@types/serve-static@*": + version "1.13.4" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.4.tgz#6662a93583e5a6cabca1b23592eb91e12fa80e7c" + integrity sha512-jTDt0o/YbpNwZbQmE/+2e+lfjJEJJR0I3OFaKQKPWkASkCoW3i6fsUnqudSMcNAfbtmADGu8f4MV4q+GqULmug== + dependencies: + "@types/express-serve-static-core" "*" + "@types/mime" "*" + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" @@ -1296,11 +1382,26 @@ semver "^7.3.2" tsutils "^3.17.1" +"@wry/equality@^0.1.2": + version "0.1.11" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790" + integrity sha512-mwEVBDUVODlsQQ5dfuLUS5/Tf7jqUKyhKYHmVi4fPB6bDMOfWvUPJmKgS1Z7Za/sOI3vzWt4+O7yCiL/70MogA== + dependencies: + tslib "^1.9.3" + abab@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -1397,6 +1498,62 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +apollo-cache-control@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.1.1.tgz#173d14ceb3eb9e7cb53de7eb8b61bee6159d4171" + integrity sha512-XJQs167e9u+e5ybSi51nGYr70NPBbswdvTEHtbtXbwkZ+n9t0SLPvUcoqceayOSwjK1XYOdU/EKPawNdb3rLQA== + dependencies: + graphql-extensions "^0.0.x" + +apollo-link@^1.2.14: + version "1.2.14" + resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.14.tgz#3feda4b47f9ebba7f4160bef8b977ba725b684d9" + integrity sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg== + dependencies: + apollo-utilities "^1.3.0" + ts-invariant "^0.4.0" + tslib "^1.9.3" + zen-observable-ts "^0.8.21" + +apollo-server-core@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-1.4.0.tgz#4faff7f110bfdd6c3f47008302ae24140f94c592" + integrity sha512-BP1Vh39krgEjkQxbjTdBURUjLHbFq1zeOChDJgaRsMxGtlhzuLWwwC6lLdPatN8jEPbeHq8Tndp9QZ3iQZOKKA== + dependencies: + apollo-cache-control "^0.1.0" + apollo-tracing "^0.1.0" + graphql-extensions "^0.0.x" + +apollo-server-express@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-1.4.0.tgz#7d7c58d6d6f9892b83fe575669093bb66738b125" + integrity sha512-zkH00nxhLnJfO0HgnNPBTfZw8qI5ILaPZ5TecMCI9+Y9Ssr2b0bFr9pBRsXy9eudPhI+/O4yqegSUsnLdF/CPw== + dependencies: + apollo-server-core "^1.4.0" + apollo-server-module-graphiql "^1.4.0" + +apollo-server-module-graphiql@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/apollo-server-module-graphiql/-/apollo-server-module-graphiql-1.4.0.tgz#c559efa285578820709f1769bb85d3b3eed3d8ec" + integrity sha512-GmkOcb5he2x5gat+TuiTvabnBf1m4jzdecal3XbXBh/Jg+kx4hcvO3TTDFQ9CuTprtzdcVyA11iqG7iOMOt7vA== + +apollo-tracing@^0.1.0: + version "0.1.4" + resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.1.4.tgz#5b8ae1b01526b160ee6e552a7f131923a9aedcc7" + integrity sha512-Uv+1nh5AsNmC3m130i2u3IqbS+nrxyVV3KYimH5QKsdPjxxIQB3JAT+jJmpeDxBel8gDVstNmCh82QSLxLSIdQ== + dependencies: + graphql-extensions "~0.0.9" + +apollo-utilities@^1.0.1, apollo-utilities@^1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.4.tgz#6129e438e8be201b6c55b0f13ce49d2c7175c9cf" + integrity sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig== + dependencies: + "@wry/equality" "^0.1.2" + fast-json-stable-stringify "^2.0.0" + ts-invariant "^0.4.0" + tslib "^1.10.0" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1419,6 +1576,11 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + array-includes@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" @@ -1463,6 +1625,18 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1587,11 +1761,26 @@ babel-preset-jest@^26.0.0: babel-plugin-jest-hoist "^26.0.0" babel-preset-current-node-syntax "^0.1.2" +backo2@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-arraybuffer@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" + integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= + +base64-js@^1.1.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -1617,6 +1806,22 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +body-parser@1.19.0, body-parser@^1.15.2: + version "1.19.0" + resolved "https://registry.yarnpkg.com/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" + boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -1660,6 +1865,13 @@ brotli-size@^4.0.0: dependencies: duplexer "0.1.1" +brotli@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/brotli/-/brotli-1.3.2.tgz#525a9cad4fcba96475d7d388f6aecb13eed52f46" + integrity sha1-UlqcrU/LqWR119OI9q7LE+7VL0Y= + dependencies: + base64-js "^1.1.2" + browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" @@ -1699,6 +1911,11 @@ builtin-modules@^3.1.0: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484" integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw== +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -1936,6 +2153,11 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +common-tags@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -1963,6 +2185,18 @@ contains-path@^0.1.0: resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/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.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" @@ -1970,6 +2204,16 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -1983,11 +2227,29 @@ core-js-compat@^3.6.2: browserslist "^4.8.5" semver "7.0.0" -core-util-is@1.0.2: +core-js@^2.5.3: + version "2.6.11" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cors-gate@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/cors-gate/-/cors-gate-1.1.3.tgz#4ff964e958a94f78da2029f0f95842410d812d19" + integrity sha512-RFqvbbpj02lqKDhqasBEkgzmT3RseCH3DKy5sT2W9S1mhctABKQP3ktKcnKN0h8t4pJ2SneI3hPl3TGNi/VmZA== + +cors@^2.8.4: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + cosmiconfig@^5.0.0: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" @@ -2009,6 +2271,13 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" +cross-fetch@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.5.tgz#2739d2981892e7ab488a7ad03b92df2816e03f4c" + integrity sha512-FFLcLtraisj5eteosnX1gf01qYDCOc4fDy0+euOt8Kn9YBY2NtXL/pCoYPavw24NIQkQqm5ZOLsGD5Zzj0gyew== + dependencies: + node-fetch "2.6.0" + cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -2212,13 +2481,20 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" +debug@^3.1.1: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -2285,6 +2561,21 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +deprecated-decorator@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37" + integrity sha1-AJZjF7ehL+kvPMgx91g68ym4bDc= + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -2360,6 +2651,16 @@ duplexer@0.1.1, duplexer@^0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= +duplexify@^3.5.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -2368,6 +2669,11 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + electron-to-chromium@^1.3.413: version "1.3.441" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.441.tgz#094f71b992dca5bc96b798cfbaf37dc76302015a" @@ -2388,7 +2694,19 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -end-of-stream@^1.1.0: +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -2438,6 +2756,11 @@ es6-promisify@^6.0.1: resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.1.1.tgz#46837651b7b06bf6fff893d03f29393668d01621" integrity sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg== +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -2639,6 +2962,16 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eventemitter3@^3.1.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + eventemitter3@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" @@ -2707,6 +3040,42 @@ expect@^26.0.1: jest-message-util "^26.0.1" jest-regex-util "^26.0.0" +express@^4.14.0: + version "4.17.1" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -2787,6 +3156,13 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fetch-ponyfill@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= + dependencies: + node-fetch "~1.7.1" + figures@^1.0.1: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -2831,6 +3207,19 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/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-cache-dir@^3.0.0: version "3.3.1" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" @@ -2879,6 +3268,15 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= +form-data@^2.3.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -2888,6 +3286,11 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -2895,6 +3298,11 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + fs-extra@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -3018,6 +3426,39 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== +graphql-extensions@^0.0.x, graphql-extensions@~0.0.9: + version "0.0.10" + resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.0.10.tgz#34bdb2546d43f6a5bc89ab23c295ec0466c6843d" + integrity sha512-TnQueqUDCYzOSrpQb3q1ngDSP2otJSF+9yNLrQGPzkMsvnQ+v6e2d5tl+B35D4y+XpmvVnAn4T3ZK28mkILveA== + dependencies: + core-js "^2.5.3" + source-map-support "^0.5.1" + +graphql-subscriptions@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/graphql-subscriptions/-/graphql-subscriptions-1.1.0.tgz#5f2fa4233eda44cf7570526adfcf3c16937aef11" + integrity sha512-6WzlBFC0lWmXJbIVE8OgFgXIP4RJi3OQgTPa0DVMsDXdpRDjTsM1K9wfl5HSYX7R87QAGlvcv2Y4BIZa/ItonA== + dependencies: + iterall "^1.2.1" + +graphql-tools@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-4.0.8.tgz#e7fb9f0d43408fb0878ba66b522ce871bafe9d30" + integrity sha512-MW+ioleBrwhRjalKjYaLQbr+920pHBgy9vM/n47sswtns8+96sRn5M/G+J1eu7IMeKWiN/9p6tmwCHU7552VJg== + dependencies: + apollo-link "^1.2.14" + apollo-utilities "^1.0.1" + deprecated-decorator "^0.1.6" + iterall "^1.1.3" + uuid "^3.1.0" + +graphql@^14.0.2: + version "14.7.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.7.0.tgz#7fa79a80a69be4a31c27dda824dc04dac2035a72" + integrity sha512-l0xWZpoPKpppFzMfvVyFmp9vLN7w/ZZJPefUicMCepfJeQ8sMcztloGYY9DfjVPo6tIUDzU5Hw3MUbIjj9AVVA== + dependencies: + iterall "^1.2.2" + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -3153,6 +3594,28 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/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-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -3174,6 +3637,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" + integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" @@ -3254,11 +3724,16 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + inquirer@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" @@ -3290,6 +3765,11 @@ ip-regex@^2.1.0: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" @@ -3456,6 +3936,11 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -3487,7 +3972,7 @@ is-resolvable@^1.0.0: resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== -is-stream@^1.1.0: +is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -3533,7 +4018,7 @@ is-wsl@^2.1.1: dependencies: is-docker "^2.0.0" -isarray@1.0.0, isarray@^1.0.0: +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -3555,6 +4040,13 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isomorphic-form-data@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz#9f6adf1c4c61ae3aefd8f110ab60fb9b143d6cec" + integrity sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg== + dependencies: + form-data "^2.3.2" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -3601,6 +4093,11 @@ istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterall@^1.1.3, iterall@^1.2.1, iterall@^1.2.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" + integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== + jest-changed-files@^26.0.1: version "26.0.1" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.0.1.tgz#1334630c6a1ad75784120f39c3aa9278e59f349f" @@ -4221,6 +4718,11 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= +lodash@^4.16.4: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" @@ -4298,11 +4800,26 @@ mdn-data@2.0.6: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/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.yarnpkg.com/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.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + microbundle@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/microbundle/-/microbundle-0.12.0.tgz#d3d531c4d7553ea2f38688e3076f8e4d70adcc65" @@ -4378,13 +4895,18 @@ mime-db@1.44.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== -mime-types@^2.1.12, mime-types@~2.1.19: +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== dependencies: mime-db "1.44.0" +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -4422,6 +4944,41 @@ mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "^1.2.5" +mockttp@^0.20.3: + version "0.20.3" + resolved "https://registry.yarnpkg.com/mockttp/-/mockttp-0.20.3.tgz#916ad20187c47f3a21813319600a519ebb2481f0" + integrity sha512-aWy0nwbl3KDl7IRvKb9Jsn7AteTcc0DStGHm4KYX+Xf8+1hrxXRNPR7EJgJOJUnD/b8N38MlGebrZATeeOvWEg== + dependencies: + "@httptoolkit/httpolyglot" "^0.2.0" + "@types/cors" "^2.8.1" + "@types/express" "^4.0.33" + "@types/node" "^10.12.9" + "@types/node-forge" "^0.9.1" + apollo-server-express "^1.1.0" + base64-arraybuffer "^0.1.5" + body-parser "^1.15.2" + brotli "^1.3.2" + common-tags "^1.8.0" + cors "^2.8.4" + cors-gate "^1.1.3" + express "^4.14.0" + fetch-ponyfill "^4.1.0" + graphql "^14.0.2" + graphql-subscriptions "^1.1.0" + graphql-tools "^4.0.5" + lodash "^4.16.4" + native-duplexpair "^1.0.0" + node-forge "^0.9.0" + normalize-url "^1.9.1" + performance-now "^2.1.0" + portfinder "^1.0.23" + subscriptions-transport-ws "^0.9.4" + typed-error "^3.0.2" + universal-websocket-client "^1.0.2" + uuid "^3.1.0" + websocket-stream "^5.1.2" + ws "^5.2.0" + module-details-from-path@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" @@ -4437,6 +4994,11 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -4464,16 +5026,44 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +native-duplexpair@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/native-duplexpair/-/native-duplexpair-1.0.0.tgz#7899078e64bf3c8a3d732601b3d40ff05db58fa0" + integrity sha1-eJkHjmS/PIo9cyYBs9QP8F21j6A= + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/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.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-fetch@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + +node-fetch@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-forge@^0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.1.tgz#775368e6846558ab6676858a4d8c6e8d16c677b5" + integrity sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ== + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -4528,6 +5118,16 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +normalize-url@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + normalize-url@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" @@ -4574,7 +5174,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.1.0: +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -4640,6 +5240,13 @@ object.values@^1.1.0: function-bind "^1.1.1" has "^1.0.3" +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -4783,6 +5390,11 @@ parse5@5.1.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -4818,6 +5430,11 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + path-type@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" @@ -4883,6 +5500,15 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" +portfinder@^1.0.23: + version "1.0.26" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.26.tgz#475658d56ca30bed72ac7f1378ed350bd1b64e70" + integrity sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ== + dependencies: + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.1" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -5250,6 +5876,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + prettier@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" @@ -5292,6 +5923,11 @@ private@^0.1.8: resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -5310,6 +5946,14 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.4" +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -5333,11 +5977,39 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/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" + react-is@^16.12.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -5379,6 +6051,19 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +readable-stream@^2.0.0, readable-stream@^2.3.3: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + 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" + regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -5698,16 +6383,16 @@ sade@^1.7.3: dependencies: mri "^1.1.0" +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-buffer@^5.0.1, safe-buffer@^5.1.2: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - safe-identifier@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.1.tgz#b6516bf72594f03142b5f914f4c01842ccb1b678" @@ -5720,7 +6405,7 @@ safe-regex@^1.1.0: 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: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -5772,11 +6457,40 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + serialize-javascript@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -5792,6 +6506,11 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -5882,6 +6601,13 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= + dependencies: + is-plain-obj "^1.0.0" + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -5893,7 +6619,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.6, source-map-support@~0.5.12: +source-map-support@^0.5.1, source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -5999,11 +6725,26 @@ static-extend@^0.1.1: 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.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + string-hash@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" @@ -6069,6 +6810,13 @@ string.prototype.trimstart@^1.0.0: define-properties "^1.1.3" es-abstract "^1.17.5" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -6129,6 +6877,17 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" +subscriptions-transport-ws@^0.9.4: + version "0.9.16" + resolved "https://registry.yarnpkg.com/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.16.tgz#90a422f0771d9c32069294c08608af2d47f596ec" + integrity sha512-pQdoU7nC+EpStXnCfh/+ho0zE0Z+ma+i7xvj7bkXKb1dvYHSZxgRPaU6spRP+Bjzow67c/rRDoix5RT0uU9omw== + dependencies: + backo2 "^1.0.2" + eventemitter3 "^3.1.0" + iterall "^1.2.1" + symbol-observable "^1.0.4" + ws "^5.2.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -6189,6 +6948,11 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" +symbol-observable@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -6307,6 +7071,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -6331,6 +7100,13 @@ tr46@^2.0.2: dependencies: punycode "^2.1.1" +ts-invariant@^0.4.0: + version "0.4.4" + resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.4.4.tgz#97a523518688f93aafad01b0e80eb803eb2abd86" + integrity sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA== + dependencies: + tslib "^1.9.3" + ts-jest@^26.1.0: version "26.1.0" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.1.0.tgz#e9070fc97b3ea5557a48b67c631c74eb35e15417" @@ -6362,7 +7138,7 @@ tslib@1.10.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== @@ -6420,6 +7196,19 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/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.yarnpkg.com/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" + +typed-error@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/typed-error/-/typed-error-3.2.0.tgz#ffa498688c458b7437e83c3c972d2b7b5b0e21ae" + integrity sha512-n0NojMTp7jD2MMgJxtjzS1it/sKIlDfQwqOECSPAGwsIU2jns3G0R6alnakRelQzxz7t8PhjYrlqYoQKUVGOsQ== + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -6427,11 +7216,21 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^3.8.3, typescript@^3.9.2: +typescript@^3.8.3: version "3.9.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.2.tgz#64e9c8e9be6ea583c54607677dd4680a1cf35db9" integrity sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw== +typescript@^3.9.6: + version "3.9.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" + integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -6475,11 +7274,23 @@ uniqs@^2.0.0: resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= +universal-websocket-client@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/universal-websocket-client/-/universal-websocket-client-1.0.2.tgz#9942307a9d418bb5defd33594048c7e367448b44" + integrity sha512-Pi6BdJtEAISb77GTbOLBLIWdYGezXgnJejrVBYKXxzNTsLcjJS+mWIJ2BRZElSlOG/wc7+yfOe5y30bzTu3Qqg== + dependencies: + ws "^3.3.3" + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + unquote@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" @@ -6510,6 +7321,11 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + util.promisify@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" @@ -6520,7 +7336,12 @@ util.promisify@~1.0.0: has-symbols "^1.0.1" object.getownpropertydescriptors "^2.1.0" -uuid@^3.3.2: +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.1.0, uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -6552,6 +7373,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + vendors@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" @@ -6607,6 +7433,18 @@ webidl-conversions@^6.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +websocket-stream@^5.1.2: + version "5.5.2" + resolved "https://registry.yarnpkg.com/websocket-stream/-/websocket-stream-5.5.2.tgz#49d87083d96839f0648f5513bbddd581f496b8a2" + integrity sha512-8z49MKIHbGk3C4HtuHWDtYX8mYej1wWabjthC/RupM9ngeukU4IWoM46dgth1UOS/T4/IqgEdCDJuMe2039OQQ== + dependencies: + duplexify "^3.5.1" + inherits "^2.0.1" + readable-stream "^2.3.3" + safe-buffer "^5.1.2" + ws "^3.2.0" + xtend "^4.0.0" + whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -6683,6 +7521,22 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" +ws@^3.2.0, ws@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + ws@^7.2.3: version "7.3.0" resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" @@ -6698,6 +7552,11 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" @@ -6732,3 +7591,16 @@ yargs@^15.3.1: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^18.1.1" + +zen-observable-ts@^0.8.21: + version "0.8.21" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz#85d0031fbbde1eba3cd07d3ba90da241215f421d" + integrity sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg== + dependencies: + tslib "^1.9.3" + zen-observable "^0.8.0" + +zen-observable@^0.8.0: + version "0.8.15" + resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" + integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==