diff --git a/package.json b/package.json index d234e7c3b5..9957bafd04 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "unleash-server", "description": "Unleash is an enterprise ready feature toggles service. It provides different strategies for handling feature toggles.", - "version": "4.13.0-beta.0", + "version": "4.13.0-beta.2", "keywords": [ "unleash", "feature toggle", @@ -145,7 +145,7 @@ "copyfiles": "2.4.1", "coveralls": "3.1.1", "del-cli": "4.0.1", - "eslint": "8.16.0", + "eslint": "8.17.0", "eslint-config-airbnb-base": "15.0.0", "eslint-config-airbnb-typescript": "17.0.0", "eslint-config-prettier": "8.5.0", @@ -156,16 +156,16 @@ "husky": "8.0.1", "jest": "27.5.1", "lint-staged": "12.5.0", - "nock": "^13.2.4", + "nock": "13.2.6", "prettier": "2.6.2", "proxyquire": "2.1.3", "source-map-support": "0.5.21", "superagent": "7.1.6", "supertest": "6.2.3", "ts-jest": "27.1.5", - "ts-node": "10.8.0", + "ts-node": "10.8.1", "tsc-watch": "5.0.3", - "typescript": "4.7.2" + "typescript": "4.7.3" }, "resolutions": { "async": "^3.2.3", diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index 04308a0d82..839da9fc2d 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -89,6 +89,8 @@ Object { "unleashUrl": "http://localhost:4242", }, "session": Object { + "clearSiteDataOnLogout": true, + "cookieName": "unleash-session", "db": true, "ttlHours": 48, }, diff --git a/src/lib/create-config.ts b/src/lib/create-config.ts index 10c8761720..890a0ed657 100644 --- a/src/lib/create-config.ts +++ b/src/lib/create-config.ts @@ -91,6 +91,11 @@ const defaultDbOptions: IDBOption = { const defaultSessionOption: ISessionOption = { ttlHours: parseEnvVarNumber(process.env.SESSION_TTL_HOURS, 48), + clearSiteDataOnLogout: parseEnvVarBoolean( + process.env.SESSION_CLEAR_SITE_DATA_ON_LOGOUT, + true, + ), + cookieName: 'unleash-session', db: true, }; diff --git a/src/lib/middleware/session-db.ts b/src/lib/middleware/session-db.ts index 01527006ff..91eb6aca54 100644 --- a/src/lib/middleware/session-db.ts +++ b/src/lib/middleware/session-db.ts @@ -10,7 +10,7 @@ function sessionDb( knex: Knex, ): RequestHandler { let store; - const { db } = config.session; + const { db, cookieName } = config.session; const age = hoursToMilliseconds(config.session.ttlHours) || hoursToMilliseconds(48); const KnexSessionStore = knexSessionStore(session); @@ -25,7 +25,7 @@ function sessionDb( store = new session.MemoryStore(); } return session({ - name: 'unleash-session', + name: cookieName, rolling: false, resave: false, saveUninitialized: false, diff --git a/src/lib/openapi/spec/feature-variants-response.ts b/src/lib/openapi/spec/feature-variants-response.ts new file mode 100644 index 0000000000..504ddc6882 --- /dev/null +++ b/src/lib/openapi/spec/feature-variants-response.ts @@ -0,0 +1,12 @@ +import { OpenAPIV3 } from 'openapi-types'; + +export const featureVariantsResponse: OpenAPIV3.ResponseObject = { + description: 'featureVariantResponse', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/featureVariantsSchema', + }, + }, + }, +}; diff --git a/src/lib/openapi/spec/feature-variants-schema.ts b/src/lib/openapi/spec/feature-variants-schema.ts new file mode 100644 index 0000000000..0031552c38 --- /dev/null +++ b/src/lib/openapi/spec/feature-variants-schema.ts @@ -0,0 +1,26 @@ +import { createSchemaObject, CreateSchemaType } from '../types'; +import { variantSchema } from './variant-schema'; + +const schema = { + type: 'object', + additionalProperties: false, + required: ['version', 'variants'], + properties: { + version: { + type: 'integer', + }, + variants: { + type: 'array', + items: { + $ref: '#/components/schemas/variantSchema', + }, + }, + }, + 'components/schemas': { + variantSchema, + }, +}; + +export type FeatureVariantsSchema = CreateSchemaType; + +export const featureVariantsSchema = createSchemaObject(schema); diff --git a/src/lib/openapi/spec/update-feature-variants-request.ts b/src/lib/openapi/spec/update-feature-variants-request.ts new file mode 100644 index 0000000000..c416dc6bca --- /dev/null +++ b/src/lib/openapi/spec/update-feature-variants-request.ts @@ -0,0 +1,15 @@ +import { OpenAPIV3 } from 'openapi-types'; + +export const updateFeatureVariantsRequest: OpenAPIV3.RequestBodyObject = { + required: true, + content: { + 'application/json': { + schema: { + type: 'array', + items: { + $ref: '#/components/schemas/variantSchema', + }, + }, + }, + }, +}; diff --git a/src/lib/routes/admin-api/project/variants.ts b/src/lib/routes/admin-api/project/variants.ts index a704ed8e86..6d25c5355d 100644 --- a/src/lib/routes/admin-api/project/variants.ts +++ b/src/lib/routes/admin-api/project/variants.ts @@ -5,10 +5,14 @@ import { IUnleashConfig } from '../../../types/option'; import { IUnleashServices } from '../../../types'; import { Request, Response } from 'express'; import { Operation } from 'fast-json-patch'; -import { UPDATE_FEATURE_VARIANTS } from '../../../types/permissions'; +import { NONE, UPDATE_FEATURE_VARIANTS } from '../../../types/permissions'; import { IVariant } from '../../../types/model'; import { extractUsername } from '../../../util/extract-user'; import { IAuthRequest } from '../../unleash-types'; +import { featureVariantsResponse } from '../../../openapi/spec/feature-variants-response'; +import { patchRequest } from '../../../openapi/spec/patch-request'; +import { updateFeatureVariantsRequest } from '../../../openapi/spec/update-feature-variants-request'; +import { FeatureVariantsSchema } from '../../../openapi/spec/feature-variants-schema'; const PREFIX = '/:projectId/features/:featureName/variants'; @@ -19,7 +23,6 @@ interface FeatureParams extends ProjectParam { interface ProjectParam { projectId: string; } - export default class VariantsController extends Controller { private logger: Logger; @@ -29,19 +32,59 @@ export default class VariantsController extends Controller { config: IUnleashConfig, { featureToggleService, - }: Pick, + openApiService, + }: Pick, ) { super(config); this.logger = config.getLogger('admin-api/project/variants.ts'); this.featureService = featureToggleService; - this.get(PREFIX, this.getVariants); - this.patch(PREFIX, this.patchVariants, UPDATE_FEATURE_VARIANTS); - this.put(PREFIX, this.overwriteVariants, UPDATE_FEATURE_VARIANTS); + this.route({ + method: 'get', + path: PREFIX, + permission: NONE, + acceptAnyContentType: true, + handler: this.getVariants, + middleware: [ + openApiService.validPath({ + tags: ['admin'], + operationId: 'getFeatureVariants', + responses: { 200: featureVariantsResponse }, + }), + ], + }); + this.route({ + method: 'patch', + path: PREFIX, + permission: UPDATE_FEATURE_VARIANTS, + handler: this.patchVariants, + middleware: [ + openApiService.validPath({ + tags: ['admin'], + operationId: 'patchFeatureVariants', + requestBody: patchRequest, + responses: { 200: featureVariantsResponse }, + }), + ], + }); + this.route({ + method: 'put', + path: PREFIX, + permission: UPDATE_FEATURE_VARIANTS, + handler: this.overwriteVariants, + middleware: [ + openApiService.validPath({ + tags: ['admin'], + operationId: 'overwriteFeatureVariants', + requestBody: updateFeatureVariantsRequest, + responses: { 200: featureVariantsResponse }, + }), + ], + }); } async getVariants( req: Request, - res: Response, + res: Response, ): Promise { const { featureName } = req.params; const variants = await this.featureService.getVariants(featureName); @@ -50,7 +93,7 @@ export default class VariantsController extends Controller { async patchVariants( req: IAuthRequest, - res: Response, + res: Response, ): Promise { const { projectId, featureName } = req.params; const userName = extractUsername(req); @@ -69,7 +112,7 @@ export default class VariantsController extends Controller { async overwriteVariants( req: IAuthRequest, - res: Response, + res: Response, ): Promise { const { projectId, featureName } = req.params; const userName = extractUsername(req); diff --git a/src/lib/routes/index.ts b/src/lib/routes/index.ts index 76961ef315..fd8cd4d8c4 100644 --- a/src/lib/routes/index.ts +++ b/src/lib/routes/index.ts @@ -5,13 +5,12 @@ import SimplePasswordProvider from './auth/simple-password-provider'; import { IUnleashConfig } from '../types/option'; import { IUnleashServices } from '../types/services'; import { api } from './api-def'; +import LogoutController from './logout'; const AdminApi = require('./admin-api'); const ClientApi = require('./client-api'); const Controller = require('./controller'); const HealthCheckController = require('./health-check'); -const LogoutController = require('./logout'); - class IndexRouter extends Controller { constructor(config: IUnleashConfig, services: IUnleashServices) { super(config); diff --git a/src/lib/routes/logout.test.ts b/src/lib/routes/logout.test.ts index c31c662afd..b5b0587a41 100644 --- a/src/lib/routes/logout.test.ts +++ b/src/lib/routes/logout.test.ts @@ -44,6 +44,59 @@ test('should set "Clear-Site-Data" header', async () => { .expect('Clear-Site-Data', '"cookies", "storage"'); }); +test('should not set "Clear-Site-Data" header', async () => { + const baseUriPath = ''; + const app = express(); + const config = createTestConfig({ + server: { baseUriPath }, + session: { clearSiteDataOnLogout: false }, + }); + app.use('/logout', new LogoutController(config).router); + const request = supertest(app); + expect.assertions(1); + await request + .get(`${baseUriPath}/logout`) + .expect(302) + .expect((res) => + expect(res.headers['Clear-Site-Data']).toBeUndefined(), + ); +}); + +test('should clear "unleash-session" cookies', async () => { + const baseUriPath = ''; + const app = express(); + const config = createTestConfig({ server: { baseUriPath } }); + app.use('/logout', new LogoutController(config).router); + const request = supertest(app); + expect.assertions(0); + await request + .get(`${baseUriPath}/logout`) + .expect(302) + .expect( + 'Set-Cookie', + 'unleash-session=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT', + ); +}); + +test('should clear "unleash-session" cookie even when disabled clear site data', async () => { + const baseUriPath = ''; + const app = express(); + const config = createTestConfig({ + server: { baseUriPath }, + session: { clearSiteDataOnLogout: false }, + }); + app.use('/logout', new LogoutController(config).router); + const request = supertest(app); + expect.assertions(0); + await request + .get(`${baseUriPath}/logout`) + .expect(302) + .expect( + 'Set-Cookie', + 'unleash-session=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT', + ); +}); + test('should call destroy on session', async () => { const baseUriPath = ''; const fakeSession = { diff --git a/src/lib/routes/logout.ts b/src/lib/routes/logout.ts index a14db86b33..ba5b45bf89 100644 --- a/src/lib/routes/logout.ts +++ b/src/lib/routes/logout.ts @@ -4,11 +4,17 @@ import Controller from './controller'; import { IAuthRequest } from './unleash-types'; class LogoutController extends Controller { + private clearSiteDataOnLogout: boolean; + + private cookieName: string; + private baseUri: string; constructor(config: IUnleashConfig) { super(config); this.baseUri = config.server.baseUriPath; + this.clearSiteDataOnLogout = config.session.clearSiteDataOnLogout; + this.cookieName = config.session.cookieName; this.get('/', this.logout); } @@ -27,10 +33,14 @@ class LogoutController extends Controller { req.logout(); } - res.set('Clear-Site-Data', '"cookies", "storage"'); + res.clearCookie(this.cookieName); + + if (this.clearSiteDataOnLogout) { + res.set('Clear-Site-Data', '"cookies", "storage"'); + } + res.redirect(`${this.baseUri}/`); } } -module.exports = LogoutController; export default LogoutController; diff --git a/src/lib/types/option.ts b/src/lib/types/option.ts index 7f83f067f5..5c7a44d268 100644 --- a/src/lib/types/option.ts +++ b/src/lib/types/option.ts @@ -37,6 +37,8 @@ export interface IDBOption { export interface ISessionOption { ttlHours: number; db: boolean; + clearSiteDataOnLogout: boolean; + cookieName: string; } export interface IVersionOption { diff --git a/src/test/e2e/api/admin/project/variants.e2e.test.ts b/src/test/e2e/api/admin/project/variants.e2e.test.ts index a741832502..b5370aa601 100644 --- a/src/test/e2e/api/admin/project/variants.e2e.test.ts +++ b/src/test/e2e/api/admin/project/variants.e2e.test.ts @@ -266,6 +266,7 @@ test('PUTing an invalid variant throws 400 exception', async () => { name: 'variant', weight: 500, weightType: 'party', + stickiness: 'userId', }, ]; await app.request diff --git a/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap b/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap index 7dd567c573..9a9277c261 100644 --- a/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap +++ b/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap @@ -1520,6 +1520,142 @@ Object { ], }, }, + "/api/admin/projects/{projectId}/features/{featureName}/variants": Object { + "get": Object { + "operationId": "getFeatureVariants", + "parameters": Array [ + Object { + "in": "path", + "name": "projectId", + "required": true, + "schema": Object { + "type": "string", + }, + }, + Object { + "in": "path", + "name": "featureName", + "required": true, + "schema": Object { + "type": "string", + }, + }, + ], + "responses": Object { + "200": Object { + "content": Object { + "application/json": Object { + "schema": Object { + "$ref": "#/components/schemas/featureVariantsSchema", + }, + }, + }, + "description": "featureVariantResponse", + }, + }, + "tags": Array [ + "admin", + ], + }, + "patch": Object { + "operationId": "patchFeatureVariants", + "parameters": Array [ + Object { + "in": "path", + "name": "projectId", + "required": true, + "schema": Object { + "type": "string", + }, + }, + Object { + "in": "path", + "name": "featureName", + "required": true, + "schema": Object { + "type": "string", + }, + }, + ], + "requestBody": Object { + "content": Object { + "application/json": Object { + "schema": Object { + "items": Object { + "$ref": "#/components/schemas/patchOperationSchema", + }, + "type": "array", + }, + }, + }, + "required": true, + }, + "responses": Object { + "200": Object { + "content": Object { + "application/json": Object { + "schema": Object { + "$ref": "#/components/schemas/featureVariantsSchema", + }, + }, + }, + "description": "featureVariantResponse", + }, + }, + "tags": Array [ + "admin", + ], + }, + "put": Object { + "operationId": "overwriteFeatureVariants", + "parameters": Array [ + Object { + "in": "path", + "name": "projectId", + "required": true, + "schema": Object { + "type": "string", + }, + }, + Object { + "in": "path", + "name": "featureName", + "required": true, + "schema": Object { + "type": "string", + }, + }, + ], + "requestBody": Object { + "content": Object { + "application/json": Object { + "schema": Object { + "items": Object { + "$ref": "#/components/schemas/variantSchema", + }, + "type": "array", + }, + }, + }, + "required": true, + }, + "responses": Object { + "200": Object { + "content": Object { + "application/json": Object { + "schema": Object { + "$ref": "#/components/schemas/featureVariantsSchema", + }, + }, + }, + "description": "featureVariantResponse", + }, + }, + "tags": Array [ + "admin", + ], + }, + }, }, "security": Array [ Object { diff --git a/website/docs/deploy/configuring-unleash.md b/website/docs/deploy/configuring-unleash.md index 90286dcbf7..8c1e4bab5f 100644 --- a/website/docs/deploy/configuring-unleash.md +++ b/website/docs/deploy/configuring-unleash.md @@ -92,7 +92,7 @@ unleash.start(unleashOptions); - `sender` - Which email should be set as sender of mails being sent from Unleash? - `smtpuser` - Username for your SMTP server - `smtppass` - Password for your SMTP server -- **eventHook** (`function(event, data)`) - If provided, this function will be invoked whenever a feature is mutated. The possible values for `event` are `'feature-created'`, `'feature-updated'`, `'feature-archived'`, `'feature-revived'`. The `data` argument contains information about the mutation. Its fields are `type` (string) - the event type (same as `event`); `createdBy` (string) - the user who performed the mutation; `data` - the contents of the change. The contents in `data` differs based on the event type; For `'feature-archived'` and `'feature-revived'`, the only field will be `name` - the name of the feature. For `'feature-created'` and `'feature-updated'` the data follows a schema defined in the code [here](https://github.com/Unleash/unleash/blob/master/src/lib/services/feature-schema.js#L65). See an [api here](/api/admin/events). +- **eventHook** (`function(event, data)`) - (_deprecated in Unleash 4.3_ in favor of the [Webhook addon](../addons/webhook.md)) If provided, this function will be invoked whenever a feature is mutated. The possible values for `event` are `'feature-created'`, `'feature-archived'` and `'feature-revived'`. The `data` argument contains information about the mutation. Its fields are `type` (string) - the event type (same as `event`); `createdBy` (string) - the user who performed the mutation; `data` - the contents of the change. The contents in `data` differs based on the event type; For `'feature-archived'` and `'feature-revived'`, the only field will be `name` - the name of the feature. For `'feature-created'` the data follows a schema defined in the code [here](https://github.com/Unleash/unleash/blob/7b7f0b84e8cddd5880dcf29c231672113224b9a7/src/lib/schema/feature-schema.ts#L77). See an [api here](/api/admin/events). - **getLogger** (function) - Used to register a [custom log provider](#how-do-i-configure-the-log-output). - **logLevel** (`debug` | `info` | `warn` | `error` | `fatal`) - The lowest level to log at, also configurable using environment variable `LOG_LEVEL`. - **preHook** (function) - this is a hook if you need to provide any middlewares to express before `unleash` adds any. Express app instance is injected as first argument. @@ -322,3 +322,24 @@ Unleash builds directly on the [node-postgres library](https://node-postgres.com - Check the default values of connection pool about idle session handling. - If you have a network component which closes idle sessions on the TCP layer, make sure that the connection pool's `idleTimeoutMillis` setting is lower than the `timespan` setting. If it isn't, then the network component will close the connection. + +### Proxying requests from Unleash + +You can configure proxy services that intercept all outgoing requests from Unleash. This lets you use the Microsoft Teams or the Webhook addon for external services, even if the internet can only be reached via a proxy on your machine or container (for example if restricted by a firewall or similiar). + +As an example, here's how you could do it using the [node-global-proxy](https://www.npmjs.com/package/node-global-proxy) package: +``` +const proxy = require("node-global-proxy").default; + +proxy.setConfig({ + http: "http://user:password@url:8080", //proxy adress, replace values as needed + //https: "https://user:password@url:1080", //if a https proxy is needed + }); + +proxy.start(); //this starts the proxy, after this call all requests will be proxied +``` + +Using above code-snippet, every outgoing request from unleash or its addons will be subsequently routed through set proxy. +If the proxy routing needs to be bypassed or stopped, its possible to stop it by using + +`proxy.stop();` diff --git a/website/package.json b/website/package.json index 0eef221ce4..67a12ba678 100644 --- a/website/package.json +++ b/website/package.json @@ -65,6 +65,6 @@ "enhanced-resolve": "5.9.3", "react-router": "6.3.0", "storybook-addon-root-attribute": "1.0.2", - "typescript": "4.7.2" + "typescript": "4.7.3" } } diff --git a/yarn.lock b/yarn.lock index 1cc45a7a36..1f6ca7374d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2832,10 +2832,10 @@ eslint-visitor-keys@^3.3.0: resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@8.16.0: - version "8.16.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.16.0.tgz#6d936e2d524599f2a86c708483b4c372c5d3bbae" - integrity sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA== +eslint@8.17.0: + version "8.17.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.17.0.tgz#1cfc4b6b6912f77d24b874ca1506b0fe09328c21" + integrity sha512-gq0m0BTJfci60Fz4nczYxNAlED+sMcihltndR8t9t1evnU/azx53x3t2UHXC/uRjcbvRw/XctpaNygSTcQD+Iw== dependencies: "@eslint/eslintrc" "^1.3.0" "@humanwhocodes/config-array" "^0.9.2" @@ -5078,11 +5078,6 @@ lodash.merge@^4.6.2: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.set@^4.3.2: - version "4.3.2" - resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz" - integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= - lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" @@ -5575,14 +5570,14 @@ next-tick@~1.0.0: resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz" integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= -nock@^13.2.4: - version "13.2.4" - resolved "https://registry.npmjs.org/nock/-/nock-13.2.4.tgz" - integrity sha512-8GPznwxcPNCH/h8B+XZcKjYPXnUV5clOKCjAqyjsiqA++MpNx9E9+t8YPp0MbThO+KauRo7aZJ1WuIZmOrT2Ug== +nock@13.2.6: + version "13.2.6" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.6.tgz#35e419cd9d385ffa67e59523d9699e41b29e1a03" + integrity sha512-GbyeSwSEP0FYouzETZ0l/XNm5tNcDNcXJKw3LCAb+mx8bZSwg1wEEvdL0FAyg5TkBJYiWSCtw6ag4XfmBy60FA== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" - lodash.set "^4.3.2" + lodash "^4.17.21" propagate "^2.0.0" node-cleanup@^2.1.2: @@ -7429,10 +7424,10 @@ ts-jest@27.1.5: semver "7.x" yargs-parser "20.x" -ts-node@10.8.0: - version "10.8.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.8.0.tgz#3ceb5ac3e67ae8025c1950626aafbdecb55d82ce" - integrity sha512-/fNd5Qh+zTt8Vt1KbYZjRHCE9sI5i7nqfD/dzBBRDeVXZXS6kToW6R7tTU6Nd4XavFs0mAVCg29Q//ML7WsZYA== +ts-node@10.8.1: + version "10.8.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.8.1.tgz#ea2bd3459011b52699d7e88daa55a45a1af4f066" + integrity sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g== dependencies: "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" @@ -7586,10 +7581,10 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@4.7.2: - version "4.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4" - integrity sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A== +typescript@4.7.3: + version "4.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d" + integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA== uid-safe@~2.1.5: version "2.1.5"