diff --git a/packages/node/package.json b/packages/node/package.json index 5f359ba5fa38..d0dddc940ec7 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -27,6 +27,7 @@ "tslib": "^1.9.3" }, "devDependencies": { + "@forge/api": "^2.1.0", "@sentry-internal/eslint-config-sdk": "6.8.0", "@types/cookie": "0.3.2", "@types/express": "^4.17.2", diff --git a/packages/node/src/transports/forge.ts b/packages/node/src/transports/forge.ts new file mode 100644 index 000000000000..ea6c4fb6e084 --- /dev/null +++ b/packages/node/src/transports/forge.ts @@ -0,0 +1,181 @@ +import { FetchMethod as ForgeRuntimeFetchMethod } from '@forge/api'; +import { eventToSentryRequest, sessionToSentryRequest } from '@sentry/core'; +import { Event, Response, Session, SessionAggregates, TransportOptions } from '@sentry/types'; +import http, { OutgoingHttpHeaders } from 'http'; +import https from 'https'; +import { URL } from 'url'; + +import { BaseTransport } from './base'; +import { + HTTPModule, + HTTPModuleClientRequest, + HTTPModuleRequestIncomingMessage, + HTTPModuleRequestOptions, +} from './base/http-module'; + +export type ErrorLogger = (map: { err: Error }, msg: string) => void; + +export type ForgeRuntimeTransportOptions = TransportOptions & { + fetch: ForgeRuntimeFetchMethod; + logError: ErrorLogger; +}; + +type ForgeHttpModuleRequestOptions = { + logError: ErrorLogger; + fetch: ForgeRuntimeFetchMethod; + options: http.RequestOptions | https.RequestOptions; + callback(res: HTTPModuleRequestIncomingMessage): void; +}; + +/** Forge fetch implementation only accepts simple headers object */ +const isValidHeadersObject = (headers: OutgoingHttpHeaders): headers is { [key: string]: string } => { + return Object.keys(headers).every(headerName => { + const value = headers[headerName]; + return typeof value === 'string'; + }); +}; + +/** Internal Error used to re-throw if Response.ok is false */ +class InvalidResponseError extends Error { + public constructor(public statusCode: number) { + super('Unexpected response code'); + } +} + +/** + * Current tsconfig target is "es5" which transforms classes into functions. + * Thus, "instanceof" check doesn't work and that's the reason why this type guard function exists. + */ +const isInvalidResponseError = (err: Error | InvalidResponseError): err is InvalidResponseError => { + return 'statusCode' in err; +}; + +/** + * Forge custom implementation of http.ClientRequest + * + * It mimics Node.JS behaviour because Forge runtime has limited Node.JS API support. + * @see https://developer.atlassian.com/platform/forge/runtime-reference/#javascript-environment + */ +class ForgeHttpModuleRequest implements HTTPModuleClientRequest { + private readonly _httpRes: Exclude; + + public constructor(private _options: ForgeHttpModuleRequestOptions) { + this._httpRes = { + setEncoding: () => null, + headers: {}, + on: () => null, + }; + } + + /** Mock method because it's not needed for this module */ + public on(): void { + return undefined; + } + + /** Sends request to Sentry API */ + public async end(body: string): Promise { + // eslint-disable-next-line @typescript-eslint/unbound-method + const { + callback, + logError, + fetch, + options: { headers, method, path, protocol, hostname, port }, + } = this._options; + const url = new URL(path || '/', 'https://example.com'); + if (protocol) { + url.protocol = protocol; + } + if (hostname) { + url.hostname = hostname; + } + if (port) { + url.port = String(port); + } + + const requestHeaders = headers && isValidHeadersObject(headers) ? headers : {}; + + try { + const res = await fetch(url.toString(), { + body, + method, + headers: requestHeaders, + }); + + if (!res.ok) { + throw new InvalidResponseError(res.status); + } + + callback({ + ...this._httpRes, + statusCode: res.status, + }); + } catch (err) { + // eslint-disable-next-line no-console + logError({ err }, 'Fetching entry API failed'); + + const statusCode = isInvalidResponseError(err) ? err.statusCode : 500; + + callback({ + ...this._httpRes, + statusCode, + }); + } + } +} + +/** + * HTTPModule implementation for Forge runtime. + * It mimics Node.JS behaviour because Forge runtime has limited Node.JS API support. + * + * @see https://developer.atlassian.com/platform/forge/runtime-reference/#javascript-environment + */ +class ForgeHttpModule implements HTTPModule { + public constructor(private _forgeFetch: ForgeRuntimeFetchMethod, private _logError: ErrorLogger) {} + + /** Sends request to Sentry API */ + public request( + options: HTTPModuleRequestOptions, + callback: (res: HTTPModuleRequestIncomingMessage) => void, + ): HTTPModuleClientRequest { + if (typeof options === 'string') { + throw new Error('String request options are not supported'); + } + + if (options instanceof URL) { + throw new Error('URL as request options is not supported'); + } + + return new ForgeHttpModuleRequest({ + options, + callback, + fetch: this._forgeFetch, + logError: this._logError, + }); + } +} + +/** Forge module transport */ +export class ForgeRuntimeTransport extends BaseTransport { + /** Create a new instance and set this.agent */ + public constructor(options: ForgeRuntimeTransportOptions) { + super(options); + + this.module = new ForgeHttpModule(options.fetch, options.logError); + this.client = undefined; + this.urlParser = url => new URL(url); + } + + /** + * @inheritDoc + */ + public sendEvent(event: Event): Promise { + return this._send(eventToSentryRequest(event, this._api), event); + } + + /** + * @inheritDoc + */ + public sendSession(session: Session | SessionAggregates): PromiseLike { + return this._send(sessionToSentryRequest(session, this._api), session); + } +} diff --git a/packages/node/src/transports/index.ts b/packages/node/src/transports/index.ts index f48517061c2f..75991b996fcf 100644 --- a/packages/node/src/transports/index.ts +++ b/packages/node/src/transports/index.ts @@ -1,3 +1,4 @@ export { BaseTransport } from './base'; export { HTTPTransport } from './http'; export { HTTPSTransport } from './https'; +export { ForgeRuntimeTransport } from './forge'; diff --git a/packages/node/test/transports/forge.test.ts b/packages/node/test/transports/forge.test.ts new file mode 100644 index 000000000000..49544f81ac67 --- /dev/null +++ b/packages/node/test/transports/forge.test.ts @@ -0,0 +1,34 @@ +import { SentryError } from '@sentry/utils'; + +import { ForgeRuntimeTransport } from '../../src/transports/forge'; + +const dsn = 'http://9e9fd4523d784609a5fc0ebb1080592f@sentry.io:8989/mysubpath/50622'; +const silenceErrors = () => null; + +describe('ForgeRuntimeTransport', () => { + test('calls Forge runtime API fetch()', async () => { + const requestSpy = jest.fn().mockResolvedValue({ + ok: true, + status: 200, + }); + const transport = new ForgeRuntimeTransport({ dsn, fetch: requestSpy, logError: silenceErrors }); + await transport.sendEvent({}); + expect(requestSpy).toHaveBeenCalled(); + }); + + test('rejects if Forge runtime API fetch() rejects', () => { + const requestSpy = jest.fn().mockRejectedValue(new Error('Fetch error to console.error')); + const transport = new ForgeRuntimeTransport({ dsn, fetch: requestSpy, logError: silenceErrors }); + + return expect(transport.sendEvent({})).rejects.toEqual(new SentryError('HTTP Error (500)')); + }); + + test('rejects if API response is not ok', () => { + const requestSpy = jest.fn().mockResolvedValue({ + ok: false, + status: 400, + }); + const transport = new ForgeRuntimeTransport({ dsn, fetch: requestSpy, logError: silenceErrors }); + return expect(transport.sendEvent({})).rejects.toEqual(new SentryError('HTTP Error (400)')); + }); +}); diff --git a/yarn.lock b/yarn.lock index 724aa12da587..200fd60f486f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -279,16 +279,11 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0": +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3", "@babel/parser@^7.13.13", "@babel/parser@^7.4.3", "@babel/parser@^7.4.5", "@babel/parser@^7.7.0": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.2.tgz#0c1680aa44ad4605b16cbdcc5c341a61bde9c746" integrity sha512-IoVDIHpsgE/fu7eXBeRWt8zLbDrSvD7H1gpomOkPpBoEN8KCruCqSDdqo8dddwQQrui30KSvQBaMUOJiuFu6QQ== -"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3", "@babel/parser@^7.13.13", "@babel/parser@^7.4.3", "@babel/parser@^7.4.5", "@babel/parser@^7.7.0": - version "7.13.13" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df" - integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a" @@ -1189,6 +1184,28 @@ reflect-metadata "^0.1.12" tslib "^1.8.1" +"@forge/api@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@forge/api/-/api-2.1.0.tgz#dd632a0b04c605061ab832987ae4bb1c0a343b07" + integrity sha512-hxbIL2coZtKLcLO/N1GhokB1aJ3kjQtjsP3kziSrsjjF0Fh014UA36mpKg//xDae0gRPtditRMR6wFgxP9z8vg== + dependencies: + "@forge/auth" "^0.0.1" + "@forge/storage" "^1.0.4" + "@types/node-fetch" "^2.5.7" + node-fetch "^2.6.1" + +"@forge/auth@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@forge/auth/-/auth-0.0.1.tgz#3fb4e82e90bd555d4ae9ef45b3aed6782a282d0c" + integrity sha512-twZjWbIk+PrW2XzrUfVCzYhh1qe5igS4h9NpapZLHNm2CaCi1gjh8klVcGJijcYJWT1Sj6Qr9gBUtkZjCinJXw== + dependencies: + tslib "^1.11.0" + +"@forge/storage@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@forge/storage/-/storage-1.0.4.tgz#41a8ee741415ce432a1d104b02e0802f35212247" + integrity sha512-sHNSfV6H3/8/hnVAfoSy5qQd29hggnigLZy8UBOpRy1yM0qB/wo0Dsf0W0jE/ImbborLW35mgM6CB0BhriW1BQ== + "@glimmer/component@^1.0.0": version "1.0.4" resolved "https://registry.yarnpkg.com/@glimmer/component/-/component-1.0.4.tgz#1c85a5181615a6647f6acfaaed68e28ad7e9626e" @@ -3056,7 +3073,7 @@ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== -"@types/eslint@*": +"@types/eslint@*", "@types/eslint@^7.2.0": version "7.2.10" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.10.tgz#4b7a9368d46c0f8cd5408c23288a59aa2394d917" integrity sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ== @@ -3064,14 +3081,6 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/eslint@^7.2.0": - version "7.2.7" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.7.tgz#f7ef1cf0dceab0ae6f9a976a0a9af14ab1baca26" - integrity sha512-EHXbc1z2GoQRqHaAT7+grxlTJ3WE2YNeD6jlpPoRc83cCoThRY+NUWjCUZaYmk51OICkPXn2hhphcWcWXgNW0Q== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - "@types/estree@*", "@types/estree@^0.0.47": version "0.0.47" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4" @@ -3248,6 +3257,14 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ== +"@types/node-fetch@^2.5.7": + version "2.5.10" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.10.tgz#9b4d4a0425562f9fcea70b12cb3fcdd946ca8132" + integrity sha512-IpkX0AasN44hgEad0gEF/V6EgR5n69VEqPEgnmoM8GsIGro3PowbWs4tR6IhxUTyPLpOn+fiGG6nrQhcmoCuIQ== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + "@types/node@*", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@^14.6.4": version "14.14.37" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e" @@ -3930,16 +3947,6 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^7.0.2: - version "7.1.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.1.1.tgz#1e6b37a454021fa9941713f38b952fc1c8d32a84" - integrity sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - ajv@^8.0.1: version "8.5.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.5.0.tgz#695528274bcb5afc865446aa275484049a18ae4b" @@ -6383,12 +6390,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001181: - version "1.0.30001204" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz#256c85709a348ec4d175e847a3b515c66e79f2aa" - integrity sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ== - -caniuse-lite@^1.0.30001173, caniuse-lite@^1.0.30001179: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001173, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001181: version "1.0.30001218" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001218.tgz#9b44f6ed16f875db6373e2debd4d14a07359002f" integrity sha512-0ASydOWSy3bB88FbDpJSTt+PfDwnMqrym3yRZfqG8EXSQ06OZhF+q5wgYP/EN+jJMERItNcDQUqMyNjzZ+r5+Q== @@ -6836,7 +6838,7 @@ combine-source-map@^0.8.0: lodash.memoize "~3.0.3" source-map "~0.5.3" -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -8172,12 +8174,7 @@ ee-first@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.47, electron-to-chromium@^1.3.649: - version "1.3.702" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.702.tgz#39b8b6860b22806482ad07a8eaf35f861d4f3ce0" - integrity sha512-qJVUKFWQnF6wP7MmTngDkmm8/KPzaiTXNFOAg5j7DSa6J7kPou7mTBqC8jpUOxauQWwHR3pn4dMRdV8IE1xdtA== - -electron-to-chromium@^1.3.634: +electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.634, electron-to-chromium@^1.3.649: version "1.3.722" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.722.tgz#621657f79e7f65402e71aa3403bc941f3a4af0a0" integrity sha512-aAsc906l0RBsVTsGTK+KirVfey9eNtxyejdkbNzkISGxb7AFna3Kf0qvsp8tMttzBt9Bz3HddtYQ+++/PZtRYA== @@ -9003,7 +9000,7 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.5.0: memory-fs "^0.5.0" tapable "^1.0.0" -enhanced-resolve@^5.3.2: +enhanced-resolve@^5.3.2, enhanced-resolve@^5.8.0: version "5.8.2" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b" integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA== @@ -9011,14 +9008,6 @@ enhanced-resolve@^5.3.2: graceful-fs "^4.2.4" tapable "^2.2.0" -enhanced-resolve@^5.8.0: - version "5.8.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.0.tgz#d9deae58f9d3773b6a111a5a46831da5be5c9ac0" - integrity sha512-Sl3KRpJA8OpprrtaIswVki3cWPiPKxXuFxJXBp+zNb6s6VwNWwFRUdtmzd2ReUut8n+sCPx7QCtQ7w5wfJhSgQ== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - enquirer@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -10248,6 +10237,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@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + 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" @@ -14369,24 +14367,12 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.46.0, "mime-db@>= 1.43.0 < 2": - version "1.46.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee" - integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== - -mime-db@1.47.0: +mime-db@1.47.0, "mime-db@>= 1.43.0 < 2": version "1.47.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== -mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.26, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.29" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2" - integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ== - dependencies: - mime-db "1.46.0" - -mime-types@^2.1.27: +mime-types@^2.0.8, mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.26, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.30" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== @@ -19187,17 +19173,7 @@ sync-disk-cache@^2.0.0: rimraf "^3.0.0" username-sync "^1.0.2" -table@^6.0.4: - version "6.0.7" - resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" - integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== - dependencies: - ajv "^7.0.2" - lodash "^4.17.20" - slice-ansi "^4.0.0" - string-width "^4.2.0" - -table@^6.0.9: +table@^6.0.4, table@^6.0.9: version "6.7.1" resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== @@ -19756,7 +19732,7 @@ tslib@1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslib@^1.10.0, tslib@^1.7.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1.10.0, tslib@^1.11.0, tslib@^1.7.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==