From a961e5717944525a51f5de1c9177f9f361c02f71 Mon Sep 17 00:00:00 2001 From: Kev <6111995+k-fish@users.noreply.github.com> Date: Mon, 13 Feb 2023 15:14:04 -0500 Subject: [PATCH 01/21] feat(tracing): Track `PerformanceResourceTiming.renderBlockingStatus` (#7127) * ref(perf): Add renderBlockingStatus As per https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/renderBlockingStatus the resource timing has information about whether an asset is blocking render or not, which is useful for determining which assets need to be addressed for fixing critical path. --- packages/replay/test/fixtures/transaction.ts | 1 + packages/tracing/src/browser/metrics/index.ts | 4 ++++ packages/tracing/test/browser/metrics/index.test.ts | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/packages/replay/test/fixtures/transaction.ts b/packages/replay/test/fixtures/transaction.ts index 24f89cdc9fb8..e1d686eadd01 100644 --- a/packages/replay/test/fixtures/transaction.ts +++ b/packages/replay/test/fixtures/transaction.ts @@ -77,6 +77,7 @@ export function Transaction(obj?: Partial): any { 'Transfer Size': 1097, 'Encoded Body Size': 797, 'Decoded Body Size': 1885, + 'resource.render_blocking_status': 'non-blocking', }, description: '/favicon.ico', op: 'resource.other', diff --git a/packages/tracing/src/browser/metrics/index.ts b/packages/tracing/src/browser/metrics/index.ts index 67cdc1714ed9..b59f344899d2 100644 --- a/packages/tracing/src/browser/metrics/index.ts +++ b/packages/tracing/src/browser/metrics/index.ts @@ -333,6 +333,7 @@ export interface ResourceEntry extends Record { transferSize?: number; encodedBodySize?: number; decodedBodySize?: number; + renderBlockingStatus?: string; } /** Create resource-related spans */ @@ -361,6 +362,9 @@ export function _addResourceSpans( if ('decodedBodySize' in entry) { data['Decoded Body Size'] = entry.decodedBodySize; } + if ('renderBlockingStatus' in entry) { + data['resource.render_blocking_status'] = entry.renderBlockingStatus; + } const startTimestamp = timeOrigin + startTime; const endTimestamp = startTimestamp + duration; diff --git a/packages/tracing/test/browser/metrics/index.test.ts b/packages/tracing/test/browser/metrics/index.test.ts index 4820a70a5c9b..0622043a598c 100644 --- a/packages/tracing/test/browser/metrics/index.test.ts +++ b/packages/tracing/test/browser/metrics/index.test.ts @@ -48,6 +48,7 @@ describe('_addResourceSpans', () => { transferSize: 256, encodedBodySize: 256, decodedBodySize: 256, + renderBlockingStatus: 'non-blocking', }; _addResourceSpans(transaction, entry, '/assets/to/me', 123, 456, 100); @@ -61,6 +62,7 @@ describe('_addResourceSpans', () => { transferSize: 256, encodedBodySize: 256, decodedBodySize: 256, + renderBlockingStatus: 'non-blocking', }; _addResourceSpans(transaction, entry, '/assets/to/me', 123, 456, 100); @@ -74,6 +76,7 @@ describe('_addResourceSpans', () => { transferSize: 256, encodedBodySize: 456, decodedBodySize: 593, + renderBlockingStatus: 'non-blocking', }; const timeOrigin = 100; @@ -90,6 +93,7 @@ describe('_addResourceSpans', () => { ['Decoded Body Size']: entry.decodedBodySize, ['Encoded Body Size']: entry.encodedBodySize, ['Transfer Size']: entry.transferSize, + ['resource.render_blocking_status']: entry.renderBlockingStatus, }, description: '/assets/to/css', endTimestamp: timeOrigin + startTime + duration, @@ -143,6 +147,7 @@ describe('_addResourceSpans', () => { transferSize: 0, encodedBodySize: 0, decodedBodySize: 0, + renderBlockingStatus: 'non-blocking', }; _addResourceSpans(transaction, entry, '/assets/to/css', 100, 23, 345); @@ -156,6 +161,7 @@ describe('_addResourceSpans', () => { ['Decoded Body Size']: entry.decodedBodySize, ['Encoded Body Size']: entry.encodedBodySize, ['Transfer Size']: entry.transferSize, + ['resource.render_blocking_status']: entry.renderBlockingStatus, }, }), ); From 2c514edac1102eeee59a4556cba155309d4c2be1 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Mon, 13 Feb 2023 15:15:34 -0500 Subject: [PATCH 02/21] test(integration): Add support for snapshots in integration tests (#7145) * adds `yarn test:update-snapshots` script to update snapshots easily. This will run tests across all browsers and update the snapshots accordingly. --- package.json | 3 +- packages/integration-tests/package.json | 3 +- .../suites/replay/privacy/template.html | 1 + .../suites/replay/privacy/test.ts | 252 +---------------- .../test.ts-snapshots/privacy-chromium.json | 266 ++++++++++++++++++ .../test.ts-snapshots/privacy-firefox.json | 266 ++++++++++++++++++ .../test.ts-snapshots/privacy-webkit.json | 266 ++++++++++++++++++ packages/integration-tests/utils/fixtures.ts | 9 + 8 files changed, 813 insertions(+), 253 deletions(-) create mode 100644 packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-chromium.json create mode 100644 packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-firefox.json create mode 100644 packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-webkit.json diff --git a/package.json b/package.json index 40dc91097f47..2d02e39096a6 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "postpublish": "lerna run --stream --concurrency 1 postpublish", "test": "lerna run --ignore @sentry-internal/* test", "test-ci-browser": "lerna run test --ignore \"@sentry/{node,opentelemetry-node,serverless,nextjs,remix,gatsby}\" --ignore @sentry-internal/*", - "test-ci-node": "ts-node ./scripts/node-unit-tests.ts" + "test-ci-node": "ts-node ./scripts/node-unit-tests.ts", + "test:update-snapshots": "lerna run test:update-snapshots" }, "volta": { "node": "16.19.0", diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 9978b0559cbb..c0470e4abb9d 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -27,7 +27,8 @@ "test:bundle:replay:es6:min": "PW_BUNDLE=bundle_replay_es6_min yarn test", "test:cjs": "PW_BUNDLE=cjs yarn test", "test:esm": "PW_BUNDLE=esm yarn test", - "test:ci": "playwright test ./suites --browser='all' --reporter='line'" + "test:ci": "playwright test ./suites --browser='all' --reporter='line'", + "test:update-snapshots": "yarn test --update-snapshots --browser='all'" }, "dependencies": { "@babel/preset-typescript": "^7.16.7", diff --git a/packages/integration-tests/suites/replay/privacy/template.html b/packages/integration-tests/suites/replay/privacy/template.html index a61532e6ae68..2eb0f8df7b37 100644 --- a/packages/integration-tests/suites/replay/privacy/template.html +++ b/packages/integration-tests/suites/replay/privacy/template.html @@ -11,5 +11,6 @@ + diff --git a/packages/integration-tests/suites/replay/privacy/test.ts b/packages/integration-tests/suites/replay/privacy/test.ts index 2c2b1f9b8070..a6315d5d1e66 100644 --- a/packages/integration-tests/suites/replay/privacy/test.ts +++ b/packages/integration-tests/suites/replay/privacy/test.ts @@ -27,255 +27,5 @@ sentryTest('should have the correct default privacy settings', async ({ getLocal const replayPayload = envelopeRequestParser(await reqPromise0, 5); const checkoutEvent = replayPayload.find(({ type }) => type === EventType.FullSnapshot); - expect(checkoutEvent?.data).toEqual({ - node: { - type: 0, - childNodes: [ - { - type: 1, - name: 'html', - publicId: '', - systemId: '', - id: 2, - }, - { - type: 2, - tagName: 'html', - attributes: {}, - childNodes: [ - { - type: 2, - tagName: 'head', - attributes: {}, - childNodes: [ - { - type: 2, - tagName: 'meta', - attributes: { - charset: 'utf-8', - }, - childNodes: [], - id: 5, - }, - ], - id: 4, - }, - { - type: 3, - textContent: '\n ', - id: 6, - }, - { - type: 2, - tagName: 'body', - attributes: {}, - childNodes: [ - { - type: 3, - textContent: '\n ', - id: 8, - }, - { - type: 2, - tagName: 'button', - attributes: { - 'aria-label': '***** **', - onclick: "console.log('Test log')", - }, - childNodes: [ - { - type: 3, - textContent: '***** **', - id: 10, - }, - ], - id: 9, - }, - { - type: 3, - textContent: '\n ', - id: 11, - }, - { - type: 2, - tagName: 'div', - attributes: {}, - childNodes: [ - { - type: 3, - textContent: '**** ****** ** ****** ** *******', - id: 13, - }, - ], - id: 12, - }, - { - type: 3, - textContent: '\n ', - id: 14, - }, - { - type: 2, - tagName: 'div', - attributes: { - 'data-sentry-unmask': '', - }, - childNodes: [ - { - type: 3, - textContent: 'This should be unmasked due to data attribute', - id: 16, - }, - ], - id: 15, - }, - { - type: 3, - textContent: '\n ', - id: 17, - }, - { - type: 2, - tagName: 'input', - attributes: { - placeholder: '*********** ****** ** ******', - }, - childNodes: [], - id: 18, - }, - { - type: 3, - textContent: '\n ', - id: 19, - }, - { - type: 2, - tagName: 'div', - attributes: { - title: '***** ****** ** ******', - }, - childNodes: [ - { - type: 3, - textContent: '***** ****** ** ******', - id: 21, - }, - ], - id: 20, - }, - { - type: 3, - textContent: '\n ', - id: 22, - }, - { - type: 2, - tagName: 'svg', - attributes: { - rr_width: '200px', - rr_height: '200px', - }, - childNodes: [], - isSVG: true, - id: 23, - }, - { - type: 3, - textContent: '\n ', - id: 24, - }, - { - type: 2, - tagName: 'svg', - attributes: { - style: 'width:200px;height:200px', - viewBox: '0 0 80 80', - 'data-sentry-unblock': '', - }, - childNodes: [ - { - type: 2, - tagName: 'path', - attributes: { - d: '', - }, - childNodes: [], - isSVG: true, - id: 26, - }, - { - type: 2, - tagName: 'area', - attributes: {}, - childNodes: [], - isSVG: true, - id: 27, - }, - { - type: 2, - tagName: 'rect', - attributes: {}, - childNodes: [], - isSVG: true, - id: 28, - }, - ], - isSVG: true, - id: 25, - }, - { - type: 3, - textContent: '\n ', - id: 29, - }, - { - type: 2, - tagName: 'img', - attributes: { - rr_width: '100px', - rr_height: '100px', - }, - childNodes: [], - id: 30, - }, - { - type: 3, - textContent: '\n ', - id: 31, - }, - { - type: 2, - tagName: 'img', - attributes: { - 'data-sentry-unblock': '', - style: 'width:100px;height:100px', - src: 'file:///none.png', - }, - childNodes: [], - id: 32, - }, - { - type: 3, - textContent: '\n ', - id: 33, - }, - { - type: 3, - textContent: '\n\n', - id: 34, - }, - ], - id: 7, - }, - ], - id: 3, - }, - ], - id: 1, - }, - initialOffset: { - left: 0, - top: 0, - }, - }); + expect(JSON.stringify(checkoutEvent?.data, null, 2)).toMatchSnapshot('privacy.json'); }); diff --git a/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-chromium.json b/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-chromium.json new file mode 100644 index 000000000000..d0cb968d61ff --- /dev/null +++ b/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-chromium.json @@ -0,0 +1,266 @@ +{ + "node": { + "type": 0, + "childNodes": [ + { + "type": 1, + "name": "html", + "publicId": "", + "systemId": "", + "id": 2 + }, + { + "type": 2, + "tagName": "html", + "attributes": {}, + "childNodes": [ + { + "type": 2, + "tagName": "head", + "attributes": {}, + "childNodes": [ + { + "type": 2, + "tagName": "meta", + "attributes": { + "charset": "utf-8" + }, + "childNodes": [], + "id": 5 + } + ], + "id": 4 + }, + { + "type": 3, + "textContent": "\n ", + "id": 6 + }, + { + "type": 2, + "tagName": "body", + "attributes": {}, + "childNodes": [ + { + "type": 3, + "textContent": "\n ", + "id": 8 + }, + { + "type": 2, + "tagName": "button", + "attributes": { + "aria-label": "***** **", + "onclick": "console.log('Test log')" + }, + "childNodes": [ + { + "type": 3, + "textContent": "***** **", + "id": 10 + } + ], + "id": 9 + }, + { + "type": 3, + "textContent": "\n ", + "id": 11 + }, + { + "type": 2, + "tagName": "div", + "attributes": {}, + "childNodes": [ + { + "type": 3, + "textContent": "**** ****** ** ****** ** *******", + "id": 13 + } + ], + "id": 12 + }, + { + "type": 3, + "textContent": "\n ", + "id": 14 + }, + { + "type": 2, + "tagName": "div", + "attributes": { + "data-sentry-unmask": "" + }, + "childNodes": [ + { + "type": 3, + "textContent": "This should be unmasked due to data attribute", + "id": 16 + } + ], + "id": 15 + }, + { + "type": 3, + "textContent": "\n ", + "id": 17 + }, + { + "type": 2, + "tagName": "input", + "attributes": { + "placeholder": "*********** ****** ** ******" + }, + "childNodes": [], + "id": 18 + }, + { + "type": 3, + "textContent": "\n ", + "id": 19 + }, + { + "type": 2, + "tagName": "div", + "attributes": { + "title": "***** ****** ** ******" + }, + "childNodes": [ + { + "type": 3, + "textContent": "***** ****** ** ******", + "id": 21 + } + ], + "id": 20 + }, + { + "type": 3, + "textContent": "\n ", + "id": 22 + }, + { + "type": 2, + "tagName": "svg", + "attributes": { + "rr_width": "200px", + "rr_height": "200px" + }, + "childNodes": [], + "isSVG": true, + "id": 23 + }, + { + "type": 3, + "textContent": "\n ", + "id": 24 + }, + { + "type": 2, + "tagName": "svg", + "attributes": { + "style": "width:200px;height:200px", + "viewBox": "0 0 80 80", + "data-sentry-unblock": "" + }, + "childNodes": [ + { + "type": 2, + "tagName": "path", + "attributes": { + "d": "" + }, + "childNodes": [], + "isSVG": true, + "id": 26 + }, + { + "type": 2, + "tagName": "area", + "attributes": {}, + "childNodes": [], + "isSVG": true, + "id": 27 + }, + { + "type": 2, + "tagName": "rect", + "attributes": {}, + "childNodes": [], + "isSVG": true, + "id": 28 + } + ], + "isSVG": true, + "id": 25 + }, + { + "type": 3, + "textContent": "\n ", + "id": 29 + }, + { + "type": 2, + "tagName": "img", + "attributes": { + "rr_width": "100px", + "rr_height": "100px" + }, + "childNodes": [], + "id": 30 + }, + { + "type": 3, + "textContent": "\n ", + "id": 31 + }, + { + "type": 2, + "tagName": "img", + "attributes": { + "data-sentry-unblock": "", + "style": "width:100px;height:100px", + "src": "file:///none.png" + }, + "childNodes": [], + "id": 32 + }, + { + "type": 3, + "textContent": "\n ", + "id": 33 + }, + { + "type": 2, + "tagName": "video", + "attributes": { + "rr_width": "30px", + "rr_height": "30px" + }, + "childNodes": [], + "id": 34 + }, + { + "type": 3, + "textContent": "\n ", + "id": 35 + }, + { + "type": 3, + "textContent": "\n\n", + "id": 36 + } + ], + "id": 7 + } + ], + "id": 3 + } + ], + "id": 1 + }, + "initialOffset": { + "left": 0, + "top": 0 + } +} \ No newline at end of file diff --git a/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-firefox.json b/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-firefox.json new file mode 100644 index 000000000000..d0cb968d61ff --- /dev/null +++ b/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-firefox.json @@ -0,0 +1,266 @@ +{ + "node": { + "type": 0, + "childNodes": [ + { + "type": 1, + "name": "html", + "publicId": "", + "systemId": "", + "id": 2 + }, + { + "type": 2, + "tagName": "html", + "attributes": {}, + "childNodes": [ + { + "type": 2, + "tagName": "head", + "attributes": {}, + "childNodes": [ + { + "type": 2, + "tagName": "meta", + "attributes": { + "charset": "utf-8" + }, + "childNodes": [], + "id": 5 + } + ], + "id": 4 + }, + { + "type": 3, + "textContent": "\n ", + "id": 6 + }, + { + "type": 2, + "tagName": "body", + "attributes": {}, + "childNodes": [ + { + "type": 3, + "textContent": "\n ", + "id": 8 + }, + { + "type": 2, + "tagName": "button", + "attributes": { + "aria-label": "***** **", + "onclick": "console.log('Test log')" + }, + "childNodes": [ + { + "type": 3, + "textContent": "***** **", + "id": 10 + } + ], + "id": 9 + }, + { + "type": 3, + "textContent": "\n ", + "id": 11 + }, + { + "type": 2, + "tagName": "div", + "attributes": {}, + "childNodes": [ + { + "type": 3, + "textContent": "**** ****** ** ****** ** *******", + "id": 13 + } + ], + "id": 12 + }, + { + "type": 3, + "textContent": "\n ", + "id": 14 + }, + { + "type": 2, + "tagName": "div", + "attributes": { + "data-sentry-unmask": "" + }, + "childNodes": [ + { + "type": 3, + "textContent": "This should be unmasked due to data attribute", + "id": 16 + } + ], + "id": 15 + }, + { + "type": 3, + "textContent": "\n ", + "id": 17 + }, + { + "type": 2, + "tagName": "input", + "attributes": { + "placeholder": "*********** ****** ** ******" + }, + "childNodes": [], + "id": 18 + }, + { + "type": 3, + "textContent": "\n ", + "id": 19 + }, + { + "type": 2, + "tagName": "div", + "attributes": { + "title": "***** ****** ** ******" + }, + "childNodes": [ + { + "type": 3, + "textContent": "***** ****** ** ******", + "id": 21 + } + ], + "id": 20 + }, + { + "type": 3, + "textContent": "\n ", + "id": 22 + }, + { + "type": 2, + "tagName": "svg", + "attributes": { + "rr_width": "200px", + "rr_height": "200px" + }, + "childNodes": [], + "isSVG": true, + "id": 23 + }, + { + "type": 3, + "textContent": "\n ", + "id": 24 + }, + { + "type": 2, + "tagName": "svg", + "attributes": { + "style": "width:200px;height:200px", + "viewBox": "0 0 80 80", + "data-sentry-unblock": "" + }, + "childNodes": [ + { + "type": 2, + "tagName": "path", + "attributes": { + "d": "" + }, + "childNodes": [], + "isSVG": true, + "id": 26 + }, + { + "type": 2, + "tagName": "area", + "attributes": {}, + "childNodes": [], + "isSVG": true, + "id": 27 + }, + { + "type": 2, + "tagName": "rect", + "attributes": {}, + "childNodes": [], + "isSVG": true, + "id": 28 + } + ], + "isSVG": true, + "id": 25 + }, + { + "type": 3, + "textContent": "\n ", + "id": 29 + }, + { + "type": 2, + "tagName": "img", + "attributes": { + "rr_width": "100px", + "rr_height": "100px" + }, + "childNodes": [], + "id": 30 + }, + { + "type": 3, + "textContent": "\n ", + "id": 31 + }, + { + "type": 2, + "tagName": "img", + "attributes": { + "data-sentry-unblock": "", + "style": "width:100px;height:100px", + "src": "file:///none.png" + }, + "childNodes": [], + "id": 32 + }, + { + "type": 3, + "textContent": "\n ", + "id": 33 + }, + { + "type": 2, + "tagName": "video", + "attributes": { + "rr_width": "30px", + "rr_height": "30px" + }, + "childNodes": [], + "id": 34 + }, + { + "type": 3, + "textContent": "\n ", + "id": 35 + }, + { + "type": 3, + "textContent": "\n\n", + "id": 36 + } + ], + "id": 7 + } + ], + "id": 3 + } + ], + "id": 1 + }, + "initialOffset": { + "left": 0, + "top": 0 + } +} \ No newline at end of file diff --git a/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-webkit.json b/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-webkit.json new file mode 100644 index 000000000000..d0cb968d61ff --- /dev/null +++ b/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy-webkit.json @@ -0,0 +1,266 @@ +{ + "node": { + "type": 0, + "childNodes": [ + { + "type": 1, + "name": "html", + "publicId": "", + "systemId": "", + "id": 2 + }, + { + "type": 2, + "tagName": "html", + "attributes": {}, + "childNodes": [ + { + "type": 2, + "tagName": "head", + "attributes": {}, + "childNodes": [ + { + "type": 2, + "tagName": "meta", + "attributes": { + "charset": "utf-8" + }, + "childNodes": [], + "id": 5 + } + ], + "id": 4 + }, + { + "type": 3, + "textContent": "\n ", + "id": 6 + }, + { + "type": 2, + "tagName": "body", + "attributes": {}, + "childNodes": [ + { + "type": 3, + "textContent": "\n ", + "id": 8 + }, + { + "type": 2, + "tagName": "button", + "attributes": { + "aria-label": "***** **", + "onclick": "console.log('Test log')" + }, + "childNodes": [ + { + "type": 3, + "textContent": "***** **", + "id": 10 + } + ], + "id": 9 + }, + { + "type": 3, + "textContent": "\n ", + "id": 11 + }, + { + "type": 2, + "tagName": "div", + "attributes": {}, + "childNodes": [ + { + "type": 3, + "textContent": "**** ****** ** ****** ** *******", + "id": 13 + } + ], + "id": 12 + }, + { + "type": 3, + "textContent": "\n ", + "id": 14 + }, + { + "type": 2, + "tagName": "div", + "attributes": { + "data-sentry-unmask": "" + }, + "childNodes": [ + { + "type": 3, + "textContent": "This should be unmasked due to data attribute", + "id": 16 + } + ], + "id": 15 + }, + { + "type": 3, + "textContent": "\n ", + "id": 17 + }, + { + "type": 2, + "tagName": "input", + "attributes": { + "placeholder": "*********** ****** ** ******" + }, + "childNodes": [], + "id": 18 + }, + { + "type": 3, + "textContent": "\n ", + "id": 19 + }, + { + "type": 2, + "tagName": "div", + "attributes": { + "title": "***** ****** ** ******" + }, + "childNodes": [ + { + "type": 3, + "textContent": "***** ****** ** ******", + "id": 21 + } + ], + "id": 20 + }, + { + "type": 3, + "textContent": "\n ", + "id": 22 + }, + { + "type": 2, + "tagName": "svg", + "attributes": { + "rr_width": "200px", + "rr_height": "200px" + }, + "childNodes": [], + "isSVG": true, + "id": 23 + }, + { + "type": 3, + "textContent": "\n ", + "id": 24 + }, + { + "type": 2, + "tagName": "svg", + "attributes": { + "style": "width:200px;height:200px", + "viewBox": "0 0 80 80", + "data-sentry-unblock": "" + }, + "childNodes": [ + { + "type": 2, + "tagName": "path", + "attributes": { + "d": "" + }, + "childNodes": [], + "isSVG": true, + "id": 26 + }, + { + "type": 2, + "tagName": "area", + "attributes": {}, + "childNodes": [], + "isSVG": true, + "id": 27 + }, + { + "type": 2, + "tagName": "rect", + "attributes": {}, + "childNodes": [], + "isSVG": true, + "id": 28 + } + ], + "isSVG": true, + "id": 25 + }, + { + "type": 3, + "textContent": "\n ", + "id": 29 + }, + { + "type": 2, + "tagName": "img", + "attributes": { + "rr_width": "100px", + "rr_height": "100px" + }, + "childNodes": [], + "id": 30 + }, + { + "type": 3, + "textContent": "\n ", + "id": 31 + }, + { + "type": 2, + "tagName": "img", + "attributes": { + "data-sentry-unblock": "", + "style": "width:100px;height:100px", + "src": "file:///none.png" + }, + "childNodes": [], + "id": 32 + }, + { + "type": 3, + "textContent": "\n ", + "id": 33 + }, + { + "type": 2, + "tagName": "video", + "attributes": { + "rr_width": "30px", + "rr_height": "30px" + }, + "childNodes": [], + "id": 34 + }, + { + "type": 3, + "textContent": "\n ", + "id": 35 + }, + { + "type": 3, + "textContent": "\n\n", + "id": 36 + } + ], + "id": 7 + } + ], + "id": 3 + } + ], + "id": 1 + }, + "initialOffset": { + "left": 0, + "top": 0 + } +} \ No newline at end of file diff --git a/packages/integration-tests/utils/fixtures.ts b/packages/integration-tests/utils/fixtures.ts index 3926bf1f9d74..15c41ab10c14 100644 --- a/packages/integration-tests/utils/fixtures.ts +++ b/packages/integration-tests/utils/fixtures.ts @@ -22,6 +22,7 @@ const getAsset = (assetDir: string, asset: string): string => { }; export type TestFixtures = { + _autoSnapshotSuffix: void; testDir: string; getLocalTestPath: (options: { testDir: string }) => Promise; runInChromium: (fn: (...args: unknown[]) => unknown, args?: unknown[]) => unknown; @@ -35,6 +36,14 @@ export type TestFixtures = { }; const sentryTest = base.extend({ + _autoSnapshotSuffix: [ + async ({}, use, testInfo) => { + testInfo.snapshotSuffix = ''; + await use(); + }, + { auto: true }, + ], + getLocalTestPath: ({}, use, testInfo) => { return use(async ({ testDir }) => { const pagePath = `file:///${path.resolve(testDir, './dist/index.html')}`; From d604022f6347922111a5f46f756a417e796c2704 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 14 Feb 2023 09:13:48 +0100 Subject: [PATCH 03/21] build: Improve CI cache (#7141) --- .github/workflows/auto-release.yml | 2 +- .github/workflows/build.yml | 64 +++++++++++++++--------------- .gitignore | 1 + .prettierignore | 1 + nx.json | 3 +- package.json | 2 +- 6 files changed, 38 insertions(+), 35 deletions(-) diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 4d81c7f233b2..cd82966591ce 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -25,7 +25,7 @@ jobs: # Parse version from head branch text: ${{ github.head_ref }} # match: preprare-release/xx.xx.xx - regex: '^preprare-release\/(\d+\.\d+\.\d+)$' + regex: '^prepare-release\/(\d+\.\d+\.\d+)$' - name: Prepare release uses: getsentry/action-prepare-release@v1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9716113ce134..dd293aedac8f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,6 @@ env: NX_CACHE_RESTORE_KEYS: | nx-Linux-${{ github.ref }}-${{ github.event.inputs.commit || github.sha }} nx-Linux-${{ github.ref }} - nx-Linux-refs/heads/develop nx-Linux jobs: @@ -173,6 +172,7 @@ jobs: key: ${{ steps.compute_lockfile_hash.outputs.hash }} - name: Install dependencies + if: steps.cache_dependencies.outputs.cache_hit != 'true' run: yarn install --ignore-engines --frozen-lockfile outputs: dependency_cache_key: ${{ steps.compute_lockfile_hash.outputs.hash }} @@ -212,7 +212,7 @@ jobs: needs.job_get_metadata.outputs.is_release == 'false' && needs.job_get_metadata.outputs.force_skip_cache == 'false' with: - path: node_modules/.cache/nx + path: .nxcache key: nx-Linux-${{ github.ref }}-${{ env.HEAD_COMMIT }} # On develop branch, we want to _store_ the cache (so it can be used by other branches), but never _restore_ from it restore-keys: @@ -244,12 +244,12 @@ jobs: - name: Set up Node uses: volta-cli/action@v4 - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -288,12 +288,12 @@ jobs: # use Node 14 for now. node-version: '14' - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -319,12 +319,12 @@ jobs: - name: Set up Node uses: volta-cli/action@v4 - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -344,12 +344,12 @@ jobs: - name: Set up Node uses: volta-cli/action@v4 - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -370,12 +370,12 @@ jobs: - name: Set up Node uses: volta-cli/action@v4 - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -407,12 +407,12 @@ jobs: with: node-version: ${{ env.DEFAULT_NODE_VERSION }} - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -442,12 +442,12 @@ jobs: with: node-version: ${{ matrix.node }} - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -480,12 +480,12 @@ jobs: with: node-version: ${{ matrix.node }} - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -553,12 +553,12 @@ jobs: - name: Set up Node uses: volta-cli/action@v4 - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -611,12 +611,12 @@ jobs: - name: Set up Node uses: volta-cli/action@v4 - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -641,12 +641,12 @@ jobs: - name: Set up Node uses: volta-cli/action@v4 - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -679,12 +679,12 @@ jobs: with: node-version: ${{ matrix.node }} - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -715,12 +715,12 @@ jobs: with: node-version: ${{ matrix.node }} - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -749,12 +749,12 @@ jobs: - name: Set up Node uses: volta-cli/action@v4 - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} @@ -812,12 +812,12 @@ jobs: - name: Set up Node uses: volta-cli/action@v4 - name: Check dependency cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check build cache - uses: actions/cache@v3 + uses: actions/cache/restore@v3 with: path: ${{ env.CACHED_BUILD_PATHS }} key: ${{ env.BUILD_CACHE_KEY }} diff --git a/.gitignore b/.gitignore index d822f532c8cc..8574a81de0a4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ sentry-node-serverless-*.zip jest/transformers/*.js # node tarballs packages/*/sentry-*.tgz +.nxcache # logs yarn-error.log diff --git a/.prettierignore b/.prettierignore index dd449725e188..f2ddf012c98e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ *.md +.nxcache diff --git a/nx.json b/nx.json index 1020417113c4..a4cbbbcd85da 100644 --- a/nx.json +++ b/nx.json @@ -8,7 +8,8 @@ "build:transpile", "build:types", "lint:eslint" - ] + ], + "cacheDirectory": ".nxcache" } } }, diff --git a/package.json b/package.json index 2d02e39096a6..8f00f559c4da 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "circularDepCheck": "lerna run circularDepCheck", "clean": "run-p clean:build clean:caches", "clean:build": "lerna run clean", - "clean:caches": "yarn rimraf eslintcache && yarn jest --clearCache", + "clean:caches": "yarn rimraf eslintcache .nxcache && yarn jest --clearCache", "clean:deps": "lerna clean --yes && rm -rf node_modules && yarn", "clean:all": "run-p clean:build clean:caches clean:deps", "codecov": "codecov", From e9eec27e6a8401a24b30e71c97bae658ec347f26 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 14 Feb 2023 09:53:47 +0100 Subject: [PATCH 04/21] feat: Put `abs_path` into stack frame object (#7167) --- packages/browser/src/stack-parsers.ts | 4 +- .../test/unit/tracekit/chromium.test.ts | 367 +++++++++++-- .../test/unit/tracekit/firefox.test.ts | 346 ++++++++++-- .../browser/test/unit/tracekit/ie.test.ts | 74 ++- .../browser/test/unit/tracekit/misc.test.ts | 24 +- .../browser/test/unit/tracekit/opera.test.ts | 149 +++++- .../test/unit/tracekit/react-native.test.ts | 495 +++++++++++++++--- .../browser/test/unit/tracekit/react.test.ts | 36 +- .../browser/test/unit/tracekit/safari.test.ts | 219 ++++++-- 9 files changed, 1504 insertions(+), 210 deletions(-) diff --git a/packages/browser/src/stack-parsers.ts b/packages/browser/src/stack-parsers.ts index d77bf0d901a2..6419f3660e9d 100644 --- a/packages/browser/src/stack-parsers.ts +++ b/packages/browser/src/stack-parsers.ts @@ -13,9 +13,9 @@ const GECKO_PRIORITY = 50; function createFrame(filename: string, func: string, lineno?: number, colno?: number): StackFrame { const frame: StackFrame = { filename, + abs_path: filename, // As opposed to filename, abs_path is immutable (I can't control your actions but don't touch it!) function: func, - // All browser frames are considered in_app - in_app: true, + in_app: true, // All browser frames are considered in_app }; if (lineno !== undefined) { diff --git a/packages/browser/test/unit/tracekit/chromium.test.ts b/packages/browser/test/unit/tracekit/chromium.test.ts index b0df582ebeef..0d600cc28dd1 100644 --- a/packages/browser/test/unit/tracekit/chromium.test.ts +++ b/packages/browser/test/unit/tracekit/chromium.test.ts @@ -9,7 +9,7 @@ describe('Tracekit - Chrome Tests', () => { expect(ex).toEqual({ value: 'foo', type: 'bar', - stacktrace: { frames: [{ filename: 'native', function: 'Array.forEach', in_app: true }] }, + stacktrace: { frames: [{ filename: 'native', abs_path: 'native', function: 'Array.forEach', in_app: true }] }, }); }); @@ -33,10 +33,38 @@ describe('Tracekit - Chrome Tests', () => { type: 'foo', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: '?', lineno: 24, colno: 4, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 20, colno: 5, in_app: true }, - { filename: 'http://path/to/file.js', function: 'bar', lineno: 16, colno: 5, in_app: true }, - { filename: 'http://path/to/file.js', function: 'bar', lineno: 13, colno: 17, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 24, + colno: 4, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 20, + colno: 5, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 16, + colno: 5, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 13, + colno: 17, + in_app: true, + }, ], }, }); @@ -62,6 +90,7 @@ describe('Tracekit - Chrome Tests', () => { frames: [ { filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', function: 'I.e.fn.(anonymous function) [as index]', lineno: 10, colno: 3651, @@ -69,6 +98,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', function: 'HTMLButtonElement.onclick', lineno: 107, colno: 146, @@ -76,6 +106,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', function: 'dumpExceptionError', lineno: 41, colno: 27, @@ -108,6 +139,7 @@ describe('Tracekit - Chrome Tests', () => { frames: [ { filename: 'webpack:///./~/react-proxy/modules/createPrototypeProxy.js?', + abs_path: 'webpack:///./~/react-proxy/modules/createPrototypeProxy.js?', function: 'TESTTESTTEST.proxiedMethod', lineno: 44, colno: 30, @@ -115,6 +147,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'webpack:///./~/react-transform-catch-errors/lib/index.js?', + abs_path: 'webpack:///./~/react-transform-catch-errors/lib/index.js?', function: 'TESTTESTTEST.tryRender', lineno: 34, colno: 31, @@ -122,6 +155,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'webpack:///./src/components/test/test.jsx?', + abs_path: 'webpack:///./src/components/test/test.jsx?', function: 'TESTTESTTEST.render', lineno: 272, colno: 32, @@ -129,6 +163,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'webpack:///./src/components/test/test.jsx?', + abs_path: 'webpack:///./src/components/test/test.jsx?', function: 'TESTTESTTEST.eval', lineno: 295, colno: 108, @@ -159,11 +194,46 @@ describe('Tracekit - Chrome Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:8080/file.js', function: '?', lineno: 31, colno: 13, in_app: true }, - { filename: 'http://localhost:8080/file.js', function: 'Object.speak', lineno: 21, colno: 17, in_app: true }, - { filename: 'http://localhost:8080/file.js', function: 'eval', lineno: 21, colno: 17, in_app: true }, - { filename: 'http://localhost:8080/file.js', function: 'foo', lineno: 21, colno: 17, in_app: true }, - { filename: 'http://localhost:8080/file.js', function: 'baz', lineno: 21, colno: 17, in_app: true }, + { + filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', + function: '?', + lineno: 31, + colno: 13, + in_app: true, + }, + { + filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', + function: 'Object.speak', + lineno: 21, + colno: 17, + in_app: true, + }, + { + filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', + function: 'eval', + lineno: 21, + colno: 17, + in_app: true, + }, + { + filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', + function: 'foo', + lineno: 21, + colno: 17, + in_app: true, + }, + { + filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', + function: 'baz', + lineno: 21, + colno: 17, + in_app: true, + }, ], }, }); @@ -193,6 +263,7 @@ describe('Tracekit - Chrome Tests', () => { frames: [ { filename: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', + abs_path: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', function: 'n.handle', lineno: 7, colno: 2863, @@ -200,6 +271,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', + abs_path: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', function: 'n.fire', lineno: 7, colno: 3019, @@ -207,6 +279,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', + abs_path: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', function: '?', lineno: 1, colno: 6911, @@ -214,6 +287,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'blob:http%3A//localhost%3A8080/d4eefe0f-361a-4682-b217-76587d9f712a', + abs_path: 'blob:http%3A//localhost%3A8080/d4eefe0f-361a-4682-b217-76587d9f712a', function: '?', lineno: 15, colno: 10978, @@ -221,6 +295,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', + abs_path: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', function: 'Object.d [as add]', lineno: 31, colno: 30039, @@ -228,12 +303,13 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', + abs_path: 'blob:http%3A//localhost%3A8080/abfc40e9-4742-44ed-9dcd-af8f99a29379', function: 's', lineno: 31, colno: 29146, in_app: true, }, - { filename: 'native', function: 'Error', in_app: true }, + { filename: 'native', abs_path: 'native', function: 'Error', in_app: true }, ], }, }); @@ -256,6 +332,7 @@ describe('Tracekit - Chrome Tests', () => { frames: [ { filename: 'examplescheme://examplehost/cd351f7250857e22ceaa.worker.js', + abs_path: 'examplescheme://examplehost/cd351f7250857e22ceaa.worker.js', function: '?', lineno: 70179, colno: 15, @@ -284,10 +361,31 @@ describe('Tracekit - Chrome Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/test', function: '?', lineno: 24, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/test', function: 'foo', lineno: 19, colno: 19, in_app: true }, - { filename: '', function: 'Array.map', in_app: true }, - { filename: 'http://localhost:5000/test', function: 'fooIterator', lineno: 20, colno: 17, in_app: true }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: '?', + lineno: 24, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: 'foo', + lineno: 19, + colno: 19, + in_app: true, + }, + { filename: '', abs_path: '', function: 'Array.map', in_app: true }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: 'fooIterator', + lineno: 20, + colno: 17, + in_app: true, + }, ], }, }); @@ -317,16 +415,79 @@ describe('Tracekit - Chrome Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/', function: '?', lineno: 50, colno: 19, in_app: true }, - { filename: 'http://localhost:5000/', function: 'Foo.testMethod', lineno: 44, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/', function: 'aha', lineno: 39, colno: 5, in_app: true }, - { filename: 'http://localhost:5000/', function: 'eval', lineno: 37, colno: 5, in_app: true }, - { filename: 'http://localhost:5000/', function: 'test', lineno: 33, colno: 23, in_app: true }, - { filename: '', function: 'Array.map', in_app: true }, - { filename: 'http://localhost:5000/', function: '?', lineno: 34, colno: 17, in_app: true }, - { filename: 'http://localhost:5000/', function: 'Object.callback', lineno: 25, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/', function: 'callAnotherThing', lineno: 20, colno: 16, in_app: true }, - { filename: 'http://localhost:5000/', function: 'Object.aha', lineno: 19, colno: 13, in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: '?', + lineno: 50, + colno: 19, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'Foo.testMethod', + lineno: 44, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'aha', + lineno: 39, + colno: 5, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'eval', + lineno: 37, + colno: 5, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'test', + lineno: 33, + colno: 23, + in_app: true, + }, + { filename: '', abs_path: '', function: 'Array.map', in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: '?', + lineno: 34, + colno: 17, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'Object.callback', + lineno: 25, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'callAnotherThing', + lineno: 20, + colno: 16, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'Object.aha', + lineno: 19, + colno: 13, + in_app: true, + }, ], }, }); @@ -349,9 +510,30 @@ describe('Tracekit - Chrome Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/', function: 'test', lineno: 33, colno: 23, in_app: true }, - { filename: 'http://localhost:5000/', function: 'Object.callback', lineno: 25, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/', function: 'callAnotherThing', lineno: 20, colno: 16, in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'test', + lineno: 33, + colno: 23, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'Object.callback', + lineno: 25, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'callAnotherThing', + lineno: 20, + colno: 16, + in_app: true, + }, ], }, }); @@ -375,10 +557,31 @@ describe('Tracekit - Chrome Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/test', function: 'Global code', lineno: 24, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/test', function: 'foo', lineno: 19, colno: 9, in_app: true }, - { filename: 'native code', function: 'Array.prototype.map', in_app: true }, - { filename: 'http://localhost:5000/test', function: 'fooIterator', lineno: 20, colno: 11, in_app: true }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: 'Global code', + lineno: 24, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: 'foo', + lineno: 19, + colno: 9, + in_app: true, + }, + { filename: 'native code', abs_path: 'native code', function: 'Array.prototype.map', in_app: true }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: 'fooIterator', + lineno: 20, + colno: 11, + in_app: true, + }, ], }, }); @@ -408,22 +611,72 @@ describe('Tracekit - Chrome Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/', function: 'Anonymous function', lineno: 50, colno: 8, in_app: true }, { filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'Anonymous function', + lineno: 50, + colno: 8, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', function: 'Foo.prototype.testMethod', lineno: 44, colno: 7, in_app: true, }, - { filename: 'http://localhost:5000/', function: 'aha', lineno: 39, colno: 5, in_app: true }, - { filename: 'eval code', function: 'eval code', lineno: 1, colno: 1, in_app: true }, - { filename: 'http://localhost:5000/', function: 'test', lineno: 33, colno: 5, in_app: true }, - { filename: 'native code', function: 'Array.prototype.map', in_app: true }, - { filename: 'http://localhost:5000/', function: 'Anonymous function', lineno: 34, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/', function: 'callback', lineno: 25, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/', function: 'callAnotherThing', lineno: 18, colno: 6, in_app: true }, - { filename: 'http://localhost:5000/', function: 'aha', lineno: 19, colno: 7, in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'aha', + lineno: 39, + colno: 5, + in_app: true, + }, + { filename: 'eval code', abs_path: 'eval code', function: 'eval code', lineno: 1, colno: 1, in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'test', + lineno: 33, + colno: 5, + in_app: true, + }, + { filename: 'native code', abs_path: 'native code', function: 'Array.prototype.map', in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'Anonymous function', + lineno: 34, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'callback', + lineno: 25, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'callAnotherThing', + lineno: 18, + colno: 6, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'aha', + lineno: 19, + colno: 7, + in_app: true, + }, ], }, }); @@ -446,6 +699,7 @@ describe('Tracekit - Chrome Tests', () => { frames: [ { filename: 'C:\\Users\\user\\path\\to\\file.js', + abs_path: 'C:\\Users\\user\\path\\to\\file.js', function: 'TESTTESTTEST.someMethod', lineno: 295, colno: 108, @@ -478,6 +732,7 @@ describe('Tracekit - Chrome Tests', () => { frames: [ { filename: 'react-dom.development.js?f8c1', + abs_path: 'react-dom.development.js?f8c1', function: 'commitLayoutEffects', in_app: true, lineno: 23426, @@ -485,6 +740,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'react-dom.development.js?f8c1', + abs_path: 'react-dom.development.js?f8c1', function: 'commitLifeCycles', in_app: true, lineno: 20663, @@ -492,6 +748,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'genericDiscoverQuery.tsx?33f8', + abs_path: 'genericDiscoverQuery.tsx?33f8', function: '_GenericDiscoverQuery.componentDidMount', in_app: true, lineno: 152, @@ -499,6 +756,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'genericDiscoverQuery.tsx?33f8', + abs_path: 'genericDiscoverQuery.tsx?33f8', function: '_GenericDiscoverQuery.eval [as fetchData]', in_app: true, lineno: 256, @@ -506,6 +764,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'genericDiscoverQuery.tsx?33f8', + abs_path: 'genericDiscoverQuery.tsx?33f8', function: 'doDiscoverQuery', in_app: true, lineno: 328, @@ -513,6 +772,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'api.tsx', + abs_path: 'api.tsx', function: 'Client.requestPromise', in_app: true, lineno: 554, @@ -542,9 +802,10 @@ describe('Tracekit - Chrome Tests', () => { type: 'ChunkLoadError', stacktrace: { frames: [ - { filename: '', function: 'Array.reduce', in_app: true }, + { filename: '', abs_path: '', function: 'Array.reduce', in_app: true }, { filename: 'webpack/runtime/ensure chunk', + abs_path: 'webpack/runtime/ensure chunk', function: '?', in_app: true, lineno: 6, @@ -552,6 +813,7 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: 'webpack/runtime/jsonp chunk loading', + abs_path: 'webpack/runtime/jsonp chunk loading', function: 'key', in_app: true, lineno: 27, @@ -559,12 +821,15 @@ describe('Tracekit - Chrome Tests', () => { }, { filename: '/_static/dist/sentry/chunks/app_bootstrap_initializeLocale_tsx.abcdefg.js', + abs_path: '/_static/dist/sentry/chunks/app_bootstrap_initializeLocale_tsx.abcdefg.js', function: '?', in_app: true, }, { filename: 'https://s1.sentry-cdn.com/_static/dist/sentry/chunks/app_bootstrap_initializeLocale_tsx.abcdefg.js', + abs_path: + 'https://s1.sentry-cdn.com/_static/dist/sentry/chunks/app_bootstrap_initializeLocale_tsx.abcdefg.js', function: '?', in_app: true, }, @@ -592,8 +857,22 @@ describe('Tracekit - Chrome Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/', function: '?', lineno: 50, colno: 19, in_app: true }, - { filename: 'http://localhost:5000/', function: 'aha', lineno: 39, colno: 5, in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: '?', + lineno: 50, + colno: 19, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'aha', + lineno: 39, + colno: 5, + in_app: true, + }, ], }, }); diff --git a/packages/browser/test/unit/tracekit/firefox.test.ts b/packages/browser/test/unit/tracekit/firefox.test.ts index f75dd7ccf010..9a062d0a123b 100644 --- a/packages/browser/test/unit/tracekit/firefox.test.ts +++ b/packages/browser/test/unit/tracekit/firefox.test.ts @@ -26,13 +26,55 @@ describe('Tracekit - Firefox Tests', () => { type: 'TypeError', stacktrace: { frames: [ - { filename: 'http://127.0.0.1:8000/js/file.js', function: '?', lineno: 24, in_app: true }, - { filename: 'http://127.0.0.1:8000/js/file.js', function: 'foo', lineno: 20, in_app: true }, - { filename: 'http://127.0.0.1:8000/js/file.js', function: 'bar', lineno: 16, in_app: true }, - { filename: 'http://127.0.0.1:8000/js/file.js', function: 'bar', lineno: 13, in_app: true }, - { filename: 'http://127.0.0.1:8000/js/stacktrace.js', function: 'printStackTrace', lineno: 18, in_app: true }, - { filename: 'http://127.0.0.1:8000/js/stacktrace.js', function: '?', lineno: 31, in_app: true }, - { filename: 'http://127.0.0.1:8000/js/stacktrace.js', function: '?', lineno: 44, in_app: true }, + { + filename: 'http://127.0.0.1:8000/js/file.js', + abs_path: 'http://127.0.0.1:8000/js/file.js', + function: '?', + lineno: 24, + in_app: true, + }, + { + filename: 'http://127.0.0.1:8000/js/file.js', + abs_path: 'http://127.0.0.1:8000/js/file.js', + function: 'foo', + lineno: 20, + in_app: true, + }, + { + filename: 'http://127.0.0.1:8000/js/file.js', + abs_path: 'http://127.0.0.1:8000/js/file.js', + function: 'bar', + lineno: 16, + in_app: true, + }, + { + filename: 'http://127.0.0.1:8000/js/file.js', + abs_path: 'http://127.0.0.1:8000/js/file.js', + function: 'bar', + lineno: 13, + in_app: true, + }, + { + filename: 'http://127.0.0.1:8000/js/stacktrace.js', + abs_path: 'http://127.0.0.1:8000/js/stacktrace.js', + function: 'printStackTrace', + lineno: 18, + in_app: true, + }, + { + filename: 'http://127.0.0.1:8000/js/stacktrace.js', + abs_path: 'http://127.0.0.1:8000/js/stacktrace.js', + function: '?', + lineno: 31, + in_app: true, + }, + { + filename: 'http://127.0.0.1:8000/js/stacktrace.js', + abs_path: 'http://127.0.0.1:8000/js/stacktrace.js', + function: '?', + lineno: 44, + in_app: true, + }, ], }, }); @@ -62,13 +104,55 @@ describe('Tracekit - Firefox Tests', () => { type: 'foo', stacktrace: { frames: [ - { filename: 'file:///G:/js/file.js', function: '?', lineno: 24, in_app: true }, - { filename: 'file:///G:/js/file.js', function: 'foo', lineno: 20, in_app: true }, - { filename: 'file:///G:/js/file.js', function: 'bar', lineno: 16, in_app: true }, - { filename: 'file:///G:/js/file.js', function: 'bar', lineno: 13, in_app: true }, - { filename: 'file:///G:/js/stacktrace.js', function: 'printStackTrace', lineno: 18, in_app: true }, - { filename: 'file:///G:/js/stacktrace.js', function: '?', lineno: 31, in_app: true }, - { filename: 'file:///G:/js/stacktrace.js', function: '?', lineno: 44, in_app: true }, + { + filename: 'file:///G:/js/file.js', + abs_path: 'file:///G:/js/file.js', + function: '?', + lineno: 24, + in_app: true, + }, + { + filename: 'file:///G:/js/file.js', + abs_path: 'file:///G:/js/file.js', + function: 'foo', + lineno: 20, + in_app: true, + }, + { + filename: 'file:///G:/js/file.js', + abs_path: 'file:///G:/js/file.js', + function: 'bar', + lineno: 16, + in_app: true, + }, + { + filename: 'file:///G:/js/file.js', + abs_path: 'file:///G:/js/file.js', + function: 'bar', + lineno: 13, + in_app: true, + }, + { + filename: 'file:///G:/js/stacktrace.js', + abs_path: 'file:///G:/js/stacktrace.js', + function: 'printStackTrace', + lineno: 18, + in_app: true, + }, + { + filename: 'file:///G:/js/stacktrace.js', + abs_path: 'file:///G:/js/stacktrace.js', + function: '?', + lineno: 31, + in_app: true, + }, + { + filename: 'file:///G:/js/stacktrace.js', + abs_path: 'file:///G:/js/stacktrace.js', + function: '?', + lineno: 44, + in_app: true, + }, ], }, }); @@ -94,9 +178,27 @@ describe('Tracekit - Firefox Tests', () => { type: 'foo', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: 'onclick', lineno: 1, in_app: true }, - { filename: 'http://path/to/file.js', function: 'dumpException3', lineno: 52, in_app: true }, - { filename: 'http://path/to/file.js', function: '?', lineno: 48, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'onclick', + lineno: 1, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'dumpException3', + lineno: 52, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 48, + in_app: true, + }, ], }, }); @@ -123,9 +225,30 @@ describe('Tracekit - Firefox Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: '.plugin/e.fn[c]/<', lineno: 1, colno: 1, in_app: true }, - { filename: 'http://path/to/file.js', function: 'bar', lineno: 1, colno: 1, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 41, colno: 13, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '.plugin/e.fn[c]/<', + lineno: 1, + colno: 1, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 1, + colno: 1, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 41, + colno: 13, + in_app: true, + }, ], }, }); @@ -158,11 +281,33 @@ describe('Tracekit - Firefox Tests', () => { type: 'NS_ERROR_FAILURE', stacktrace: { frames: [ - { filename: 'file:///path/to/index.html', function: '?', lineno: 23, colno: 1, in_app: true }, - { filename: 'file:///path/to/file.js', function: 'bar', lineno: 20, colno: 3, in_app: true }, - { filename: 'file:///path/to/file.js', function: 'App.prototype.foo', lineno: 15, colno: 2, in_app: true }, + { + filename: 'file:///path/to/index.html', + abs_path: 'file:///path/to/index.html', + function: '?', + lineno: 23, + colno: 1, + in_app: true, + }, + { + filename: 'file:///path/to/file.js', + abs_path: 'file:///path/to/file.js', + function: 'bar', + lineno: 20, + colno: 3, + in_app: true, + }, + { + filename: 'file:///path/to/file.js', + abs_path: 'file:///path/to/file.js', + function: 'App.prototype.foo', + lineno: 15, + colno: 2, + in_app: true, + }, { filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', function: '[2] { frames: [ { filename: 'resource://path/data/content/bundle.js', + abs_path: 'resource://path/data/content/bundle.js', function: 'wrapped', lineno: 7270, colno: 25, @@ -202,6 +348,7 @@ describe('Tracekit - Firefox Tests', () => { }, { filename: 'resource://path/data/content/vendor.bundle.js', + abs_path: 'resource://path/data/content/vendor.bundle.js', function: 'dispatchEvent', lineno: 18, colno: 23028, @@ -209,6 +356,7 @@ describe('Tracekit - Firefox Tests', () => { }, { filename: 'resource://path/data/content/bundle.js', + abs_path: 'resource://path/data/content/bundle.js', function: 'render', lineno: 5529, colno: 16, @@ -241,11 +389,43 @@ describe('Tracekit - Firefox Tests', () => { type: 'foo', stacktrace: { frames: [ - { filename: 'http://localhost:8080/file.js', function: '?', lineno: 33, colno: 9, in_app: true }, - { filename: 'http://localhost:8080/file.js', function: 'speak', lineno: 26, colno: 17, in_app: true }, - { filename: 'http://localhost:8080/file.js', function: 'eval', lineno: 26, in_app: true }, - { filename: 'http://localhost:8080/file.js', function: 'foo', lineno: 26, in_app: true }, - { filename: 'http://localhost:8080/file.js', function: 'baz', lineno: 26, in_app: true }, + { + filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', + function: '?', + lineno: 33, + colno: 9, + in_app: true, + }, + { + filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', + function: 'speak', + lineno: 26, + colno: 17, + in_app: true, + }, + { + filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', + function: 'eval', + lineno: 26, + in_app: true, + }, + { + filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', + function: 'foo', + lineno: 26, + in_app: true, + }, + { + filename: 'http://localhost:8080/file.js', + abs_path: 'http://localhost:8080/file.js', + function: 'baz', + lineno: 26, + in_app: true, + }, ], }, }); @@ -267,9 +447,30 @@ describe('Tracekit - Firefox Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/test', function: '?', lineno: 24, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/test', function: 'foo', lineno: 19, colno: 19, in_app: true }, - { filename: 'http://localhost:5000/test', function: 'fooIterator', lineno: 20, colno: 17, in_app: true }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: '?', + lineno: 24, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: 'foo', + lineno: 19, + colno: 19, + in_app: true, + }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: 'fooIterator', + lineno: 20, + colno: 17, + in_app: true, + }, ], }, }); @@ -297,15 +498,77 @@ describe('Tracekit - Firefox Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/', function: '?', lineno: 50, colno: 19, in_app: true }, - { filename: 'http://localhost:5000/', function: 'testMethod', lineno: 44, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/', function: 'aha', lineno: 39, colno: 5, in_app: true }, - { filename: 'http://localhost:5000/', function: 'eval', lineno: 39, in_app: true }, - { filename: 'http://localhost:5000/', function: 'test', lineno: 33, colno: 23, in_app: true }, - { filename: 'http://localhost:5000/', function: 'test/<', lineno: 34, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/', function: 'callback', lineno: 25, colno: 7, in_app: true }, - { filename: 'http://localhost:5000/', function: 'callAnotherThing', lineno: 20, colno: 15, in_app: true }, - { filename: 'http://localhost:5000/', function: 'aha', lineno: 19, colno: 13, in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: '?', + lineno: 50, + colno: 19, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'testMethod', + lineno: 44, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'aha', + lineno: 39, + colno: 5, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'eval', + lineno: 39, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'test', + lineno: 33, + colno: 23, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'test/<', + lineno: 34, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'callback', + lineno: 25, + colno: 7, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'callAnotherThing', + lineno: 20, + colno: 15, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'aha', + lineno: 19, + colno: 13, + in_app: true, + }, ], }, }); @@ -332,6 +595,7 @@ describe('Tracekit - Firefox Tests', () => { { colno: 1018410, filename: 'https://www.random_website.com/main.4a4119c3cdfd10266d84.js', + abs_path: 'https://www.random_website.com/main.4a4119c3cdfd10266d84.js', function: 'handleProfileResult', in_app: true, lineno: 146, @@ -339,6 +603,7 @@ describe('Tracekit - Firefox Tests', () => { { colno: 333807, filename: 'https://www.random_website.com/vendor.d1cae9cfc9917df88de7.js', + abs_path: 'https://www.random_website.com/vendor.d1cae9cfc9917df88de7.js', function: 'detectChanges', in_app: true, lineno: 1, @@ -346,6 +611,7 @@ describe('Tracekit - Firefox Tests', () => { { colno: 296021, filename: 'https://www.random_website.com/vendor.d1cae9cfc9917df88de7.js', + abs_path: 'https://www.random_website.com/vendor.d1cae9cfc9917df88de7.js', function: 'us', in_app: true, lineno: 1, diff --git a/packages/browser/test/unit/tracekit/ie.test.ts b/packages/browser/test/unit/tracekit/ie.test.ts index 544542b0dcaf..6417a7043c32 100644 --- a/packages/browser/test/unit/tracekit/ie.test.ts +++ b/packages/browser/test/unit/tracekit/ie.test.ts @@ -23,9 +23,30 @@ describe('Tracekit - IE Tests', () => { type: 'foo', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: 'bar', lineno: 82, colno: 1, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 46, colno: 9, in_app: true }, - { filename: 'http://path/to/file.js', function: 'Anonymous function', lineno: 48, colno: 13, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 82, + colno: 1, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 46, + colno: 9, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'Anonymous function', + lineno: 48, + colno: 13, + in_app: true, + }, ], }, }); @@ -52,9 +73,30 @@ describe('Tracekit - IE Tests', () => { type: 'TypeError', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: 'bar', lineno: 108, colno: 1, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 45, colno: 13, in_app: true }, - { filename: 'http://path/to/file.js', function: 'Anonymous function', lineno: 47, colno: 21, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 108, + colno: 1, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 45, + colno: 13, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'Anonymous function', + lineno: 47, + colno: 21, + in_app: true, + }, ], }, }); @@ -80,9 +122,23 @@ describe('Tracekit - IE Tests', () => { type: 'ReferenceError', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: 'bar', lineno: 109, colno: 1, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 58, colno: 17, in_app: true }, - { filename: 'eval code', function: 'eval code', lineno: 1, colno: 1, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 109, + colno: 1, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 58, + colno: 17, + in_app: true, + }, + { filename: 'eval code', abs_path: 'eval code', function: 'eval code', lineno: 1, colno: 1, in_app: true }, ], }, }); diff --git a/packages/browser/test/unit/tracekit/misc.test.ts b/packages/browser/test/unit/tracekit/misc.test.ts index e9db457ea196..dbe718c9410f 100644 --- a/packages/browser/test/unit/tracekit/misc.test.ts +++ b/packages/browser/test/unit/tracekit/misc.test.ts @@ -19,9 +19,27 @@ describe('Tracekit - Misc Tests', () => { type: 'foo', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: '?', lineno: 4287, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 4283, in_app: true }, - { filename: 'file:///path/to/file.js', function: '?', lineno: 878, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 4287, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 4283, + in_app: true, + }, + { + filename: 'file:///path/to/file.js', + abs_path: 'file:///path/to/file.js', + function: '?', + lineno: 878, + in_app: true, + }, ], }, }); diff --git a/packages/browser/test/unit/tracekit/opera.test.ts b/packages/browser/test/unit/tracekit/opera.test.ts index e86855dc172a..08d7119fb5bb 100644 --- a/packages/browser/test/unit/tracekit/opera.test.ts +++ b/packages/browser/test/unit/tracekit/opera.test.ts @@ -37,13 +37,55 @@ describe('Tracekit - Opera Tests', () => { type: 'foo', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: '?', lineno: 15, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 11, in_app: true }, - { filename: 'http://path/to/file.js', function: 'bar', lineno: 7, in_app: true }, - { filename: 'http://path/to/file.js', function: 'bar', lineno: 4, in_app: true }, - { filename: 'http://path/to/file.js', function: 'printStackTrace', lineno: 18, in_app: true }, - { filename: 'http://path/to/file.js', function: '?', lineno: 27, in_app: true }, - { filename: 'http://path/to/file.js', function: '?', lineno: 42, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 15, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 11, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 7, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 4, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'printStackTrace', + lineno: 18, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 27, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 42, + in_app: true, + }, ], }, }); @@ -83,13 +125,62 @@ describe('Tracekit - Opera Tests', () => { type: 'foo', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: '?', lineno: 15, colno: 3, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 11, colno: 4, in_app: true }, - { filename: 'http://path/to/file.js', function: 'bar', lineno: 7, colno: 4, in_app: true }, - { filename: 'http://path/to/file.js', function: 'bar', lineno: 4, colno: 5, in_app: true }, - { filename: 'http://path/to/file.js', function: 'printStackTrace', lineno: 18, colno: 4, in_app: true }, - { filename: 'http://path/to/file.js', function: 'run', lineno: 27, colno: 8, in_app: true }, - { filename: 'http://path/to/file.js', function: 'createException', lineno: 42, colno: 12, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 15, + colno: 3, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 11, + colno: 4, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 7, + colno: 4, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 4, + colno: 5, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'printStackTrace', + lineno: 18, + colno: 4, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'run', + lineno: 27, + colno: 8, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'createException', + lineno: 42, + colno: 12, + in_app: true, + }, ], }, }); @@ -122,6 +213,7 @@ describe('Tracekit - Opera Tests', () => { frames: [ { filename: 'http://localhost:8000/ExceptionLab.html', + abs_path: 'http://localhost:8000/ExceptionLab.html', function: '', lineno: 1, colno: 0, @@ -129,6 +221,7 @@ describe('Tracekit - Opera Tests', () => { }, { filename: 'http://localhost:8000/ExceptionLab.html', + abs_path: 'http://localhost:8000/ExceptionLab.html', function: 'dumpException3', lineno: 46, colno: 8, @@ -136,6 +229,7 @@ describe('Tracekit - Opera Tests', () => { }, { filename: 'http://localhost:8000/ExceptionLab.html', + abs_path: 'http://localhost:8000/ExceptionLab.html', function: '', lineno: 48, colno: 12, @@ -164,9 +258,30 @@ describe('Tracekit - Opera Tests', () => { type: 'TypeError', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: 'bar', lineno: 108, colno: 168, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 52, colno: 15, in_app: true }, - { filename: 'http://path/to/file.js', function: '?', lineno: 47, colno: 22, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 108, + colno: 168, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 52, + colno: 15, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 47, + colno: 22, + in_app: true, + }, ], }, }); diff --git a/packages/browser/test/unit/tracekit/react-native.test.ts b/packages/browser/test/unit/tracekit/react-native.test.ts index 9a74e46007b1..7615a27c4afe 100644 --- a/packages/browser/test/unit/tracekit/react-native.test.ts +++ b/packages/browser/test/unit/tracekit/react-native.test.ts @@ -22,18 +22,41 @@ describe('Tracekit - React Native Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'index.android.bundle', function: 'P', lineno: 93, colno: 714, in_app: true }, - { filename: 'index.android.bundle', function: 'Object.y', lineno: 93, colno: 571, in_app: true }, { filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'P', + lineno: 93, + colno: 714, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'Object.y', + lineno: 93, + colno: 571, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', function: 's.touchableHandleResponderRelease', lineno: 198, colno: 5615, in_app: true, }, - { filename: 'index.android.bundle', function: 's._receiveSignal', lineno: 198, colno: 8309, in_app: true }, { filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 's._receiveSignal', + lineno: 198, + colno: 8309, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', function: 's._performSideEffectsForTransition', lineno: 198, colno: 9608, @@ -41,12 +64,20 @@ describe('Tracekit - React Native Tests', () => { }, { filename: 'index.android.bundle', + abs_path: 'index.android.bundle', function: 's.touchableHandlePress', lineno: 214, colno: 2048, in_app: true, }, - { filename: 'index.android.bundle', function: 'Object.onPress', lineno: 2342, colno: 3773, in_app: true }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'Object.onPress', + lineno: 2342, + colno: 3773, + in_app: true, + }, ], }, }); @@ -69,10 +100,12 @@ describe('Tracekit - React Native Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: '[native code]', function: 'forEach', in_app: true }, + { filename: '[native code]', abs_path: '[native code]', function: 'forEach', in_app: true }, { filename: '/data/user/0/com.sentrytest/files/.expo-internal/bundle-613EDD44F3305B9D75D4679663900F2BCDDDC326F247CA3202A3A4219FD412D3', + abs_path: + '/data/user/0/com.sentrytest/files/.expo-internal/bundle-613EDD44F3305B9D75D4679663900F2BCDDDC326F247CA3202A3A4219FD412D3', function: 'p', lineno: 96, colno: 385, @@ -81,6 +114,8 @@ describe('Tracekit - React Native Tests', () => { { filename: '/data/user/0/com.sentrytest/files/.expo-internal/bundle-613EDD44F3305B9D75D4679663900F2BCDDDC326F247CA3202A3A4219FD412D3', + abs_path: + '/data/user/0/com.sentrytest/files/.expo-internal/bundle-613EDD44F3305B9D75D4679663900F2BCDDDC326F247CA3202A3A4219FD412D3', function: 'onResponderRelease', lineno: 221, colno: 5666, @@ -89,6 +124,8 @@ describe('Tracekit - React Native Tests', () => { { filename: '/data/user/0/com.sentrytest/files/.expo-internal/bundle-613EDD44F3305B9D75D4679663900F2BCDDDC326F247CA3202A3A4219FD412D3', + abs_path: + '/data/user/0/com.sentrytest/files/.expo-internal/bundle-613EDD44F3305B9D75D4679663900F2BCDDDC326F247CA3202A3A4219FD412D3', function: 'value', lineno: 221, colno: 7656, @@ -97,6 +134,8 @@ describe('Tracekit - React Native Tests', () => { { filename: '/data/user/0/com.sentrytest/files/.expo-internal/bundle-613EDD44F3305B9D75D4679663900F2BCDDDC326F247CA3202A3A4219FD412D3', + abs_path: + '/data/user/0/com.sentrytest/files/.expo-internal/bundle-613EDD44F3305B9D75D4679663900F2BCDDDC326F247CA3202A3A4219FD412D3', function: 'onPress', lineno: 595, colno: 658, @@ -133,6 +172,8 @@ describe('Tracekit - React Native Tests', () => { { filename: '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativeBaseComponent.js', + abs_path: + '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/native/ReactNativeBaseComponent.js', function: 'this', lineno: 74, colno: 41, @@ -141,6 +182,8 @@ describe('Tracekit - React Native Tests', () => { { filename: '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactMultiChild.js', + abs_path: + '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactMultiChild.js', function: 'children', lineno: 264, colno: 10, @@ -149,6 +192,8 @@ describe('Tracekit - React Native Tests', () => { { filename: '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactReconciler.js', + abs_path: + '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactReconciler.js', function: 'child', lineno: 68, colno: 25, @@ -157,6 +202,8 @@ describe('Tracekit - React Native Tests', () => { { filename: '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js', + abs_path: + '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js', function: '_currentElement', lineno: 346, colno: 40, @@ -165,6 +212,8 @@ describe('Tracekit - React Native Tests', () => { { filename: '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js', + abs_path: + '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js', function: 'renderedElement', lineno: 484, colno: 29, @@ -173,6 +222,8 @@ describe('Tracekit - React Native Tests', () => { { filename: '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js', + abs_path: + '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js', function: '_renderValidatedComponent', lineno: 1075, colno: 15, @@ -181,6 +232,8 @@ describe('Tracekit - React Native Tests', () => { { filename: '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js', + abs_path: + '/home/username/sample-workspace/sampleapp.collect.react/node_modules/react-native/Libraries/Renderer/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js', function: '_renderValidatedComponentWithoutOwnerOrContext', lineno: 1050, colno: 29, @@ -188,6 +241,7 @@ describe('Tracekit - React Native Tests', () => { }, { filename: '/home/username/sample-workspace/sampleapp.collect.react/src/components/GpsMonitorScene.js', + abs_path: '/home/username/sample-workspace/sampleapp.collect.react/src/components/GpsMonitorScene.js', function: 'render', lineno: 78, colno: 24, @@ -249,73 +303,260 @@ describe('Tracekit - React Native Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: '[native code]', function: '?', in_app: true }, - { filename: 'index.android.bundle', function: 'value', lineno: 29, colno: 927, in_app: true }, - { filename: 'index.android.bundle', function: 'value', lineno: 29, colno: 2417, in_app: true }, - { filename: 'index.android.bundle', function: '?', lineno: 29, colno: 955, in_app: true }, - { filename: 'index.android.bundle', function: 'value', lineno: 29, colno: 3016, in_app: true }, - { filename: 'index.android.bundle', function: 'receiveTouches', lineno: 156, colno: 918, in_app: true }, + { filename: '[native code]', abs_path: '[native code]', function: '?', in_app: true }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'value', + lineno: 29, + colno: 927, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'value', + lineno: 29, + colno: 2417, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: '?', + lineno: 29, + colno: 955, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'value', + lineno: 29, + colno: 3016, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'receiveTouches', + lineno: 156, + colno: 918, + in_app: true, + }, { filename: 'index.android.bundle', + abs_path: 'index.android.bundle', function: '_receiveRootNodeIDEvent', lineno: 156, colno: 544, in_app: true, }, - { filename: 'index.android.bundle', function: 'u', lineno: 93, colno: 150, in_app: true }, - { filename: 'index.android.bundle', function: 'i', lineno: 93, colno: 90, in_app: true }, - { filename: 'index.android.bundle', function: 'i', lineno: 176, colno: 358, in_app: true }, - { filename: 'index.android.bundle', function: 'batchedUpdates', lineno: 188, colno: 464, in_app: true }, - { filename: 'index.android.bundle', function: 'perform', lineno: 177, colno: 596, in_app: true }, - { filename: 'index.android.bundle', function: 'c', lineno: 93, colno: 60, in_app: true }, - { filename: 'index.android.bundle', function: 'a', lineno: 93, colno: 276, in_app: true }, - { filename: 'index.android.bundle', function: '?', lineno: 156, colno: 572, in_app: true }, - { filename: 'index.android.bundle', function: 'handleTopLevel', lineno: 157, colno: 174, in_app: true }, - { filename: 'index.android.bundle', function: 's', lineno: 157, colno: 88, in_app: true }, - { filename: 'index.android.bundle', function: 'processEventQueue', lineno: 146, colno: 1432, in_app: true }, - { filename: 'index.android.bundle', function: 'i', lineno: 149, colno: 80, in_app: true }, - { filename: '[native code]', function: 'forEach', in_app: true }, - { filename: 'index.android.bundle', function: 'g', lineno: 146, colno: 604, in_app: true }, - { filename: 'index.android.bundle', function: 'v', lineno: 146, colno: 501, in_app: true }, - { filename: 'index.android.bundle', function: 'a', lineno: 95, colno: 567, in_app: true }, - { filename: 'index.android.bundle', function: 'c', lineno: 95, colno: 365, in_app: true }, { filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'u', + lineno: 93, + colno: 150, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'i', + lineno: 93, + colno: 90, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'i', + lineno: 176, + colno: 358, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'batchedUpdates', + lineno: 188, + colno: 464, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'perform', + lineno: 177, + colno: 596, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'c', + lineno: 93, + colno: 60, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'a', + lineno: 93, + colno: 276, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: '?', + lineno: 156, + colno: 572, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'handleTopLevel', + lineno: 157, + colno: 174, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 's', + lineno: 157, + colno: 88, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'processEventQueue', + lineno: 146, + colno: 1432, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'i', + lineno: 149, + colno: 80, + in_app: true, + }, + { filename: '[native code]', abs_path: '[native code]', function: 'forEach', in_app: true }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'g', + lineno: 146, + colno: 604, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'v', + lineno: 146, + colno: 501, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'a', + lineno: 95, + colno: 567, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'c', + lineno: 95, + colno: 365, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', function: 'invokeGuardedCallbackAndCatchFirstError', lineno: 79, colno: 580, in_app: true, }, - { filename: 'index.android.bundle', function: 'invokeGuardedCallback', lineno: 79, colno: 459, in_app: true }, - { filename: 'index.android.bundle', function: 'u', lineno: 79, colno: 142, in_app: true }, - { filename: '[native code]', function: '?', in_app: true }, { filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'invokeGuardedCallback', + lineno: 79, + colno: 459, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'u', + lineno: 79, + colno: 142, + in_app: true, + }, + { filename: '[native code]', abs_path: '[native code]', function: '?', in_app: true }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', function: 'touchableHandleResponderRelease', lineno: 252, colno: 4735, in_app: true, }, - { filename: '[native code]', function: '?', in_app: true }, - { filename: 'index.android.bundle', function: '_receiveSignal', lineno: 252, colno: 7291, in_app: true }, - { filename: '[native code]', function: '?', in_app: true }, + { filename: '[native code]', abs_path: '[native code]', function: '?', in_app: true }, { filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: '_receiveSignal', + lineno: 252, + colno: 7291, + in_app: true, + }, + { filename: '[native code]', abs_path: '[native code]', function: '?', in_app: true }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', function: '_performSideEffectsForTransition', lineno: 252, colno: 8508, in_app: true, }, - { filename: '[native code]', function: '?', in_app: true }, + { filename: '[native code]', abs_path: '[native code]', function: '?', in_app: true }, { filename: 'index.android.bundle', + abs_path: 'index.android.bundle', function: 'touchableHandlePress', lineno: 258, colno: 1497, in_app: true, }, - { filename: 'index.android.bundle', function: 'onPress', lineno: 12, colno: 2336, in_app: true }, - { filename: 'index.android.bundle', function: 'value', lineno: 12, colno: 1917, in_app: true }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'onPress', + lineno: 12, + colno: 2336, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'value', + lineno: 12, + colno: 1917, + in_app: true, + }, ], }, }); @@ -360,38 +601,168 @@ describe('Tracekit - React Native Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'index.android.bundle', function: 'value', lineno: 1, colno: 31561, in_app: true }, - { filename: 'index.android.bundle', function: 'value', lineno: 1, colno: 32776, in_app: true }, - { filename: 'index.android.bundle', function: 'anonymous', lineno: 1, colno: 31603, in_app: true }, - { filename: 'index.android.bundle', function: 'value', lineno: 1, colno: 33176, in_app: true }, - { filename: 'native', function: 'apply', in_app: true }, - { filename: 'index.android.bundle', function: 'receiveTouches', lineno: 1, colno: 122512, in_app: true }, - { filename: 'index.android.bundle', function: 'Ue', lineno: 1, colno: 77571, in_app: true }, - { filename: 'index.android.bundle', function: 'Ne', lineno: 1, colno: 77238, in_app: true }, - { filename: 'index.android.bundle', function: '_e', lineno: 1, colno: 127755, in_app: true }, - { filename: 'index.android.bundle', function: 'anonymous', lineno: 1, colno: 77747, in_app: true }, - { filename: 'index.android.bundle', function: 'z', lineno: 1, colno: 74642, in_app: true }, - { filename: 'native', function: 'forEach', in_app: true }, - { filename: 'index.android.bundle', function: 'A', lineno: 1, colno: 74709, in_app: true }, - { filename: 'index.android.bundle', function: 'N', lineno: 1, colno: 74267, in_app: true }, - { filename: 'index.android.bundle', function: 'C', lineno: 1, colno: 74126, in_app: true }, - { filename: 'native', function: 'apply', in_app: true }, - { filename: 'index.android.bundle', function: 'k', lineno: 1, colno: 74094, in_app: true }, - { filename: 'native', function: 'apply', in_app: true }, - { filename: 'index.android.bundle', function: 'b', lineno: 1, colno: 74037, in_app: true }, - { filename: 'native', function: 'apply', in_app: true }, - { filename: 'native', function: 'onResponderRelease', in_app: true }, - { filename: 'native', function: 'touchableHandleResponderRelease', in_app: true }, - { filename: 'native', function: '_receiveSignal', in_app: true }, { filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'value', + lineno: 1, + colno: 31561, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'value', + lineno: 1, + colno: 32776, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'anonymous', + lineno: 1, + colno: 31603, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'value', + lineno: 1, + colno: 33176, + in_app: true, + }, + { + filename: 'native', + abs_path: 'native', + function: 'apply', + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'receiveTouches', + lineno: 1, + colno: 122512, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'Ue', + lineno: 1, + colno: 77571, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'Ne', + lineno: 1, + colno: 77238, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: '_e', + lineno: 1, + colno: 127755, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'anonymous', + lineno: 1, + colno: 77747, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'z', + lineno: 1, + colno: 74642, + in_app: true, + }, + { + filename: 'native', + abs_path: 'native', + function: 'forEach', + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'A', + lineno: 1, + colno: 74709, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'N', + lineno: 1, + colno: 74267, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'C', + lineno: 1, + colno: 74126, + in_app: true, + }, + { filename: 'native', abs_path: 'native', function: 'apply', in_app: true }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'k', + lineno: 1, + colno: 74094, + in_app: true, + }, + { filename: 'native', abs_path: 'native', function: 'apply', in_app: true }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'b', + lineno: 1, + colno: 74037, + in_app: true, + }, + { filename: 'native', abs_path: 'native', function: 'apply', in_app: true }, + { filename: 'native', abs_path: 'native', function: 'onResponderRelease', in_app: true }, + { filename: 'native', abs_path: 'native', function: 'touchableHandleResponderRelease', in_app: true }, + { filename: 'native', abs_path: 'native', function: '_receiveSignal', in_app: true }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', function: '_performSideEffectsForTransition', lineno: 1, colno: 230843, in_app: true, }, - { filename: 'index.android.bundle', function: 'anonymous', lineno: 1, colno: 224280, in_app: true }, - { filename: 'index.android.bundle', function: 'onPress', lineno: 1, colno: 452701, in_app: true }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'anonymous', + lineno: 1, + colno: 224280, + in_app: true, + }, + { + filename: 'index.android.bundle', + abs_path: 'index.android.bundle', + function: 'onPress', + lineno: 1, + colno: 452701, + in_app: true, + }, ], }, }); diff --git a/packages/browser/test/unit/tracekit/react.test.ts b/packages/browser/test/unit/tracekit/react.test.ts index d949a4dee0eb..5ebaa602ed06 100644 --- a/packages/browser/test/unit/tracekit/react.test.ts +++ b/packages/browser/test/unit/tracekit/react.test.ts @@ -23,9 +23,17 @@ describe('Tracekit - React Tests', () => { type: 'Invariant Violation', stacktrace: { frames: [ - { filename: 'http://localhost:5000/', function: 'f', lineno: 1, colno: 980, in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'f', + lineno: 1, + colno: 980, + in_app: true, + }, { filename: 'http://localhost:5000/static/js/foo.chunk.js', + abs_path: 'http://localhost:5000/static/js/foo.chunk.js', function: 'ho', lineno: 1, colno: 68735, @@ -33,6 +41,7 @@ describe('Tracekit - React Tests', () => { }, { filename: 'http://localhost:5000/static/js/foo.chunk.js', + abs_path: 'http://localhost:5000/static/js/foo.chunk.js', function: 'a', lineno: 1, colno: 21841, @@ -40,6 +49,7 @@ describe('Tracekit - React Tests', () => { }, { filename: 'http://localhost:5000/static/js/foo.chunk.js', + abs_path: 'http://localhost:5000/static/js/foo.chunk.js', function: '?', lineno: 1, colno: 21738, @@ -70,9 +80,17 @@ describe('Tracekit - React Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/', function: 'f', lineno: 1, colno: 980, in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'f', + lineno: 1, + colno: 980, + in_app: true, + }, { filename: 'http://localhost:5000/static/js/foo.chunk.js', + abs_path: 'http://localhost:5000/static/js/foo.chunk.js', function: 'ho', lineno: 1, colno: 68735, @@ -80,6 +98,7 @@ describe('Tracekit - React Tests', () => { }, { filename: 'http://localhost:5000/static/js/foo.chunk.js', + abs_path: 'http://localhost:5000/static/js/foo.chunk.js', function: 'a', lineno: 1, colno: 21841, @@ -87,6 +106,7 @@ describe('Tracekit - React Tests', () => { }, { filename: 'http://localhost:5000/static/js/foo.chunk.js', + abs_path: 'http://localhost:5000/static/js/foo.chunk.js', function: '?', lineno: 1, colno: 21738, @@ -118,9 +138,17 @@ describe('Tracekit - React Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/', function: 'f', lineno: 1, colno: 980, in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'f', + lineno: 1, + colno: 980, + in_app: true, + }, { filename: 'http://localhost:5000/static/js/foo.chunk.js', + abs_path: 'http://localhost:5000/static/js/foo.chunk.js', function: 'ho', lineno: 1, colno: 68735, @@ -128,6 +156,7 @@ describe('Tracekit - React Tests', () => { }, { filename: 'http://localhost:5000/static/js/foo.chunk.js', + abs_path: 'http://localhost:5000/static/js/foo.chunk.js', function: 'a', lineno: 1, colno: 21841, @@ -135,6 +164,7 @@ describe('Tracekit - React Tests', () => { }, { filename: 'http://localhost:5000/static/js/foo.chunk.js', + abs_path: 'http://localhost:5000/static/js/foo.chunk.js', function: '?', lineno: 1, colno: 21738, diff --git a/packages/browser/test/unit/tracekit/safari.test.ts b/packages/browser/test/unit/tracekit/safari.test.ts index 657ffc7daecc..c2ff37cdfe4c 100644 --- a/packages/browser/test/unit/tracekit/safari.test.ts +++ b/packages/browser/test/unit/tracekit/safari.test.ts @@ -22,10 +22,28 @@ describe('Tracekit - Safari Tests', () => { type: 'foo', stacktrace: { frames: [ - { filename: '[native code]', function: '?', in_app: true }, - { filename: 'http://path/to/file.js', function: 'onclick', lineno: 82, in_app: true }, - { filename: 'http://path/to/file.js', function: 'dumpException3', lineno: 52, in_app: true }, - { filename: 'http://path/to/file.js', function: '?', lineno: 48, in_app: true }, + { filename: '[native code]', abs_path: '[native code]', function: '?', in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'onclick', + lineno: 82, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'dumpException3', + lineno: 52, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 48, + in_app: true, + }, ], }, }); @@ -48,9 +66,30 @@ describe('Tracekit - Safari Tests', () => { type: 'TypeError', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: 'bar', lineno: 108, colno: 107, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 52, colno: 15, in_app: true }, - { filename: 'http://path/to/file.js', function: '?', lineno: 48, colno: 22, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 108, + colno: 107, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 52, + colno: 15, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 48, + colno: 22, + in_app: true, + }, ], }, }); @@ -74,9 +113,30 @@ describe('Tracekit - Safari Tests', () => { type: 'TypeError', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: 'bar', lineno: 108, colno: 23, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 52, colno: 15, in_app: true }, - { filename: 'http://path/to/file.js', function: '?', lineno: 47, colno: 22, in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 108, + colno: 23, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 52, + colno: 15, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: '?', + lineno: 47, + colno: 22, + in_app: true, + }, ], }, }); @@ -104,9 +164,23 @@ describe('Tracekit - Safari Tests', () => { type: 'ReferenceError', stacktrace: { frames: [ - { filename: 'http://path/to/file.js', function: 'bar', lineno: 109, colno: 91, in_app: true }, - { filename: 'http://path/to/file.js', function: 'foo', lineno: 58, colno: 21, in_app: true }, - { filename: '[native code]', function: 'eval', in_app: true }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'bar', + lineno: 109, + colno: 91, + in_app: true, + }, + { + filename: 'http://path/to/file.js', + abs_path: 'http://path/to/file.js', + function: 'foo', + lineno: 58, + colno: 21, + in_app: true, + }, + { filename: '[native code]', abs_path: '[native code]', function: 'eval', in_app: true }, ], }, }); @@ -131,6 +205,7 @@ describe('Tracekit - Safari Tests', () => { frames: [ { filename: 'safari-extension://3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/topee-content.js', + abs_path: 'safari-extension://3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/topee-content.js', function: '?', lineno: 3313, colno: 26, @@ -138,6 +213,7 @@ describe('Tracekit - Safari Tests', () => { }, { filename: 'safari-extension://3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/commons.js', + abs_path: 'safari-extension://3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/commons.js', function: 'ClipperError', lineno: 223036, colno: 10, @@ -163,9 +239,10 @@ describe('Tracekit - Safari Tests', () => { type: 'TypeError', stacktrace: { frames: [ - { filename: '[native code]', function: 'promiseReactionJob', in_app: true }, + { filename: '[native code]', abs_path: '[native code]', function: 'promiseReactionJob', in_app: true }, { filename: 'safari-extension://com.grammarly.safari.extension.ext2-W8F64X92K3/ee7759dd/Grammarly.js', + abs_path: 'safari-extension://com.grammarly.safari.extension.ext2-W8F64X92K3/ee7759dd/Grammarly.js', function: '?', lineno: 2, colno: 1588410, @@ -173,6 +250,7 @@ describe('Tracekit - Safari Tests', () => { }, { filename: 'safari-extension://com.grammarly.safari.extension.ext2-W8F64X92K3/ee7759dd/Grammarly.js', + abs_path: 'safari-extension://com.grammarly.safari.extension.ext2-W8F64X92K3/ee7759dd/Grammarly.js', function: 'isClaimed', lineno: 2, colno: 929865, @@ -201,6 +279,7 @@ describe('Tracekit - Safari Tests', () => { frames: [ { filename: 'safari-web-extension://3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/topee-content.js', + abs_path: 'safari-web-extension://3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/topee-content.js', function: '?', lineno: 3313, colno: 26, @@ -208,6 +287,7 @@ describe('Tracekit - Safari Tests', () => { }, { filename: 'safari-web-extension://3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/commons.js', + abs_path: 'safari-web-extension://3284871F-A480-4FFC-8BC4-3F362C752446/2665fee0/commons.js', function: 'ClipperError', lineno: 223036, colno: 10, @@ -233,9 +313,10 @@ describe('Tracekit - Safari Tests', () => { type: 'TypeError', stacktrace: { frames: [ - { filename: '[native code]', function: 'promiseReactionJob', in_app: true }, + { filename: '[native code]', abs_path: '[native code]', function: 'promiseReactionJob', in_app: true }, { filename: 'safari-web-extension://46434E60-F5BD-48A4-80C8-A422C5D16897/scripts/content-script.js', + abs_path: 'safari-web-extension://46434E60-F5BD-48A4-80C8-A422C5D16897/scripts/content-script.js', function: '?', lineno: 29, colno: 56027, @@ -243,6 +324,7 @@ describe('Tracekit - Safari Tests', () => { }, { filename: 'safari-web-extension://46434E60-F5BD-48A4-80C8-A422C5D16897/scripts/content-script.js', + abs_path: 'safari-web-extension://46434E60-F5BD-48A4-80C8-A422C5D16897/scripts/content-script.js', function: 'p_', lineno: 29, colno: 33314, @@ -271,10 +353,31 @@ describe('Tracekit - Safari Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/test', function: 'global code', lineno: 24, colno: 10, in_app: true }, - { filename: 'http://localhost:5000/test', function: 'foo', lineno: 19, colno: 22, in_app: true }, - { filename: '[native code]', function: 'map', in_app: true }, - { filename: 'http://localhost:5000/test', function: 'fooIterator', lineno: 20, colno: 26, in_app: true }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: 'global code', + lineno: 24, + colno: 10, + in_app: true, + }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: 'foo', + lineno: 19, + colno: 22, + in_app: true, + }, + { filename: '[native code]', abs_path: '[native code]', function: 'map', in_app: true }, + { + filename: 'http://localhost:5000/test', + abs_path: 'http://localhost:5000/test', + function: 'fooIterator', + lineno: 20, + colno: 26, + in_app: true, + }, ], }, }); @@ -305,17 +408,73 @@ describe('Tracekit - Safari Tests', () => { type: 'Error', stacktrace: { frames: [ - { filename: 'http://localhost:5000/', function: '?', lineno: 50, colno: 29, in_app: true }, - { filename: 'http://localhost:5000/', function: 'testMethod', lineno: 44, colno: 10, in_app: true }, - { filename: 'http://localhost:5000/', function: 'aha', lineno: 39, colno: 9, in_app: true }, - { filename: '[native code]', function: 'eval', in_app: true }, - { filename: 'http://localhost:5000/', function: 'test', lineno: 33, colno: 26, in_app: true }, - { filename: '[native code]', function: 'map', in_app: true }, - { filename: 'http://localhost:5000/', function: '?', lineno: 34, colno: 25, in_app: true }, - { filename: 'http://localhost:5000/', function: 'callback', lineno: 25, colno: 23, in_app: true }, - { filename: 'http://localhost:5000/', function: 'callAnotherThing', lineno: 20, colno: 16, in_app: true }, - { filename: '[native code]', function: 'aha', in_app: true }, - { filename: 'http://localhost:5000/', function: 'aha', lineno: 19, colno: 22, in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: '?', + lineno: 50, + colno: 29, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'testMethod', + lineno: 44, + colno: 10, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'aha', + lineno: 39, + colno: 9, + in_app: true, + }, + { filename: '[native code]', abs_path: '[native code]', function: 'eval', in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'test', + lineno: 33, + colno: 26, + in_app: true, + }, + { filename: '[native code]', abs_path: '[native code]', function: 'map', in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: '?', + lineno: 34, + colno: 25, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'callback', + lineno: 25, + colno: 23, + in_app: true, + }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'callAnotherThing', + lineno: 20, + colno: 16, + in_app: true, + }, + { filename: '[native code]', abs_path: '[native code]', function: 'aha', in_app: true }, + { + filename: 'http://localhost:5000/', + abs_path: 'http://localhost:5000/', + function: 'aha', + lineno: 19, + colno: 22, + in_app: true, + }, ], }, }); From f9933170d89f5b2153bb369d1a9db7717741f76b Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 14 Feb 2023 10:49:22 +0100 Subject: [PATCH 05/21] ref(replay): Extract worker into dedicated package (#7139) --- package.json | 1 + packages/replay-worker/.eslintignore | 1 + packages/replay-worker/.eslintrc.js | 17 +++++++ packages/replay-worker/.gitignore | 5 ++ packages/replay-worker/LICENSE | 14 +++++ packages/replay-worker/README.md | 12 +++++ packages/replay-worker/jest.config.js | 1 + packages/replay-worker/package.json | 51 +++++++++++++++++++ .../rollup.worker.config.js} | 10 ++-- .../src/Compressor.ts | 24 ++++++++- .../src/handleMessage.ts | 13 +++-- .../worker => replay-worker}/src/worker.ts | 0 packages/replay-worker/test/tsconfig.json | 7 +++ .../test/unit}/Compressor.test.ts | 4 +- packages/replay-worker/tsconfig.json | 11 ++++ packages/replay-worker/tsconfig.test.json | 7 +++ packages/replay-worker/vendor/index.d.ts | 1 + packages/replay-worker/vendor/index.js | 6 +++ .../vendor/worker.d.ts} | 0 packages/replay/.eslintrc.js | 1 - packages/replay/package.json | 20 +++----- packages/replay/rollup.npm.config.js | 3 +- packages/replay/src/eventBuffer/index.ts | 5 +- packages/replay/src/worker/worker.js | 2 - packages/replay/tsconfig.worker.json | 19 ------- yarn.lock | 41 ++++++++++++++- 26 files changed, 226 insertions(+), 50 deletions(-) create mode 100644 packages/replay-worker/.eslintignore create mode 100644 packages/replay-worker/.eslintrc.js create mode 100644 packages/replay-worker/.gitignore create mode 100644 packages/replay-worker/LICENSE create mode 100644 packages/replay-worker/README.md create mode 100644 packages/replay-worker/jest.config.js create mode 100644 packages/replay-worker/package.json rename packages/{replay/rollup.config.worker.js => replay-worker/rollup.worker.config.js} (70%) rename packages/{replay/worker => replay-worker}/src/Compressor.ts (75%) rename packages/{replay/worker => replay-worker}/src/handleMessage.ts (84%) rename packages/{replay/worker => replay-worker}/src/worker.ts (100%) create mode 100644 packages/replay-worker/test/tsconfig.json rename packages/{replay/test/unit/worker => replay-worker/test/unit}/Compressor.test.ts (88%) create mode 100644 packages/replay-worker/tsconfig.json create mode 100644 packages/replay-worker/tsconfig.test.json create mode 100644 packages/replay-worker/vendor/index.d.ts create mode 100644 packages/replay-worker/vendor/index.js rename packages/{replay/src/worker/worker.js.d.ts => replay-worker/vendor/worker.d.ts} (100%) delete mode 100644 packages/replay/src/worker/worker.js delete mode 100644 packages/replay/tsconfig.worker.json diff --git a/package.json b/package.json index 8f00f559c4da..606308f1dbe2 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "packages/react", "packages/remix", "packages/replay", + "packages/replay-worker", "packages/serverless", "packages/svelte", "packages/tracing", diff --git a/packages/replay-worker/.eslintignore b/packages/replay-worker/.eslintignore new file mode 100644 index 000000000000..22d0d82f8095 --- /dev/null +++ b/packages/replay-worker/.eslintignore @@ -0,0 +1 @@ +vendor diff --git a/packages/replay-worker/.eslintrc.js b/packages/replay-worker/.eslintrc.js new file mode 100644 index 000000000000..47c60ffff072 --- /dev/null +++ b/packages/replay-worker/.eslintrc.js @@ -0,0 +1,17 @@ +// Note: All paths are relative to the directory in which eslint is being run, rather than the directory where this file +// lives + +// ESLint config docs: https://eslint.org/docs/user-guide/configuring/ + +module.exports = { + extends: ['../../.eslintrc.js'], + overrides: [ + { + files: ['src/**/*.ts'], + rules: { + // We cannot use backticks, as that conflicts with the stringified worker + 'prefer-template': 'off', + }, + }, + ], +}; diff --git a/packages/replay-worker/.gitignore b/packages/replay-worker/.gitignore new file mode 100644 index 000000000000..bb9eed987fb4 --- /dev/null +++ b/packages/replay-worker/.gitignore @@ -0,0 +1,5 @@ +node_modules +/*.tgz +.eslintcache +build +!vendor/*.d.ts diff --git a/packages/replay-worker/LICENSE b/packages/replay-worker/LICENSE new file mode 100644 index 000000000000..d11896ba1181 --- /dev/null +++ b/packages/replay-worker/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2023 Sentry (https://sentry.io) and individual contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/replay-worker/README.md b/packages/replay-worker/README.md new file mode 100644 index 000000000000..6d63a45eb8c5 --- /dev/null +++ b/packages/replay-worker/README.md @@ -0,0 +1,12 @@ +

+ + Sentry + +

+ +# Sentry Session Replay Worker + +This is an internal package that is used by @sentry/replay. +It generates a web worker and converts it to a string, so that we can process it easier in replay. + +By extracting this into a dedicated (private, internal) package, we can streamline the build of replay. diff --git a/packages/replay-worker/jest.config.js b/packages/replay-worker/jest.config.js new file mode 100644 index 000000000000..24f49ab59a4c --- /dev/null +++ b/packages/replay-worker/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest/jest.config.js'); diff --git a/packages/replay-worker/package.json b/packages/replay-worker/package.json new file mode 100644 index 000000000000..313bb14f856e --- /dev/null +++ b/packages/replay-worker/package.json @@ -0,0 +1,51 @@ +{ + "name": "@sentry-internal/replay-worker", + "version": "7.37.2", + "description": "Worker for @sentry/replay", + "main": "build/index.js", + "module": "build/index.js", + "sideEffects": false, + "private": true, + "scripts": { + "build": "yarn build:transpile", + "build:transpile": "rollup -c rollup.worker.config.js", + "build:types": "yarn build:transpile", + "build:dev": "yarn build", + "build:watch": "run-p build:transpile:watch", + "build:dev:watch": "run-p build:watch", + "build:transpile:watch": "yarn build:rollup --watch", + "clean": "rimraf build", + "fix": "run-s fix:eslint fix:prettier", + "fix:eslint": "eslint . --format stylish --fix", + "fix:prettier": "prettier --write \"{src,test}/**/*.ts\"", + "lint": "run-s lint:prettier lint:eslint", + "lint:eslint": "eslint . --format stylish", + "lint:prettier": "prettier --check \"{src,test}/**/*.ts\"", + "test": "jest", + "test:watch": "jest --watch" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/getsentry/sentry-javascript.git" + }, + "author": "Sentry", + "license": "MIT", + "bugs": { + "url": "https://github.com/getsentry/sentry-javascript/issues" + }, + "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", + "devDependencies": { + "@types/pako": "^2.0.0", + "rollup-plugin-copy": "~3.4.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "pako": "^2.1.0" + }, + "engines": { + "node": ">=12" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/packages/replay/rollup.config.worker.js b/packages/replay-worker/rollup.worker.config.js similarity index 70% rename from packages/replay/rollup.config.worker.js rename to packages/replay-worker/rollup.worker.config.js index 7258a1864548..15506c2f0eab 100644 --- a/packages/replay/rollup.config.worker.js +++ b/packages/replay-worker/rollup.worker.config.js @@ -4,15 +4,16 @@ import resolve from '@rollup/plugin-node-resolve'; import typescript from '@rollup/plugin-typescript'; import { defineConfig } from 'rollup'; import { terser } from 'rollup-plugin-terser'; +import copy from 'rollup-plugin-copy'; const config = defineConfig({ - input: ['./worker/src/worker.ts'], + input: ['./src/worker.ts'], output: { - dir: './src/worker/', + dir: './build/', format: 'esm', }, plugins: [ - typescript({ tsconfig: './tsconfig.worker.json' }), + typescript({ tsconfig: './tsconfig.json', inlineSourceMap: false, sourceMap: false, inlineSources: false }), resolve(), terser({ mangle: { @@ -25,6 +26,9 @@ const config = defineConfig({ return `export default \`${code}\`;`; }, }, + copy({ + targets: [{ src: 'vendor/*', dest: 'build' }], + }), ], }); diff --git a/packages/replay/worker/src/Compressor.ts b/packages/replay-worker/src/Compressor.ts similarity index 75% rename from packages/replay/worker/src/Compressor.ts rename to packages/replay-worker/src/Compressor.ts index 5eb6f5fe4953..8f58198a890b 100644 --- a/packages/replay/worker/src/Compressor.ts +++ b/packages/replay-worker/src/Compressor.ts @@ -1,5 +1,8 @@ -import { constants, Deflate } from 'pako'; +import { constants, Deflate, deflate } from 'pako'; +/** + * A stateful compressor that can be used to batch compress events. + */ export class Compressor { /** * pako deflator instance @@ -15,10 +18,16 @@ export class Compressor { this._init(); } + /** + * Clear the compressor buffer. + */ public clear(): void { this._init(); } + /** + * Add an event to the compressor buffer. + */ public addEvent(data: string): void { if (!data) { throw new Error('Adding invalid event'); @@ -34,6 +43,9 @@ export class Compressor { this._hasEvents = true; } + /** + * Finish compression of the current buffer. + */ public finish(): Uint8Array { // We should always have a list, it can be empty this.deflate.push(']', constants.Z_FINISH); @@ -51,6 +63,9 @@ export class Compressor { return result; } + /** + * Re-initialize the compressor buffer. + */ private _init(): void { this._hasEvents = false; this.deflate = new Deflate(); @@ -59,3 +74,10 @@ export class Compressor { this.deflate.push('[', constants.Z_NO_FLUSH); } } + +/** + * Compress a string. + */ +export function compress(data: string): Uint8Array { + return deflate(data); +} diff --git a/packages/replay/worker/src/handleMessage.ts b/packages/replay-worker/src/handleMessage.ts similarity index 84% rename from packages/replay/worker/src/handleMessage.ts rename to packages/replay-worker/src/handleMessage.ts index 95fcb1256f11..958797e82cbb 100644 --- a/packages/replay/worker/src/handleMessage.ts +++ b/packages/replay-worker/src/handleMessage.ts @@ -1,18 +1,18 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { Compressor } from './Compressor'; +import { compress, Compressor } from './Compressor'; const compressor = new Compressor(); interface Handlers { clear: () => void; addEvent: (data: string) => void; - finish: () => void; + finish: () => Uint8Array; + compress: (data: string) => Uint8Array; } const handlers: Handlers = { clear: () => { compressor.clear(); - return ''; }, addEvent: (data: string) => { @@ -22,8 +22,15 @@ const handlers: Handlers = { finish: () => { return compressor.finish(); }, + + compress: (data: string) => { + return compress(data); + }, }; +/** + * Handler for worker messages. + */ export function handleMessage(e: MessageEvent): void { const method = e.data.method as string; const id = e.data.id as number; diff --git a/packages/replay/worker/src/worker.ts b/packages/replay-worker/src/worker.ts similarity index 100% rename from packages/replay/worker/src/worker.ts rename to packages/replay-worker/src/worker.ts diff --git a/packages/replay-worker/test/tsconfig.json b/packages/replay-worker/test/tsconfig.json new file mode 100644 index 000000000000..6c6ff4423fde --- /dev/null +++ b/packages/replay-worker/test/tsconfig.json @@ -0,0 +1,7 @@ +// TODO Once https://github.com/microsoft/TypeScript/issues/33094 is done (if it ever is), this file can disappear, as +// it's purely a placeholder to satisfy VSCode. +{ + "extends": "../tsconfig.test.json", + + "include": ["./**/*"] +} diff --git a/packages/replay/test/unit/worker/Compressor.test.ts b/packages/replay-worker/test/unit/Compressor.test.ts similarity index 88% rename from packages/replay/test/unit/worker/Compressor.test.ts rename to packages/replay-worker/test/unit/Compressor.test.ts index e701ce2d79ff..73b067fa7213 100644 --- a/packages/replay/test/unit/worker/Compressor.test.ts +++ b/packages/replay-worker/test/unit/Compressor.test.ts @@ -1,8 +1,8 @@ import pako from 'pako'; -import { Compressor } from '../../../worker/src/Compressor'; +import { Compressor } from '../../src/Compressor'; -describe('Unit | worker | Compressor', () => { +describe('Compressor', () => { it('compresses multiple events', () => { const compressor = new Compressor(); diff --git a/packages/replay-worker/tsconfig.json b/packages/replay-worker/tsconfig.json new file mode 100644 index 000000000000..38cb52869a42 --- /dev/null +++ b/packages/replay-worker/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "esnext", + "lib": ["webworker", "scripthost"], + "esModuleInterop": true, + "target": "es6", + "strictPropertyInitialization": false + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/replay-worker/tsconfig.test.json b/packages/replay-worker/tsconfig.test.json new file mode 100644 index 000000000000..6a49bbfc2a41 --- /dev/null +++ b/packages/replay-worker/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "include": ["test/**/*.ts"], + "compilerOptions": { + "types": ["node", "jest"] + } +} diff --git a/packages/replay-worker/vendor/index.d.ts b/packages/replay-worker/vendor/index.d.ts new file mode 100644 index 000000000000..63e84bb3ac83 --- /dev/null +++ b/packages/replay-worker/vendor/index.d.ts @@ -0,0 +1 @@ +export function getWorkerURL(): string; diff --git a/packages/replay-worker/vendor/index.js b/packages/replay-worker/vendor/index.js new file mode 100644 index 000000000000..9bf87542112b --- /dev/null +++ b/packages/replay-worker/vendor/index.js @@ -0,0 +1,6 @@ +import workerString from './worker'; + +export function getWorkerURL() { + const workerBlob = new Blob([workerString]); + return URL.createObjectURL(workerBlob); +} diff --git a/packages/replay/src/worker/worker.js.d.ts b/packages/replay-worker/vendor/worker.d.ts similarity index 100% rename from packages/replay/src/worker/worker.js.d.ts rename to packages/replay-worker/vendor/worker.d.ts diff --git a/packages/replay/.eslintrc.js b/packages/replay/.eslintrc.js index a0d9dc46f1f8..e4101e557b26 100644 --- a/packages/replay/.eslintrc.js +++ b/packages/replay/.eslintrc.js @@ -5,7 +5,6 @@ module.exports = { extends: ['../../.eslintrc.js'], - ignorePatterns: ['rollup.config.worker.js'], overrides: [ { files: ['worker/**/*.ts'], diff --git a/packages/replay/package.json b/packages/replay/package.json index adce38782e46..f3536f9ea967 100644 --- a/packages/replay/package.json +++ b/packages/replay/package.json @@ -7,17 +7,14 @@ "types": "build/npm/types/index.d.ts", "sideEffects": false, "scripts": { - "build": "run-s build:worker && run-p build:core build:types build:bundle", - "build:transpile": "run-s build:worker build:core", + "build": "run-p build:transpile build:types build:bundle", + "build:transpile": "rollup -c rollup.npm.config.js", "build:bundle": "rollup -c rollup.bundle.config.js", - "build:dev": "run-p build:worker build:transpile build:types", - "build:worker": "rollup -c rollup.config.worker.js", - "build:core": "rollup -c rollup.npm.config.js", + "build:dev": "run-p build:transpile build:types", "build:types": "tsc -p tsconfig.types.json", - "build:watch": "run-p build:worker:watch build:core:watch build:bundle:watch build:types:watch", - "build:dev:watch": "run-p build:core:watch build:types:watch", - "build:core:watch": "yarn build:core --watch", - "build:worker:watch": "yarn build:worker --watch", + "build:watch": "run-p build:transpile:watch build:bundle:watch build:types:watch", + "build:dev:watch": "run-p build:transpile:watch build:types:watch", + "build:transpile:watch": "yarn build:transpile --watch", "build:bundle:watch": "yarn build:bundle --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:tarball": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", @@ -25,7 +22,7 @@ "clean": "rimraf build sentry-replay-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", - "fix:prettier": "prettier --write \"{src,test,scripts,worker}/**/*.ts\"", + "fix:prettier": "prettier --write \"{src,test,scripts}/**/*.ts\"", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts,worker}/**/*.ts\"", @@ -46,10 +43,9 @@ "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { "@babel/core": "^7.17.5", + "@sentry-internal/replay-worker": "7.37.2", "@sentry-internal/rrweb": "1.103.0", - "@types/pako": "^2.0.0", "jsdom-worker": "^0.2.1", - "pako": "^2.0.4", "tslib": "^1.9.3" }, "dependencies": { diff --git a/packages/replay/rollup.npm.config.js b/packages/replay/rollup.npm.config.js index 77934a115f87..c3c2db72bebf 100644 --- a/packages/replay/rollup.npm.config.js +++ b/packages/replay/rollup.npm.config.js @@ -5,8 +5,7 @@ export default makeNPMConfigVariants( hasBundles: true, packageSpecificConfig: { output: { - // set exports to 'named' or 'auto' so that rollup doesn't warn about - // the default export in `worker/worker.js` + // set exports to 'named' or 'auto' so that rollup doesn't warn exports: 'named', // set preserveModules to false because for Replay we actually want // to bundle everything into one file. diff --git a/packages/replay/src/eventBuffer/index.ts b/packages/replay/src/eventBuffer/index.ts index a928d6832d1e..f0eb83c68243 100644 --- a/packages/replay/src/eventBuffer/index.ts +++ b/packages/replay/src/eventBuffer/index.ts @@ -1,7 +1,7 @@ +import { getWorkerURL } from '@sentry-internal/replay-worker'; import { logger } from '@sentry/utils'; import type { EventBuffer } from '../types'; -import workerString from '../worker/worker.js'; import { EventBufferArray } from './EventBufferArray'; import { EventBufferProxy } from './EventBufferProxy'; @@ -16,8 +16,7 @@ export function createEventBuffer({ useCompression }: CreateEventBufferParams): // eslint-disable-next-line no-restricted-globals if (useCompression && window.Worker) { try { - const workerBlob = new Blob([workerString]); - const workerUrl = URL.createObjectURL(workerBlob); + const workerUrl = getWorkerURL(); __DEBUG_BUILD__ && logger.log('[Replay] Using compression worker'); const worker = new Worker(workerUrl); diff --git a/packages/replay/src/worker/worker.js b/packages/replay/src/worker/worker.js deleted file mode 100644 index d034d5002634..000000000000 --- a/packages/replay/src/worker/worker.js +++ /dev/null @@ -1,2 +0,0 @@ -export default `/*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */ -function t(t){let e=t.length;for(;--e>=0;)t[e]=0}const e=new Uint8Array([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]),a=new Uint8Array([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]),i=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]),n=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),s=new Array(576);t(s);const r=new Array(60);t(r);const o=new Array(512);t(o);const l=new Array(256);t(l);const h=new Array(29);t(h);const d=new Array(30);function _(t,e,a,i,n){this.static_tree=t,this.extra_bits=e,this.extra_base=a,this.elems=i,this.max_length=n,this.has_stree=t&&t.length}let f,c,u;function w(t,e){this.dyn_tree=t,this.max_code=0,this.stat_desc=e}t(d);const m=t=>t<256?o[t]:o[256+(t>>>7)],b=(t,e)=>{t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255},g=(t,e,a)=>{t.bi_valid>16-a?(t.bi_buf|=e<>16-t.bi_valid,t.bi_valid+=a-16):(t.bi_buf|=e<{g(t,a[2*e],a[2*e+1])},k=(t,e)=>{let a=0;do{a|=1&t,t>>>=1,a<<=1}while(--e>0);return a>>>1},v=(t,e,a)=>{const i=new Array(16);let n,s,r=0;for(n=1;n<=15;n++)r=r+a[n-1]<<1,i[n]=r;for(s=0;s<=e;s++){let e=t[2*s+1];0!==e&&(t[2*s]=k(i[e]++,e))}},y=t=>{let e;for(e=0;e<286;e++)t.dyn_ltree[2*e]=0;for(e=0;e<30;e++)t.dyn_dtree[2*e]=0;for(e=0;e<19;e++)t.bl_tree[2*e]=0;t.dyn_ltree[512]=1,t.opt_len=t.static_len=0,t.sym_next=t.matches=0},x=t=>{t.bi_valid>8?b(t,t.bi_buf):t.bi_valid>0&&(t.pending_buf[t.pending++]=t.bi_buf),t.bi_buf=0,t.bi_valid=0},z=(t,e,a,i)=>{const n=2*e,s=2*a;return t[n]{const i=t.heap[a];let n=a<<1;for(;n<=t.heap_len&&(n{let s,r,o,_,f=0;if(0!==t.sym_next)do{s=255&t.pending_buf[t.sym_buf+f++],s+=(255&t.pending_buf[t.sym_buf+f++])<<8,r=t.pending_buf[t.sym_buf+f++],0===s?p(t,r,i):(o=l[r],p(t,o+256+1,i),_=e[o],0!==_&&(r-=h[o],g(t,r,_)),s--,o=m(s),p(t,o,n),_=a[o],0!==_&&(s-=d[o],g(t,s,_)))}while(f{const a=e.dyn_tree,i=e.stat_desc.static_tree,n=e.stat_desc.has_stree,s=e.stat_desc.elems;let r,o,l,h=-1;for(t.heap_len=0,t.heap_max=573,r=0;r>1;r>=1;r--)A(t,a,r);l=s;do{r=t.heap[1],t.heap[1]=t.heap[t.heap_len--],A(t,a,1),o=t.heap[1],t.heap[--t.heap_max]=r,t.heap[--t.heap_max]=o,a[2*l]=a[2*r]+a[2*o],t.depth[l]=(t.depth[r]>=t.depth[o]?t.depth[r]:t.depth[o])+1,a[2*r+1]=a[2*o+1]=l,t.heap[1]=l++,A(t,a,1)}while(t.heap_len>=2);t.heap[--t.heap_max]=t.heap[1],((t,e)=>{const a=e.dyn_tree,i=e.max_code,n=e.stat_desc.static_tree,s=e.stat_desc.has_stree,r=e.stat_desc.extra_bits,o=e.stat_desc.extra_base,l=e.stat_desc.max_length;let h,d,_,f,c,u,w=0;for(f=0;f<=15;f++)t.bl_count[f]=0;for(a[2*t.heap[t.heap_max]+1]=0,h=t.heap_max+1;h<573;h++)d=t.heap[h],f=a[2*a[2*d+1]+1]+1,f>l&&(f=l,w++),a[2*d+1]=f,d>i||(t.bl_count[f]++,c=0,d>=o&&(c=r[d-o]),u=a[2*d],t.opt_len+=u*(f+c),s&&(t.static_len+=u*(n[2*d+1]+c)));if(0!==w){do{for(f=l-1;0===t.bl_count[f];)f--;t.bl_count[f]--,t.bl_count[f+1]+=2,t.bl_count[l]--,w-=2}while(w>0);for(f=l;0!==f;f--)for(d=t.bl_count[f];0!==d;)_=t.heap[--h],_>i||(a[2*_+1]!==f&&(t.opt_len+=(f-a[2*_+1])*a[2*_],a[2*_+1]=f),d--)}})(t,e),v(a,h,t.bl_count)},Z=(t,e,a)=>{let i,n,s=-1,r=e[1],o=0,l=7,h=4;for(0===r&&(l=138,h=3),e[2*(a+1)+1]=65535,i=0;i<=a;i++)n=r,r=e[2*(i+1)+1],++o{let i,n,s=-1,r=e[1],o=0,l=7,h=4;for(0===r&&(l=138,h=3),i=0;i<=a;i++)if(n=r,r=e[2*(i+1)+1],!(++o{g(t,0+(i?1:0),3),x(t),b(t,a),b(t,~a),a&&t.pending_buf.set(t.window.subarray(e,e+a),t.pending),t.pending+=a};var T=(t,e,a,i)=>{let o,l,h=0;t.level>0?(2===t.strm.data_type&&(t.strm.data_type=(t=>{let e,a=4093624447;for(e=0;e<=31;e++,a>>>=1)if(1&a&&0!==t.dyn_ltree[2*e])return 0;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return 1;for(e=32;e<256;e++)if(0!==t.dyn_ltree[2*e])return 1;return 0})(t)),R(t,t.l_desc),R(t,t.d_desc),h=(t=>{let e;for(Z(t,t.dyn_ltree,t.l_desc.max_code),Z(t,t.dyn_dtree,t.d_desc.max_code),R(t,t.bl_desc),e=18;e>=3&&0===t.bl_tree[2*n[e]+1];e--);return t.opt_len+=3*(e+1)+5+5+4,e})(t),o=t.opt_len+3+7>>>3,l=t.static_len+3+7>>>3,l<=o&&(o=l)):o=l=a+5,a+4<=o&&-1!==e?D(t,e,a,i):4===t.strategy||l===o?(g(t,2+(i?1:0),3),E(t,s,r)):(g(t,4+(i?1:0),3),((t,e,a,i)=>{let s;for(g(t,e-257,5),g(t,a-1,5),g(t,i-4,4),s=0;s{S||((()=>{let t,n,w,m,b;const g=new Array(16);for(w=0,m=0;m<28;m++)for(h[m]=w,t=0;t<1<>=7;m<30;m++)for(d[m]=b<<7,t=0;t<1<(t.pending_buf[t.sym_buf+t.sym_next++]=e,t.pending_buf[t.sym_buf+t.sym_next++]=e>>8,t.pending_buf[t.sym_buf+t.sym_next++]=a,0===e?t.dyn_ltree[2*a]++:(t.matches++,e--,t.dyn_ltree[2*(l[a]+256+1)]++,t.dyn_dtree[2*m(e)]++),t.sym_next===t.sym_end),_tr_align:t=>{g(t,2,3),p(t,256,s),(t=>{16===t.bi_valid?(b(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):t.bi_valid>=8&&(t.pending_buf[t.pending++]=255&t.bi_buf,t.bi_buf>>=8,t.bi_valid-=8)})(t)}};var F=(t,e,a,i)=>{let n=65535&t|0,s=t>>>16&65535|0,r=0;for(;0!==a;){r=a>2e3?2e3:a,a-=r;do{n=n+e[i++]|0,s=s+n|0}while(--r);n%=65521,s%=65521}return n|s<<16|0};const L=new Uint32Array((()=>{let t,e=[];for(var a=0;a<256;a++){t=a;for(var i=0;i<8;i++)t=1&t?3988292384^t>>>1:t>>>1;e[a]=t}return e})());var N=(t,e,a,i)=>{const n=L,s=i+a;t^=-1;for(let a=i;a>>8^n[255&(t^e[a])];return-1^t},I={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"},B={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_MEM_ERROR:-4,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8};const{_tr_init:C,_tr_stored_block:H,_tr_flush_block:M,_tr_tally:j,_tr_align:K}=O,{Z_NO_FLUSH:P,Z_PARTIAL_FLUSH:Y,Z_FULL_FLUSH:G,Z_FINISH:X,Z_BLOCK:W,Z_OK:q,Z_STREAM_END:J,Z_STREAM_ERROR:Q,Z_DATA_ERROR:V,Z_BUF_ERROR:$,Z_DEFAULT_COMPRESSION:tt,Z_FILTERED:et,Z_HUFFMAN_ONLY:at,Z_RLE:it,Z_FIXED:nt,Z_DEFAULT_STRATEGY:st,Z_UNKNOWN:rt,Z_DEFLATED:ot}=B,lt=(t,e)=>(t.msg=I[e],e),ht=t=>2*t-(t>4?9:0),dt=t=>{let e=t.length;for(;--e>=0;)t[e]=0},_t=t=>{let e,a,i,n=t.w_size;e=t.hash_size,i=e;do{a=t.head[--i],t.head[i]=a>=n?a-n:0}while(--e);e=n,i=e;do{a=t.prev[--i],t.prev[i]=a>=n?a-n:0}while(--e)};let ft=(t,e,a)=>(e<{const e=t.state;let a=e.pending;a>t.avail_out&&(a=t.avail_out),0!==a&&(t.output.set(e.pending_buf.subarray(e.pending_out,e.pending_out+a),t.next_out),t.next_out+=a,e.pending_out+=a,t.total_out+=a,t.avail_out-=a,e.pending-=a,0===e.pending&&(e.pending_out=0))},ut=(t,e)=>{M(t,t.block_start>=0?t.block_start:-1,t.strstart-t.block_start,e),t.block_start=t.strstart,ct(t.strm)},wt=(t,e)=>{t.pending_buf[t.pending++]=e},mt=(t,e)=>{t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e},bt=(t,e,a,i)=>{let n=t.avail_in;return n>i&&(n=i),0===n?0:(t.avail_in-=n,e.set(t.input.subarray(t.next_in,t.next_in+n),a),1===t.state.wrap?t.adler=F(t.adler,e,n,a):2===t.state.wrap&&(t.adler=N(t.adler,e,n,a)),t.next_in+=n,t.total_in+=n,n)},gt=(t,e)=>{let a,i,n=t.max_chain_length,s=t.strstart,r=t.prev_length,o=t.nice_match;const l=t.strstart>t.w_size-262?t.strstart-(t.w_size-262):0,h=t.window,d=t.w_mask,_=t.prev,f=t.strstart+258;let c=h[s+r-1],u=h[s+r];t.prev_length>=t.good_match&&(n>>=2),o>t.lookahead&&(o=t.lookahead);do{if(a=e,h[a+r]===u&&h[a+r-1]===c&&h[a]===h[s]&&h[++a]===h[s+1]){s+=2,a++;do{}while(h[++s]===h[++a]&&h[++s]===h[++a]&&h[++s]===h[++a]&&h[++s]===h[++a]&&h[++s]===h[++a]&&h[++s]===h[++a]&&h[++s]===h[++a]&&h[++s]===h[++a]&&sr){if(t.match_start=e,r=i,i>=o)break;c=h[s+r-1],u=h[s+r]}}}while((e=_[e&d])>l&&0!=--n);return r<=t.lookahead?r:t.lookahead},pt=t=>{const e=t.w_size;let a,i,n;do{if(i=t.window_size-t.lookahead-t.strstart,t.strstart>=e+(e-262)&&(t.window.set(t.window.subarray(e,e+e-i),0),t.match_start-=e,t.strstart-=e,t.block_start-=e,t.insert>t.strstart&&(t.insert=t.strstart),_t(t),i+=e),0===t.strm.avail_in)break;if(a=bt(t.strm,t.window,t.strstart+t.lookahead,i),t.lookahead+=a,t.lookahead+t.insert>=3)for(n=t.strstart-t.insert,t.ins_h=t.window[n],t.ins_h=ft(t,t.ins_h,t.window[n+1]);t.insert&&(t.ins_h=ft(t,t.ins_h,t.window[n+3-1]),t.prev[n&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=n,n++,t.insert--,!(t.lookahead+t.insert<3)););}while(t.lookahead<262&&0!==t.strm.avail_in)},kt=(t,e)=>{let a,i,n,s=t.pending_buf_size-5>t.w_size?t.w_size:t.pending_buf_size-5,r=0,o=t.strm.avail_in;do{if(a=65535,n=t.bi_valid+42>>3,t.strm.avail_outi+t.strm.avail_in&&(a=i+t.strm.avail_in),a>n&&(a=n),a>8,t.pending_buf[t.pending-2]=~a,t.pending_buf[t.pending-1]=~a>>8,ct(t.strm),i&&(i>a&&(i=a),t.strm.output.set(t.window.subarray(t.block_start,t.block_start+i),t.strm.next_out),t.strm.next_out+=i,t.strm.avail_out-=i,t.strm.total_out+=i,t.block_start+=i,a-=i),a&&(bt(t.strm,t.strm.output,t.strm.next_out,a),t.strm.next_out+=a,t.strm.avail_out-=a,t.strm.total_out+=a)}while(0===r);return o-=t.strm.avail_in,o&&(o>=t.w_size?(t.matches=2,t.window.set(t.strm.input.subarray(t.strm.next_in-t.w_size,t.strm.next_in),0),t.strstart=t.w_size,t.insert=t.strstart):(t.window_size-t.strstart<=o&&(t.strstart-=t.w_size,t.window.set(t.window.subarray(t.w_size,t.w_size+t.strstart),0),t.matches<2&&t.matches++,t.insert>t.strstart&&(t.insert=t.strstart)),t.window.set(t.strm.input.subarray(t.strm.next_in-o,t.strm.next_in),t.strstart),t.strstart+=o,t.insert+=o>t.w_size-t.insert?t.w_size-t.insert:o),t.block_start=t.strstart),t.high_watern&&t.block_start>=t.w_size&&(t.block_start-=t.w_size,t.strstart-=t.w_size,t.window.set(t.window.subarray(t.w_size,t.w_size+t.strstart),0),t.matches<2&&t.matches++,n+=t.w_size,t.insert>t.strstart&&(t.insert=t.strstart)),n>t.strm.avail_in&&(n=t.strm.avail_in),n&&(bt(t.strm,t.window,t.strstart,n),t.strstart+=n,t.insert+=n>t.w_size-t.insert?t.w_size-t.insert:n),t.high_water>3,n=t.pending_buf_size-n>65535?65535:t.pending_buf_size-n,s=n>t.w_size?t.w_size:n,i=t.strstart-t.block_start,(i>=s||(i||e===X)&&e!==P&&0===t.strm.avail_in&&i<=n)&&(a=i>n?n:i,r=e===X&&0===t.strm.avail_in&&a===i?1:0,H(t,t.block_start,a,r),t.block_start+=a,ct(t.strm)),r?3:1)},vt=(t,e)=>{let a,i;for(;;){if(t.lookahead<262){if(pt(t),t.lookahead<262&&e===P)return 1;if(0===t.lookahead)break}if(a=0,t.lookahead>=3&&(t.ins_h=ft(t,t.ins_h,t.window[t.strstart+3-1]),a=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart),0!==a&&t.strstart-a<=t.w_size-262&&(t.match_length=gt(t,a)),t.match_length>=3)if(i=j(t,t.strstart-t.match_start,t.match_length-3),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=3){t.match_length--;do{t.strstart++,t.ins_h=ft(t,t.ins_h,t.window[t.strstart+3-1]),a=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart}while(0!=--t.match_length);t.strstart++}else t.strstart+=t.match_length,t.match_length=0,t.ins_h=t.window[t.strstart],t.ins_h=ft(t,t.ins_h,t.window[t.strstart+1]);else i=j(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++;if(i&&(ut(t,!1),0===t.strm.avail_out))return 1}return t.insert=t.strstart<2?t.strstart:2,e===X?(ut(t,!0),0===t.strm.avail_out?3:4):t.sym_next&&(ut(t,!1),0===t.strm.avail_out)?1:2},yt=(t,e)=>{let a,i,n;for(;;){if(t.lookahead<262){if(pt(t),t.lookahead<262&&e===P)return 1;if(0===t.lookahead)break}if(a=0,t.lookahead>=3&&(t.ins_h=ft(t,t.ins_h,t.window[t.strstart+3-1]),a=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart),t.prev_length=t.match_length,t.prev_match=t.match_start,t.match_length=2,0!==a&&t.prev_length4096)&&(t.match_length=2)),t.prev_length>=3&&t.match_length<=t.prev_length){n=t.strstart+t.lookahead-3,i=j(t,t.strstart-1-t.prev_match,t.prev_length-3),t.lookahead-=t.prev_length-1,t.prev_length-=2;do{++t.strstart<=n&&(t.ins_h=ft(t,t.ins_h,t.window[t.strstart+3-1]),a=t.prev[t.strstart&t.w_mask]=t.head[t.ins_h],t.head[t.ins_h]=t.strstart)}while(0!=--t.prev_length);if(t.match_available=0,t.match_length=2,t.strstart++,i&&(ut(t,!1),0===t.strm.avail_out))return 1}else if(t.match_available){if(i=j(t,0,t.window[t.strstart-1]),i&&ut(t,!1),t.strstart++,t.lookahead--,0===t.strm.avail_out)return 1}else t.match_available=1,t.strstart++,t.lookahead--}return t.match_available&&(i=j(t,0,t.window[t.strstart-1]),t.match_available=0),t.insert=t.strstart<2?t.strstart:2,e===X?(ut(t,!0),0===t.strm.avail_out?3:4):t.sym_next&&(ut(t,!1),0===t.strm.avail_out)?1:2};function xt(t,e,a,i,n){this.good_length=t,this.max_lazy=e,this.nice_length=a,this.max_chain=i,this.func=n}const zt=[new xt(0,0,0,0,kt),new xt(4,4,8,4,vt),new xt(4,5,16,8,vt),new xt(4,6,32,32,vt),new xt(4,4,16,16,yt),new xt(8,16,32,32,yt),new xt(8,16,128,128,yt),new xt(8,32,128,256,yt),new xt(32,128,258,1024,yt),new xt(32,258,258,4096,yt)];function At(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=ot,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new Uint16Array(1146),this.dyn_dtree=new Uint16Array(122),this.bl_tree=new Uint16Array(78),dt(this.dyn_ltree),dt(this.dyn_dtree),dt(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new Uint16Array(16),this.heap=new Uint16Array(573),dt(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new Uint16Array(573),dt(this.depth),this.sym_buf=0,this.lit_bufsize=0,this.sym_next=0,this.sym_end=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}const Et=t=>{if(!t)return 1;const e=t.state;return!e||e.strm!==t||42!==e.status&&57!==e.status&&69!==e.status&&73!==e.status&&91!==e.status&&103!==e.status&&113!==e.status&&666!==e.status?1:0},Rt=t=>{if(Et(t))return lt(t,Q);t.total_in=t.total_out=0,t.data_type=rt;const e=t.state;return e.pending=0,e.pending_out=0,e.wrap<0&&(e.wrap=-e.wrap),e.status=2===e.wrap?57:e.wrap?42:113,t.adler=2===e.wrap?0:1,e.last_flush=-2,C(e),q},Zt=t=>{const e=Rt(t);var a;return e===q&&((a=t.state).window_size=2*a.w_size,dt(a.head),a.max_lazy_match=zt[a.level].max_lazy,a.good_match=zt[a.level].good_length,a.nice_match=zt[a.level].nice_length,a.max_chain_length=zt[a.level].max_chain,a.strstart=0,a.block_start=0,a.lookahead=0,a.insert=0,a.match_length=a.prev_length=2,a.match_available=0,a.ins_h=0),e},Ut=(t,e,a,i,n,s)=>{if(!t)return Q;let r=1;if(e===tt&&(e=6),i<0?(r=0,i=-i):i>15&&(r=2,i-=16),n<1||n>9||a!==ot||i<8||i>15||e<0||e>9||s<0||s>nt||8===i&&1!==r)return lt(t,Q);8===i&&(i=9);const o=new At;return t.state=o,o.strm=t,o.status=42,o.wrap=r,o.gzhead=null,o.w_bits=i,o.w_size=1<Ut(t,e,ot,15,8,st),deflateInit2:Ut,deflateReset:Zt,deflateResetKeep:Rt,deflateSetHeader:(t,e)=>Et(t)||2!==t.state.wrap?Q:(t.state.gzhead=e,q),deflate:(t,e)=>{if(Et(t)||e>W||e<0)return t?lt(t,Q):Q;const a=t.state;if(!t.output||0!==t.avail_in&&!t.input||666===a.status&&e!==X)return lt(t,0===t.avail_out?$:Q);const i=a.last_flush;if(a.last_flush=e,0!==a.pending){if(ct(t),0===t.avail_out)return a.last_flush=-1,q}else if(0===t.avail_in&&ht(e)<=ht(i)&&e!==X)return lt(t,$);if(666===a.status&&0!==t.avail_in)return lt(t,$);if(42===a.status&&0===a.wrap&&(a.status=113),42===a.status){let e=ot+(a.w_bits-8<<4)<<8,i=-1;if(i=a.strategy>=at||a.level<2?0:a.level<6?1:6===a.level?2:3,e|=i<<6,0!==a.strstart&&(e|=32),e+=31-e%31,mt(a,e),0!==a.strstart&&(mt(a,t.adler>>>16),mt(a,65535&t.adler)),t.adler=1,a.status=113,ct(t),0!==a.pending)return a.last_flush=-1,q}if(57===a.status)if(t.adler=0,wt(a,31),wt(a,139),wt(a,8),a.gzhead)wt(a,(a.gzhead.text?1:0)+(a.gzhead.hcrc?2:0)+(a.gzhead.extra?4:0)+(a.gzhead.name?8:0)+(a.gzhead.comment?16:0)),wt(a,255&a.gzhead.time),wt(a,a.gzhead.time>>8&255),wt(a,a.gzhead.time>>16&255),wt(a,a.gzhead.time>>24&255),wt(a,9===a.level?2:a.strategy>=at||a.level<2?4:0),wt(a,255&a.gzhead.os),a.gzhead.extra&&a.gzhead.extra.length&&(wt(a,255&a.gzhead.extra.length),wt(a,a.gzhead.extra.length>>8&255)),a.gzhead.hcrc&&(t.adler=N(t.adler,a.pending_buf,a.pending,0)),a.gzindex=0,a.status=69;else if(wt(a,0),wt(a,0),wt(a,0),wt(a,0),wt(a,0),wt(a,9===a.level?2:a.strategy>=at||a.level<2?4:0),wt(a,3),a.status=113,ct(t),0!==a.pending)return a.last_flush=-1,q;if(69===a.status){if(a.gzhead.extra){let e=a.pending,i=(65535&a.gzhead.extra.length)-a.gzindex;for(;a.pending+i>a.pending_buf_size;){let n=a.pending_buf_size-a.pending;if(a.pending_buf.set(a.gzhead.extra.subarray(a.gzindex,a.gzindex+n),a.pending),a.pending=a.pending_buf_size,a.gzhead.hcrc&&a.pending>e&&(t.adler=N(t.adler,a.pending_buf,a.pending-e,e)),a.gzindex+=n,ct(t),0!==a.pending)return a.last_flush=-1,q;e=0,i-=n}let n=new Uint8Array(a.gzhead.extra);a.pending_buf.set(n.subarray(a.gzindex,a.gzindex+i),a.pending),a.pending+=i,a.gzhead.hcrc&&a.pending>e&&(t.adler=N(t.adler,a.pending_buf,a.pending-e,e)),a.gzindex=0}a.status=73}if(73===a.status){if(a.gzhead.name){let e,i=a.pending;do{if(a.pending===a.pending_buf_size){if(a.gzhead.hcrc&&a.pending>i&&(t.adler=N(t.adler,a.pending_buf,a.pending-i,i)),ct(t),0!==a.pending)return a.last_flush=-1,q;i=0}e=a.gzindexi&&(t.adler=N(t.adler,a.pending_buf,a.pending-i,i)),a.gzindex=0}a.status=91}if(91===a.status){if(a.gzhead.comment){let e,i=a.pending;do{if(a.pending===a.pending_buf_size){if(a.gzhead.hcrc&&a.pending>i&&(t.adler=N(t.adler,a.pending_buf,a.pending-i,i)),ct(t),0!==a.pending)return a.last_flush=-1,q;i=0}e=a.gzindexi&&(t.adler=N(t.adler,a.pending_buf,a.pending-i,i))}a.status=103}if(103===a.status){if(a.gzhead.hcrc){if(a.pending+2>a.pending_buf_size&&(ct(t),0!==a.pending))return a.last_flush=-1,q;wt(a,255&t.adler),wt(a,t.adler>>8&255),t.adler=0}if(a.status=113,ct(t),0!==a.pending)return a.last_flush=-1,q}if(0!==t.avail_in||0!==a.lookahead||e!==P&&666!==a.status){let i=0===a.level?kt(a,e):a.strategy===at?((t,e)=>{let a;for(;;){if(0===t.lookahead&&(pt(t),0===t.lookahead)){if(e===P)return 1;break}if(t.match_length=0,a=j(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++,a&&(ut(t,!1),0===t.strm.avail_out))return 1}return t.insert=0,e===X?(ut(t,!0),0===t.strm.avail_out?3:4):t.sym_next&&(ut(t,!1),0===t.strm.avail_out)?1:2})(a,e):a.strategy===it?((t,e)=>{let a,i,n,s;const r=t.window;for(;;){if(t.lookahead<=258){if(pt(t),t.lookahead<=258&&e===P)return 1;if(0===t.lookahead)break}if(t.match_length=0,t.lookahead>=3&&t.strstart>0&&(n=t.strstart-1,i=r[n],i===r[++n]&&i===r[++n]&&i===r[++n])){s=t.strstart+258;do{}while(i===r[++n]&&i===r[++n]&&i===r[++n]&&i===r[++n]&&i===r[++n]&&i===r[++n]&&i===r[++n]&&i===r[++n]&&nt.lookahead&&(t.match_length=t.lookahead)}if(t.match_length>=3?(a=j(t,1,t.match_length-3),t.lookahead-=t.match_length,t.strstart+=t.match_length,t.match_length=0):(a=j(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++),a&&(ut(t,!1),0===t.strm.avail_out))return 1}return t.insert=0,e===X?(ut(t,!0),0===t.strm.avail_out?3:4):t.sym_next&&(ut(t,!1),0===t.strm.avail_out)?1:2})(a,e):zt[a.level].func(a,e);if(3!==i&&4!==i||(a.status=666),1===i||3===i)return 0===t.avail_out&&(a.last_flush=-1),q;if(2===i&&(e===Y?K(a):e!==W&&(H(a,0,0,!1),e===G&&(dt(a.head),0===a.lookahead&&(a.strstart=0,a.block_start=0,a.insert=0))),ct(t),0===t.avail_out))return a.last_flush=-1,q}return e!==X?q:a.wrap<=0?J:(2===a.wrap?(wt(a,255&t.adler),wt(a,t.adler>>8&255),wt(a,t.adler>>16&255),wt(a,t.adler>>24&255),wt(a,255&t.total_in),wt(a,t.total_in>>8&255),wt(a,t.total_in>>16&255),wt(a,t.total_in>>24&255)):(mt(a,t.adler>>>16),mt(a,65535&t.adler)),ct(t),a.wrap>0&&(a.wrap=-a.wrap),0!==a.pending?q:J)},deflateEnd:t=>{if(Et(t))return Q;const e=t.state.status;return t.state=null,113===e?lt(t,V):q},deflateSetDictionary:(t,e)=>{let a=e.length;if(Et(t))return Q;const i=t.state,n=i.wrap;if(2===n||1===n&&42!==i.status||i.lookahead)return Q;if(1===n&&(t.adler=F(t.adler,e,a,0)),i.wrap=0,a>=i.w_size){0===n&&(dt(i.head),i.strstart=0,i.block_start=0,i.insert=0);let t=new Uint8Array(i.w_size);t.set(e.subarray(a-i.w_size,a),0),e=t,a=i.w_size}const s=t.avail_in,r=t.next_in,o=t.input;for(t.avail_in=a,t.next_in=0,t.input=e,pt(i);i.lookahead>=3;){let t=i.strstart,e=i.lookahead-2;do{i.ins_h=ft(i,i.ins_h,i.window[t+3-1]),i.prev[t&i.w_mask]=i.head[i.ins_h],i.head[i.ins_h]=t,t++}while(--e);i.strstart=t,i.lookahead=2,pt(i)}return i.strstart+=i.lookahead,i.block_start=i.strstart,i.insert=i.lookahead,i.lookahead=0,i.match_length=i.prev_length=2,i.match_available=0,t.next_in=r,t.input=o,t.avail_in=s,i.wrap=n,q},deflateInfo:"pako deflate (from Nodeca project)"};const Dt=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var Tt=function(t){const e=Array.prototype.slice.call(arguments,1);for(;e.length;){const a=e.shift();if(a){if("object"!=typeof a)throw new TypeError(a+"must be non-object");for(const e in a)Dt(a,e)&&(t[e]=a[e])}}return t},Ot=t=>{let e=0;for(let a=0,i=t.length;a=252?6:t>=248?5:t>=240?4:t>=224?3:t>=192?2:1;Lt[254]=Lt[254]=1;var Nt=t=>{if("function"==typeof TextEncoder&&TextEncoder.prototype.encode)return(new TextEncoder).encode(t);let e,a,i,n,s,r=t.length,o=0;for(n=0;n>>6,e[s++]=128|63&a):a<65536?(e[s++]=224|a>>>12,e[s++]=128|a>>>6&63,e[s++]=128|63&a):(e[s++]=240|a>>>18,e[s++]=128|a>>>12&63,e[s++]=128|a>>>6&63,e[s++]=128|63&a);return e},It=(t,e)=>{const a=e||t.length;if("function"==typeof TextDecoder&&TextDecoder.prototype.decode)return(new TextDecoder).decode(t.subarray(0,e));let i,n;const s=new Array(2*a);for(n=0,i=0;i4)s[n++]=65533,i+=r-1;else{for(e&=2===r?31:3===r?15:7;r>1&&i1?s[n++]=65533:e<65536?s[n++]=e:(e-=65536,s[n++]=55296|e>>10&1023,s[n++]=56320|1023&e)}}return((t,e)=>{if(e<65534&&t.subarray&&Ft)return String.fromCharCode.apply(null,t.length===e?t:t.subarray(0,e));let a="";for(let i=0;i{(e=e||t.length)>t.length&&(e=t.length);let a=e-1;for(;a>=0&&128==(192&t[a]);)a--;return a<0||0===a?e:a+Lt[t[a]]>e?a:e};var Ct=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0};const Ht=Object.prototype.toString,{Z_NO_FLUSH:Mt,Z_SYNC_FLUSH:jt,Z_FULL_FLUSH:Kt,Z_FINISH:Pt,Z_OK:Yt,Z_STREAM_END:Gt,Z_DEFAULT_COMPRESSION:Xt,Z_DEFAULT_STRATEGY:Wt,Z_DEFLATED:qt}=B;function Jt(t){this.options=Tt({level:Xt,method:qt,chunkSize:16384,windowBits:15,memLevel:8,strategy:Wt},t||{});let e=this.options;e.raw&&e.windowBits>0?e.windowBits=-e.windowBits:e.gzip&&e.windowBits>0&&e.windowBits<16&&(e.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new Ct,this.strm.avail_out=0;let a=St.deflateInit2(this.strm,e.level,e.method,e.windowBits,e.memLevel,e.strategy);if(a!==Yt)throw new Error(I[a]);if(e.header&&St.deflateSetHeader(this.strm,e.header),e.dictionary){let t;if(t="string"==typeof e.dictionary?Nt(e.dictionary):"[object ArrayBuffer]"===Ht.call(e.dictionary)?new Uint8Array(e.dictionary):e.dictionary,a=St.deflateSetDictionary(this.strm,t),a!==Yt)throw new Error(I[a]);this._dict_set=!0}}function Qt(t,e){const a=new Jt(e);if(a.push(t,!0),a.err)throw a.msg||I[a.err];return a.result}Jt.prototype.push=function(t,e){const a=this.strm,i=this.options.chunkSize;let n,s;if(this.ended)return!1;for(s=e===~~e?e:!0===e?Pt:Mt,"string"==typeof t?a.input=Nt(t):"[object ArrayBuffer]"===Ht.call(t)?a.input=new Uint8Array(t):a.input=t,a.next_in=0,a.avail_in=a.input.length;;)if(0===a.avail_out&&(a.output=new Uint8Array(i),a.next_out=0,a.avail_out=i),(s===jt||s===Kt)&&a.avail_out<=6)this.onData(a.output.subarray(0,a.next_out)),a.avail_out=0;else{if(n=St.deflate(a,s),n===Gt)return a.next_out>0&&this.onData(a.output.subarray(0,a.next_out)),n=St.deflateEnd(this.strm),this.onEnd(n),this.ended=!0,n===Yt;if(0!==a.avail_out){if(s>0&&a.next_out>0)this.onData(a.output.subarray(0,a.next_out)),a.avail_out=0;else if(0===a.avail_in)break}else this.onData(a.output)}return!0},Jt.prototype.onData=function(t){this.chunks.push(t)},Jt.prototype.onEnd=function(t){t===Yt&&(this.result=Ot(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg};var Vt={Deflate:Jt,deflate:Qt,deflateRaw:function(t,e){return(e=e||{}).raw=!0,Qt(t,e)},gzip:function(t,e){return(e=e||{}).gzip=!0,Qt(t,e)},constants:B};var $t=function(t,e){let a,i,n,s,r,o,l,h,d,_,f,c,u,w,m,b,g,p,k,v,y,x,z,A;const E=t.state;a=t.next_in,z=t.input,i=a+(t.avail_in-5),n=t.next_out,A=t.output,s=n-(e-t.avail_out),r=n+(t.avail_out-257),o=E.dmax,l=E.wsize,h=E.whave,d=E.wnext,_=E.window,f=E.hold,c=E.bits,u=E.lencode,w=E.distcode,m=(1<>>24,f>>>=p,c-=p,p=g>>>16&255,0===p)A[n++]=65535&g;else{if(!(16&p)){if(0==(64&p)){g=u[(65535&g)+(f&(1<>>=p,c-=p),c<15&&(f+=z[a++]<>>24,f>>>=p,c-=p,p=g>>>16&255,!(16&p)){if(0==(64&p)){g=w[(65535&g)+(f&(1<o){t.msg="invalid distance too far back",E.mode=16209;break t}if(f>>>=p,c-=p,p=n-s,v>p){if(p=v-p,p>h&&E.sane){t.msg="invalid distance too far back",E.mode=16209;break t}if(y=0,x=_,0===d){if(y+=l-p,p2;)A[n++]=x[y++],A[n++]=x[y++],A[n++]=x[y++],k-=3;k&&(A[n++]=x[y++],k>1&&(A[n++]=x[y++]))}else{y=n-v;do{A[n++]=A[y++],A[n++]=A[y++],A[n++]=A[y++],k-=3}while(k>2);k&&(A[n++]=A[y++],k>1&&(A[n++]=A[y++]))}break}}break}}while(a>3,a-=k,c-=k<<3,f&=(1<{const l=o.bits;let h,d,_,f,c,u,w=0,m=0,b=0,g=0,p=0,k=0,v=0,y=0,x=0,z=0,A=null;const E=new Uint16Array(16),R=new Uint16Array(16);let Z,U,S,D=null;for(w=0;w<=15;w++)E[w]=0;for(m=0;m=1&&0===E[g];g--);if(p>g&&(p=g),0===g)return n[s++]=20971520,n[s++]=20971520,o.bits=1,0;for(b=1;b0&&(0===t||1!==g))return-1;for(R[1]=0,w=1;w<15;w++)R[w+1]=R[w]+E[w];for(m=0;m852||2===t&&x>592)return 1;for(;;){Z=w-v,r[m]+1=u?(U=D[r[m]-u],S=A[r[m]-u]):(U=96,S=0),h=1<>v)+d]=Z<<24|U<<16|S|0}while(0!==d);for(h=1<>=1;if(0!==h?(z&=h-1,z+=h):z=0,m++,0==--E[w]){if(w===g)break;w=e[a+r[m]]}if(w>p&&(z&f)!==_){for(0===v&&(v=p),c+=b,k=w-v,y=1<852||2===t&&x>592)return 1;_=z&f,n[_]=p<<24|k<<16|c-s|0}}return 0!==z&&(n[c+z]=w-v<<24|64<<16|0),o.bits=p,0};const{Z_FINISH:se,Z_BLOCK:re,Z_TREES:oe,Z_OK:le,Z_STREAM_END:he,Z_NEED_DICT:de,Z_STREAM_ERROR:_e,Z_DATA_ERROR:fe,Z_MEM_ERROR:ce,Z_BUF_ERROR:ue,Z_DEFLATED:we}=B,me=16209,be=t=>(t>>>24&255)+(t>>>8&65280)+((65280&t)<<8)+((255&t)<<24);function ge(){this.strm=null,this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new Uint16Array(320),this.work=new Uint16Array(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}const pe=t=>{if(!t)return 1;const e=t.state;return!e||e.strm!==t||e.mode<16180||e.mode>16211?1:0},ke=t=>{if(pe(t))return _e;const e=t.state;return t.total_in=t.total_out=e.total=0,t.msg="",e.wrap&&(t.adler=1&e.wrap),e.mode=16180,e.last=0,e.havedict=0,e.flags=-1,e.dmax=32768,e.head=null,e.hold=0,e.bits=0,e.lencode=e.lendyn=new Int32Array(852),e.distcode=e.distdyn=new Int32Array(592),e.sane=1,e.back=-1,le},ve=t=>{if(pe(t))return _e;const e=t.state;return e.wsize=0,e.whave=0,e.wnext=0,ke(t)},ye=(t,e)=>{let a;if(pe(t))return _e;const i=t.state;return e<0?(a=0,e=-e):(a=5+(e>>4),e<48&&(e&=15)),e&&(e<8||e>15)?_e:(null!==i.window&&i.wbits!==e&&(i.window=null),i.wrap=a,i.wbits=e,ve(t))},xe=(t,e)=>{if(!t)return _e;const a=new ge;t.state=a,a.strm=t,a.window=null,a.mode=16180;const i=ye(t,e);return i!==le&&(t.state=null),i};let ze,Ae,Ee=!0;const Re=t=>{if(Ee){ze=new Int32Array(512),Ae=new Int32Array(32);let e=0;for(;e<144;)t.lens[e++]=8;for(;e<256;)t.lens[e++]=9;for(;e<280;)t.lens[e++]=7;for(;e<288;)t.lens[e++]=8;for(ne(1,t.lens,0,288,ze,0,t.work,{bits:9}),e=0;e<32;)t.lens[e++]=5;ne(2,t.lens,0,32,Ae,0,t.work,{bits:5}),Ee=!1}t.lencode=ze,t.lenbits=9,t.distcode=Ae,t.distbits=5},Ze=(t,e,a,i)=>{let n;const s=t.state;return null===s.window&&(s.wsize=1<=s.wsize?(s.window.set(e.subarray(a-s.wsize,a),0),s.wnext=0,s.whave=s.wsize):(n=s.wsize-s.wnext,n>i&&(n=i),s.window.set(e.subarray(a-i,a-i+n),s.wnext),(i-=n)?(s.window.set(e.subarray(a-i,a),0),s.wnext=i,s.whave=s.wsize):(s.wnext+=n,s.wnext===s.wsize&&(s.wnext=0),s.whavexe(t,15),inflateInit2:xe,inflate:(t,e)=>{let a,i,n,s,r,o,l,h,d,_,f,c,u,w,m,b,g,p,k,v,y,x,z=0;const A=new Uint8Array(4);let E,R;const Z=new Uint8Array([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);if(pe(t)||!t.output||!t.input&&0!==t.avail_in)return _e;a=t.state,16191===a.mode&&(a.mode=16192),r=t.next_out,n=t.output,l=t.avail_out,s=t.next_in,i=t.input,o=t.avail_in,h=a.hold,d=a.bits,_=o,f=l,x=le;t:for(;;)switch(a.mode){case 16180:if(0===a.wrap){a.mode=16192;break}for(;d<16;){if(0===o)break t;o--,h+=i[s++]<>>8&255,a.check=N(a.check,A,2,0),h=0,d=0,a.mode=16181;break}if(a.head&&(a.head.done=!1),!(1&a.wrap)||(((255&h)<<8)+(h>>8))%31){t.msg="incorrect header check",a.mode=me;break}if((15&h)!==we){t.msg="unknown compression method",a.mode=me;break}if(h>>>=4,d-=4,y=8+(15&h),0===a.wbits&&(a.wbits=y),y>15||y>a.wbits){t.msg="invalid window size",a.mode=me;break}a.dmax=1<>8&1),512&a.flags&&4&a.wrap&&(A[0]=255&h,A[1]=h>>>8&255,a.check=N(a.check,A,2,0)),h=0,d=0,a.mode=16182;case 16182:for(;d<32;){if(0===o)break t;o--,h+=i[s++]<>>8&255,A[2]=h>>>16&255,A[3]=h>>>24&255,a.check=N(a.check,A,4,0)),h=0,d=0,a.mode=16183;case 16183:for(;d<16;){if(0===o)break t;o--,h+=i[s++]<>8),512&a.flags&&4&a.wrap&&(A[0]=255&h,A[1]=h>>>8&255,a.check=N(a.check,A,2,0)),h=0,d=0,a.mode=16184;case 16184:if(1024&a.flags){for(;d<16;){if(0===o)break t;o--,h+=i[s++]<>>8&255,a.check=N(a.check,A,2,0)),h=0,d=0}else a.head&&(a.head.extra=null);a.mode=16185;case 16185:if(1024&a.flags&&(c=a.length,c>o&&(c=o),c&&(a.head&&(y=a.head.extra_len-a.length,a.head.extra||(a.head.extra=new Uint8Array(a.head.extra_len)),a.head.extra.set(i.subarray(s,s+c),y)),512&a.flags&&4&a.wrap&&(a.check=N(a.check,i,c,s)),o-=c,s+=c,a.length-=c),a.length))break t;a.length=0,a.mode=16186;case 16186:if(2048&a.flags){if(0===o)break t;c=0;do{y=i[s+c++],a.head&&y&&a.length<65536&&(a.head.name+=String.fromCharCode(y))}while(y&&c>9&1,a.head.done=!0),t.adler=a.check=0,a.mode=16191;break;case 16189:for(;d<32;){if(0===o)break t;o--,h+=i[s++]<>>=7&d,d-=7&d,a.mode=16206;break}for(;d<3;){if(0===o)break t;o--,h+=i[s++]<>>=1,d-=1,3&h){case 0:a.mode=16193;break;case 1:if(Re(a),a.mode=16199,e===oe){h>>>=2,d-=2;break t}break;case 2:a.mode=16196;break;case 3:t.msg="invalid block type",a.mode=me}h>>>=2,d-=2;break;case 16193:for(h>>>=7&d,d-=7&d;d<32;){if(0===o)break t;o--,h+=i[s++]<>>16^65535)){t.msg="invalid stored block lengths",a.mode=me;break}if(a.length=65535&h,h=0,d=0,a.mode=16194,e===oe)break t;case 16194:a.mode=16195;case 16195:if(c=a.length,c){if(c>o&&(c=o),c>l&&(c=l),0===c)break t;n.set(i.subarray(s,s+c),r),o-=c,s+=c,l-=c,r+=c,a.length-=c;break}a.mode=16191;break;case 16196:for(;d<14;){if(0===o)break t;o--,h+=i[s++]<>>=5,d-=5,a.ndist=1+(31&h),h>>>=5,d-=5,a.ncode=4+(15&h),h>>>=4,d-=4,a.nlen>286||a.ndist>30){t.msg="too many length or distance symbols",a.mode=me;break}a.have=0,a.mode=16197;case 16197:for(;a.have>>=3,d-=3}for(;a.have<19;)a.lens[Z[a.have++]]=0;if(a.lencode=a.lendyn,a.lenbits=7,E={bits:a.lenbits},x=ne(0,a.lens,0,19,a.lencode,0,a.work,E),a.lenbits=E.bits,x){t.msg="invalid code lengths set",a.mode=me;break}a.have=0,a.mode=16198;case 16198:for(;a.have>>24,b=z>>>16&255,g=65535&z,!(m<=d);){if(0===o)break t;o--,h+=i[s++]<>>=m,d-=m,a.lens[a.have++]=g;else{if(16===g){for(R=m+2;d>>=m,d-=m,0===a.have){t.msg="invalid bit length repeat",a.mode=me;break}y=a.lens[a.have-1],c=3+(3&h),h>>>=2,d-=2}else if(17===g){for(R=m+3;d>>=m,d-=m,y=0,c=3+(7&h),h>>>=3,d-=3}else{for(R=m+7;d>>=m,d-=m,y=0,c=11+(127&h),h>>>=7,d-=7}if(a.have+c>a.nlen+a.ndist){t.msg="invalid bit length repeat",a.mode=me;break}for(;c--;)a.lens[a.have++]=y}}if(a.mode===me)break;if(0===a.lens[256]){t.msg="invalid code -- missing end-of-block",a.mode=me;break}if(a.lenbits=9,E={bits:a.lenbits},x=ne(1,a.lens,0,a.nlen,a.lencode,0,a.work,E),a.lenbits=E.bits,x){t.msg="invalid literal/lengths set",a.mode=me;break}if(a.distbits=6,a.distcode=a.distdyn,E={bits:a.distbits},x=ne(2,a.lens,a.nlen,a.ndist,a.distcode,0,a.work,E),a.distbits=E.bits,x){t.msg="invalid distances set",a.mode=me;break}if(a.mode=16199,e===oe)break t;case 16199:a.mode=16200;case 16200:if(o>=6&&l>=258){t.next_out=r,t.avail_out=l,t.next_in=s,t.avail_in=o,a.hold=h,a.bits=d,$t(t,f),r=t.next_out,n=t.output,l=t.avail_out,s=t.next_in,i=t.input,o=t.avail_in,h=a.hold,d=a.bits,16191===a.mode&&(a.back=-1);break}for(a.back=0;z=a.lencode[h&(1<>>24,b=z>>>16&255,g=65535&z,!(m<=d);){if(0===o)break t;o--,h+=i[s++]<>p)],m=z>>>24,b=z>>>16&255,g=65535&z,!(p+m<=d);){if(0===o)break t;o--,h+=i[s++]<>>=p,d-=p,a.back+=p}if(h>>>=m,d-=m,a.back+=m,a.length=g,0===b){a.mode=16205;break}if(32&b){a.back=-1,a.mode=16191;break}if(64&b){t.msg="invalid literal/length code",a.mode=me;break}a.extra=15&b,a.mode=16201;case 16201:if(a.extra){for(R=a.extra;d>>=a.extra,d-=a.extra,a.back+=a.extra}a.was=a.length,a.mode=16202;case 16202:for(;z=a.distcode[h&(1<>>24,b=z>>>16&255,g=65535&z,!(m<=d);){if(0===o)break t;o--,h+=i[s++]<>p)],m=z>>>24,b=z>>>16&255,g=65535&z,!(p+m<=d);){if(0===o)break t;o--,h+=i[s++]<>>=p,d-=p,a.back+=p}if(h>>>=m,d-=m,a.back+=m,64&b){t.msg="invalid distance code",a.mode=me;break}a.offset=g,a.extra=15&b,a.mode=16203;case 16203:if(a.extra){for(R=a.extra;d>>=a.extra,d-=a.extra,a.back+=a.extra}if(a.offset>a.dmax){t.msg="invalid distance too far back",a.mode=me;break}a.mode=16204;case 16204:if(0===l)break t;if(c=f-l,a.offset>c){if(c=a.offset-c,c>a.whave&&a.sane){t.msg="invalid distance too far back",a.mode=me;break}c>a.wnext?(c-=a.wnext,u=a.wsize-c):u=a.wnext-c,c>a.length&&(c=a.length),w=a.window}else w=n,u=r-a.offset,c=a.length;c>l&&(c=l),l-=c,a.length-=c;do{n[r++]=w[u++]}while(--c);0===a.length&&(a.mode=16200);break;case 16205:if(0===l)break t;n[r++]=a.length,l--,a.mode=16200;break;case 16206:if(a.wrap){for(;d<32;){if(0===o)break t;o--,h|=i[s++]<{if(pe(t))return _e;let e=t.state;return e.window&&(e.window=null),t.state=null,le},inflateGetHeader:(t,e)=>{if(pe(t))return _e;const a=t.state;return 0==(2&a.wrap)?_e:(a.head=e,e.done=!1,le)},inflateSetDictionary:(t,e)=>{const a=e.length;let i,n,s;return pe(t)?_e:(i=t.state,0!==i.wrap&&16190!==i.mode?_e:16190===i.mode&&(n=1,n=F(n,e,a,0),n!==i.check)?fe:(s=Ze(t,e,a,a),s?(i.mode=16210,ce):(i.havedict=1,le)))},inflateInfo:"pako inflate (from Nodeca project)"};var Se=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1};const De=Object.prototype.toString,{Z_NO_FLUSH:Te,Z_FINISH:Oe,Z_OK:Fe,Z_STREAM_END:Le,Z_NEED_DICT:Ne,Z_STREAM_ERROR:Ie,Z_DATA_ERROR:Be,Z_MEM_ERROR:Ce}=B;function He(t){this.options=Tt({chunkSize:65536,windowBits:15,to:""},t||{});const e=this.options;e.raw&&e.windowBits>=0&&e.windowBits<16&&(e.windowBits=-e.windowBits,0===e.windowBits&&(e.windowBits=-15)),!(e.windowBits>=0&&e.windowBits<16)||t&&t.windowBits||(e.windowBits+=32),e.windowBits>15&&e.windowBits<48&&0==(15&e.windowBits)&&(e.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new Ct,this.strm.avail_out=0;let a=Ue.inflateInit2(this.strm,e.windowBits);if(a!==Fe)throw new Error(I[a]);if(this.header=new Se,Ue.inflateGetHeader(this.strm,this.header),e.dictionary&&("string"==typeof e.dictionary?e.dictionary=Nt(e.dictionary):"[object ArrayBuffer]"===De.call(e.dictionary)&&(e.dictionary=new Uint8Array(e.dictionary)),e.raw&&(a=Ue.inflateSetDictionary(this.strm,e.dictionary),a!==Fe)))throw new Error(I[a])}He.prototype.push=function(t,e){const a=this.strm,i=this.options.chunkSize,n=this.options.dictionary;let s,r,o;if(this.ended)return!1;for(r=e===~~e?e:!0===e?Oe:Te,"[object ArrayBuffer]"===De.call(t)?a.input=new Uint8Array(t):a.input=t,a.next_in=0,a.avail_in=a.input.length;;){for(0===a.avail_out&&(a.output=new Uint8Array(i),a.next_out=0,a.avail_out=i),s=Ue.inflate(a,r),s===Ne&&n&&(s=Ue.inflateSetDictionary(a,n),s===Fe?s=Ue.inflate(a,r):s===Be&&(s=Ne));a.avail_in>0&&s===Le&&a.state.wrap>0&&0!==t[a.next_in];)Ue.inflateReset(a),s=Ue.inflate(a,r);switch(s){case Ie:case Be:case Ne:case Ce:return this.onEnd(s),this.ended=!0,!1}if(o=a.avail_out,a.next_out&&(0===a.avail_out||s===Le))if("string"===this.options.to){let t=Bt(a.output,a.next_out),e=a.next_out-t,n=It(a.output,t);a.next_out=e,a.avail_out=i-e,e&&a.output.set(a.output.subarray(t,t+e),0),this.onData(n)}else this.onData(a.output.length===a.next_out?a.output:a.output.subarray(0,a.next_out));if(s!==Fe||0!==o){if(s===Le)return s=Ue.inflateEnd(this.strm),this.onEnd(s),this.ended=!0,!0;if(0===a.avail_in)break}}return!0},He.prototype.onData=function(t){this.chunks.push(t)},He.prototype.onEnd=function(t){t===Fe&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=Ot(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg};const{Deflate:Me,deflate:je,deflateRaw:Ke,gzip:Pe}=Vt;var Ye=Me,Ge=B;const Xe=new class{constructor(){this._init()}clear(){this._init()}addEvent(t){if(!t)throw new Error("Adding invalid event");const e=this._hasEvents?",":"";this.deflate.push(e+t,Ge.Z_SYNC_FLUSH),this._hasEvents=!0}finish(){if(this.deflate.push("]",Ge.Z_FINISH),this.deflate.err)throw this.deflate.err;const t=this.deflate.result;return this._init(),t}_init(){this._hasEvents=!1,this.deflate=new Ye,this.deflate.push("[",Ge.Z_NO_FLUSH)}},We={clear:()=>(Xe.clear(),""),addEvent:t=>Xe.addEvent(t),finish:()=>Xe.finish()};addEventListener("message",(function(t){const e=t.data.method,a=t.data.id,i=t.data.arg;if(e in We&&"function"==typeof We[e])try{const t=We[e](i);postMessage({id:a,method:e,success:!0,response:t})}catch(t){postMessage({id:a,method:e,success:!1,response:t.message}),console.error(t)}})),postMessage({id:void 0,method:"init",success:!0,response:void 0});`; diff --git a/packages/replay/tsconfig.worker.json b/packages/replay/tsconfig.worker.json deleted file mode 100644 index d8cd26b6db55..000000000000 --- a/packages/replay/tsconfig.worker.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "module": "esnext", - "moduleResolution": "node", - "noImplicitAny": true, - "noEmitOnError": false, - "esModuleInterop": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "target": "es6", - "declaration": false, - "lib": ["webworker", "scripthost"], - "baseUrl": "worker/src", - "rootDir": "worker/src", - "outDir": "src/worker" - }, - "include": ["worker/src/worker.ts", "src/types.ts"], - "exclude": ["node_modules"] -} diff --git a/yarn.lock b/yarn.lock index 38ba0892b21e..fdb1f6cf674e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3785,6 +3785,13 @@ dependencies: "@types/node" "*" +"@types/fs-extra@^8.0.1": + version "8.1.2" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.2.tgz#7125cc2e4bdd9bd2fc83005ffdb1d0ba00cca61f" + integrity sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg== + dependencies: + "@types/node" "*" + "@types/fs-extra@^8.1.0": version "8.1.1" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.1.tgz#1e49f22d09aa46e19b51c0b013cb63d0d923a068" @@ -8197,7 +8204,7 @@ color@^3.0.0: color-convert "^1.9.1" color-string "^1.5.4" -colorette@^1.2.1, colorette@^1.2.2: +colorette@^1.1.0, colorette@^1.2.1, colorette@^1.2.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== @@ -12650,6 +12657,20 @@ globby@10.0.0: merge2 "^1.2.3" slash "^3.0.0" +globby@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22" + integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + globby@11.1.0, globby@^11.0.1, globby@^11.0.2, globby@^11.0.3, globby@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -14144,6 +14165,11 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b" + integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== + is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -18667,7 +18693,7 @@ pad@^3.2.0: dependencies: wcwidth "^1.0.1" -pako@^2.0.4, pako@^2.1.0: +pako@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== @@ -21123,6 +21149,17 @@ rollup-plugin-cleanup@3.2.1: js-cleanup "^1.2.0" rollup-pluginutils "^2.8.2" +rollup-plugin-copy@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz#f1228a3ffb66ffad8606e2f3fb7ff23141ed3286" + integrity sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ== + dependencies: + "@types/fs-extra" "^8.0.1" + colorette "^1.1.0" + fs-extra "^8.1.0" + globby "10.0.1" + is-plain-object "^3.0.0" + rollup-plugin-license@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/rollup-plugin-license/-/rollup-plugin-license-2.6.1.tgz#20f15cc37950f362f8eefdc6e3a2e659d0cad9eb" From f8047f66ac8abf64f992b5d681007383ea175276 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 14 Feb 2023 11:19:26 +0100 Subject: [PATCH 06/21] fix(browser): Ensure dedupe integration ignores non-errors (#7172) --- packages/browser/src/integrations/dedupe.ts | 6 +++ .../browser/test/integration/suites/api.js | 14 ++++++ packages/integrations/src/dedupe.ts | 6 +++ packages/integrations/test/dedupe.test.ts | 48 ++++++++++++++++++- 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/packages/browser/src/integrations/dedupe.ts b/packages/browser/src/integrations/dedupe.ts index 84d6d983812b..c9903eaadf92 100644 --- a/packages/browser/src/integrations/dedupe.ts +++ b/packages/browser/src/integrations/dedupe.ts @@ -23,6 +23,12 @@ export class Dedupe implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { const eventProcessor: EventProcessor = currentEvent => { + // We want to ignore any non-error type events, e.g. transactions or replays + // These should never be deduped, and also not be compared against as _previousEvent. + if (currentEvent.type) { + return currentEvent; + } + const self = getCurrentHub().getIntegration(Dedupe); if (self) { // Juuust in case something goes wrong diff --git a/packages/browser/test/integration/suites/api.js b/packages/browser/test/integration/suites/api.js index be5de29abd0e..00ea7d58cd0d 100644 --- a/packages/browser/test/integration/suites/api.js +++ b/packages/browser/test/integration/suites/api.js @@ -112,12 +112,26 @@ describe('API', function () { // Same exceptions, different stacktrace (different line number), don't dedupe throwSameConsecutiveErrors('bar'); + + // Same exception, with transaction in between, dedupe + throwError(); + Sentry.captureEvent({ + event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', + message: 'someMessage', + transaction: 'wat', + type: 'transaction', + }); + throwError(); }).then(function (summary) { + // We have a length of one here since transactions don't go through beforeSend + // and we add events to summary in beforeSend + assert.equal(summary.events.length, 6); assert.match(summary.events[0].exception.values[0].value, /Exception no \d+/); assert.match(summary.events[1].exception.values[0].value, /Exception no \d+/); assert.equal(summary.events[2].exception.values[0].value, 'foo'); assert.equal(summary.events[3].exception.values[0].value, 'bar'); assert.equal(summary.events[4].exception.values[0].value, 'bar'); + assert.equal(summary.events[5].exception.values[0].value, 'foo'); }); }); diff --git a/packages/integrations/src/dedupe.ts b/packages/integrations/src/dedupe.ts index 625cc88f504a..8cb52295abc8 100644 --- a/packages/integrations/src/dedupe.ts +++ b/packages/integrations/src/dedupe.ts @@ -23,6 +23,12 @@ export class Dedupe implements Integration { */ public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { const eventProcessor: EventProcessor = currentEvent => { + // We want to ignore any non-error type events, e.g. transactions or replays + // These should never be deduped, and also not be compared against as _previousEvent. + if (currentEvent.type) { + return currentEvent; + } + const self = getCurrentHub().getIntegration(Dedupe); if (self) { // Juuust in case something goes wrong diff --git a/packages/integrations/test/dedupe.test.ts b/packages/integrations/test/dedupe.test.ts index 33704907dc83..7ffc30d1bdcf 100644 --- a/packages/integrations/test/dedupe.test.ts +++ b/packages/integrations/test/dedupe.test.ts @@ -1,6 +1,6 @@ -import type { Event as SentryEvent, Exception, StackFrame, Stacktrace } from '@sentry/types'; +import type { Event as SentryEvent, EventProcessor, Exception, Hub, StackFrame, Stacktrace } from '@sentry/types'; -import { _shouldDropEvent } from '../src/dedupe'; +import { _shouldDropEvent, Dedupe } from '../src/dedupe'; type EventWithException = SentryEvent & { exception: { @@ -175,4 +175,48 @@ describe('Dedupe', () => { expect(_shouldDropEvent(eventB, eventC)).toBe(false); }); }); + + describe('setupOnce', () => { + let dedupeFunc: EventProcessor; + + beforeEach(function () { + const integration = new Dedupe(); + const addGlobalEventProcessor = (callback: EventProcessor) => { + dedupeFunc = callback; + }; + + const getCurrentHub = () => { + return { + getIntegration() { + return integration; + }, + } as unknown as Hub; + }; + + integration.setupOnce(addGlobalEventProcessor, getCurrentHub); + }); + + it('ignores consecutive errors', () => { + expect(dedupeFunc(clone(exceptionEvent), {})).not.toBeNull(); + expect(dedupeFunc(clone(exceptionEvent), {})).toBeNull(); + expect(dedupeFunc(clone(exceptionEvent), {})).toBeNull(); + }); + + it('ignores transactions between errors', () => { + expect(dedupeFunc(clone(exceptionEvent), {})).not.toBeNull(); + expect( + dedupeFunc( + { + event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', + message: 'someMessage', + transaction: 'wat', + type: 'transaction', + }, + {}, + ), + ).not.toBeNull(); + expect(dedupeFunc(clone(exceptionEvent), {})).toBeNull(); + expect(dedupeFunc(clone(exceptionEvent), {})).toBeNull(); + }); + }); }); From 8a29725a87427940b0ca56623308bdb81b3e45e5 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 14 Feb 2023 13:25:20 +0100 Subject: [PATCH 07/21] build(replay): Improve replay-worker build (#7173) --- packages/replay-worker/package.json | 13 ++-- .../replay-worker/rollup.worker.config.js | 62 ++++++++++++------- packages/replay-worker/src/_worker.ts | 12 ++++ .../{vendor/index.js => src/index.ts} | 5 +- packages/replay-worker/src/worker.ts | 15 +---- packages/replay-worker/tsconfig.types.json | 10 +++ packages/replay-worker/vendor/index.d.ts | 1 - packages/replay-worker/vendor/worker.d.ts | 2 - yarn.lock | 39 +----------- 9 files changed, 75 insertions(+), 84 deletions(-) create mode 100644 packages/replay-worker/src/_worker.ts rename packages/replay-worker/{vendor/index.js => src/index.ts} (61%) create mode 100644 packages/replay-worker/tsconfig.types.json delete mode 100644 packages/replay-worker/vendor/index.d.ts delete mode 100644 packages/replay-worker/vendor/worker.d.ts diff --git a/packages/replay-worker/package.json b/packages/replay-worker/package.json index 313bb14f856e..e4d05b39c345 100644 --- a/packages/replay-worker/package.json +++ b/packages/replay-worker/package.json @@ -2,18 +2,20 @@ "name": "@sentry-internal/replay-worker", "version": "7.37.2", "description": "Worker for @sentry/replay", - "main": "build/index.js", - "module": "build/index.js", + "main": "build/npm/esm/index.js", + "module": "build/npm/esm/index.js", + "types": "build/npm/types/index.d.ts", "sideEffects": false, "private": true, "scripts": { - "build": "yarn build:transpile", + "build": "run-p build:transpile build:types", "build:transpile": "rollup -c rollup.worker.config.js", - "build:types": "yarn build:transpile", + "build:types": "tsc -p tsconfig.types.json", "build:dev": "yarn build", - "build:watch": "run-p build:transpile:watch", + "build:watch": "run-p build:transpile:watch build:types:watch", "build:dev:watch": "run-p build:watch", "build:transpile:watch": "yarn build:rollup --watch", + "build:types:watch": "yarn build:types --watch", "clean": "rimraf build", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", @@ -36,7 +38,6 @@ "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { "@types/pako": "^2.0.0", - "rollup-plugin-copy": "~3.4.0", "tslib": "^1.9.3" }, "dependencies": { diff --git a/packages/replay-worker/rollup.worker.config.js b/packages/replay-worker/rollup.worker.config.js index 15506c2f0eab..7149be6612b0 100644 --- a/packages/replay-worker/rollup.worker.config.js +++ b/packages/replay-worker/rollup.worker.config.js @@ -4,32 +4,46 @@ import resolve from '@rollup/plugin-node-resolve'; import typescript from '@rollup/plugin-typescript'; import { defineConfig } from 'rollup'; import { terser } from 'rollup-plugin-terser'; -import copy from 'rollup-plugin-copy'; -const config = defineConfig({ - input: ['./src/worker.ts'], - output: { - dir: './build/', - format: 'esm', +const config = defineConfig([ + { + input: ['./src/index.ts'], + output: { + dir: './build/npm/esm', + format: 'esm', + }, + external: ['./worker'], + plugins: [ + typescript({ tsconfig: './tsconfig.json', inlineSourceMap: false, sourceMap: false, inlineSources: false }), + terser({ + mangle: { + module: true, + }, + }), + ], }, - plugins: [ - typescript({ tsconfig: './tsconfig.json', inlineSourceMap: false, sourceMap: false, inlineSources: false }), - resolve(), - terser({ - mangle: { - module: true, - }, - }), - { - name: 'worker-to-string', - renderChunk(code) { - return `export default \`${code}\`;`; - }, + { + input: ['./src/_worker.ts'], + output: { + file: './build/npm/esm/worker.ts', + format: 'esm', }, - copy({ - targets: [{ src: 'vendor/*', dest: 'build' }], - }), - ], -}); + plugins: [ + typescript({ tsconfig: './tsconfig.json', inlineSourceMap: false, sourceMap: false, inlineSources: false }), + resolve(), + terser({ + mangle: { + module: true, + }, + }), + { + name: 'worker-to-string', + renderChunk(code) { + return `export default \`${code}\`;`; + }, + }, + ], + }, +]); export default config; diff --git a/packages/replay-worker/src/_worker.ts b/packages/replay-worker/src/_worker.ts new file mode 100644 index 000000000000..d81bb43d46df --- /dev/null +++ b/packages/replay-worker/src/_worker.ts @@ -0,0 +1,12 @@ +import { handleMessage } from './handleMessage'; + +addEventListener('message', handleMessage); + +// Immediately send a message when worker loads, so we know the worker is ready +// @ts-ignore this syntax is actually fine +postMessage({ + id: undefined, + method: 'init', + success: true, + response: undefined, +}); diff --git a/packages/replay-worker/vendor/index.js b/packages/replay-worker/src/index.ts similarity index 61% rename from packages/replay-worker/vendor/index.js rename to packages/replay-worker/src/index.ts index 9bf87542112b..103568d7a8d4 100644 --- a/packages/replay-worker/vendor/index.js +++ b/packages/replay-worker/src/index.ts @@ -1,6 +1,9 @@ import workerString from './worker'; -export function getWorkerURL() { +/** + * Get the URL for a web worker. + */ +export function getWorkerURL(): string { const workerBlob = new Blob([workerString]); return URL.createObjectURL(workerBlob); } diff --git a/packages/replay-worker/src/worker.ts b/packages/replay-worker/src/worker.ts index d81bb43d46df..e31356388d35 100644 --- a/packages/replay-worker/src/worker.ts +++ b/packages/replay-worker/src/worker.ts @@ -1,12 +1,3 @@ -import { handleMessage } from './handleMessage'; - -addEventListener('message', handleMessage); - -// Immediately send a message when worker loads, so we know the worker is ready -// @ts-ignore this syntax is actually fine -postMessage({ - id: undefined, - method: 'init', - success: true, - response: undefined, -}); +// This is replaced at build-time with the content from _worker.ts, wrapped as a string. +// This is just a placeholder so that types etc. are correct. +export default '' as string; diff --git a/packages/replay-worker/tsconfig.types.json b/packages/replay-worker/tsconfig.types.json new file mode 100644 index 000000000000..58b6f60a32b1 --- /dev/null +++ b/packages/replay-worker/tsconfig.types.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/index.ts", "src/worker.ts"], + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "build/npm/types" + } +} diff --git a/packages/replay-worker/vendor/index.d.ts b/packages/replay-worker/vendor/index.d.ts deleted file mode 100644 index 63e84bb3ac83..000000000000 --- a/packages/replay-worker/vendor/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export function getWorkerURL(): string; diff --git a/packages/replay-worker/vendor/worker.d.ts b/packages/replay-worker/vendor/worker.d.ts deleted file mode 100644 index 1dd215b1e7b2..000000000000 --- a/packages/replay-worker/vendor/worker.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const workerString: string; -export default workerString; diff --git a/yarn.lock b/yarn.lock index fdb1f6cf674e..61778398af84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3785,13 +3785,6 @@ dependencies: "@types/node" "*" -"@types/fs-extra@^8.0.1": - version "8.1.2" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.2.tgz#7125cc2e4bdd9bd2fc83005ffdb1d0ba00cca61f" - integrity sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg== - dependencies: - "@types/node" "*" - "@types/fs-extra@^8.1.0": version "8.1.1" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.1.tgz#1e49f22d09aa46e19b51c0b013cb63d0d923a068" @@ -8204,7 +8197,7 @@ color@^3.0.0: color-convert "^1.9.1" color-string "^1.5.4" -colorette@^1.1.0, colorette@^1.2.1, colorette@^1.2.2: +colorette@^1.2.1, colorette@^1.2.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== @@ -12657,20 +12650,6 @@ globby@10.0.0: merge2 "^1.2.3" slash "^3.0.0" -globby@10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22" - integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A== - dependencies: - "@types/glob" "^7.1.1" - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.0.3" - glob "^7.1.3" - ignore "^5.1.1" - merge2 "^1.2.3" - slash "^3.0.0" - globby@11.1.0, globby@^11.0.1, globby@^11.0.2, globby@^11.0.3, globby@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -14165,11 +14144,6 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-plain-object@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b" - integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== - is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -21149,17 +21123,6 @@ rollup-plugin-cleanup@3.2.1: js-cleanup "^1.2.0" rollup-pluginutils "^2.8.2" -rollup-plugin-copy@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz#f1228a3ffb66ffad8606e2f3fb7ff23141ed3286" - integrity sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ== - dependencies: - "@types/fs-extra" "^8.0.1" - colorette "^1.1.0" - fs-extra "^8.1.0" - globby "10.0.1" - is-plain-object "^3.0.0" - rollup-plugin-license@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/rollup-plugin-license/-/rollup-plugin-license-2.6.1.tgz#20f15cc37950f362f8eefdc6e3a2e659d0cad9eb" From a9937863dae396eef173458fb79926739d0b3d33 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 14 Feb 2023 14:00:38 +0100 Subject: [PATCH 08/21] feat(otel): Convert exception otel events to sentry errors (#7165) --- .../opentelemetry-node/src/spanprocessor.ts | 49 ++++++++++++++++++- .../test/spanprocessor.test.ts | 40 +++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry-node/src/spanprocessor.ts b/packages/opentelemetry-node/src/spanprocessor.ts index f2ebcd6af12e..b96cc424fb0d 100644 --- a/packages/opentelemetry-node/src/spanprocessor.ts +++ b/packages/opentelemetry-node/src/spanprocessor.ts @@ -1,10 +1,11 @@ import type { Context } from '@opentelemetry/api'; import { trace } from '@opentelemetry/api'; import type { Span as OtelSpan, SpanProcessor as OtelSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; import { Transaction } from '@sentry/tracing'; import type { DynamicSamplingContext, Span as SentrySpan, TraceparentData, TransactionContext } from '@sentry/types'; -import { logger } from '@sentry/utils'; +import { isString, logger } from '@sentry/utils'; import { SENTRY_DYNAMIC_SAMPLING_CONTEXT_KEY, SENTRY_TRACE_PARENT_CONTEXT_KEY } from './constants'; import { isSentryRequestSpan } from './utils/is-sentry-request'; @@ -93,6 +94,12 @@ export class SentrySpanProcessor implements OtelSpanProcessor { * @inheritDoc */ public onEnd(otelSpan: OtelSpan): void { + const hub = getCurrentHub(); + if (!hub) { + __DEBUG_BUILD__ && logger.error('SentrySpanProcessor has triggered onEnd before a hub has been setup.'); + return; + } + const otelSpanId = otelSpan.spanContext().spanId; const sentrySpan = SENTRY_SPAN_PROCESSOR_MAP.get(otelSpanId); @@ -112,6 +119,46 @@ export class SentrySpanProcessor implements OtelSpanProcessor { return; } + otelSpan.events.forEach(event => { + if (event.name !== 'exception') { + return; + } + + const attributes = event.attributes; + if (!attributes) { + return; + } + + const message = attributes[SemanticAttributes.EXCEPTION_MESSAGE]; + const syntheticError = new Error(message as string | undefined); + + const stack = attributes[SemanticAttributes.EXCEPTION_STACKTRACE]; + if (isString(stack)) { + syntheticError.stack = stack; + } + + const type = attributes[SemanticAttributes.EXCEPTION_TYPE]; + if (isString(type)) { + syntheticError.name = type; + } + + hub.captureException(syntheticError, { + captureContext: { + contexts: { + otel: { + attributes: otelSpan.attributes, + resource: otelSpan.resource.attributes, + }, + trace: { + trace_id: otelSpan.spanContext().traceId, + span_id: otelSpan.spanContext().spanId, + parent_span_id: otelSpan.parentSpanId, + }, + }, + }, + }); + }); + if (sentrySpan instanceof Transaction) { updateTransactionWithOtelData(sentrySpan, otelSpan); } else { diff --git a/packages/opentelemetry-node/test/spanprocessor.test.ts b/packages/opentelemetry-node/test/spanprocessor.test.ts index d07c0b950f64..27a72a1943d9 100644 --- a/packages/opentelemetry-node/test/spanprocessor.test.ts +++ b/packages/opentelemetry-node/test/spanprocessor.test.ts @@ -750,6 +750,46 @@ describe('SentrySpanProcessor', () => { trace_id: otelSpan.spanContext().traceId, }); }); + + it('generates Sentry errors from opentelemetry span exception events', () => { + let sentryEvent: any; + let otelSpan: any; + + client = new NodeClient({ + ...DEFAULT_NODE_CLIENT_OPTIONS, + beforeSend: event => { + sentryEvent = event; + return null; + }, + }); + hub = new Hub(client); + makeMain(hub); + + const tracer = provider.getTracer('default'); + + tracer.startActiveSpan('GET /users', parentOtelSpan => { + tracer.startActiveSpan('SELECT * FROM users;', child => { + child.recordException(new Error('this is an otel error!')); + otelSpan = child as OtelSpan; + child.end(); + }); + + parentOtelSpan.end(); + }); + + expect(sentryEvent).toBeDefined(); + expect(sentryEvent.exception).toBeDefined(); + expect(sentryEvent.exception.values[0]).toEqual({ + mechanism: expect.any(Object), + type: 'Error', + value: 'this is an otel error!', + }); + expect(sentryEvent.contexts.trace).toEqual({ + parent_span_id: otelSpan.parentSpanId, + span_id: otelSpan.spanContext().spanId, + trace_id: otelSpan.spanContext().traceId, + }); + }); }); // OTEL expects a custom date format From 1921888b817b2168ceeaa618080fbae880cd4a23 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 14 Feb 2023 14:25:53 +0100 Subject: [PATCH 09/21] fix(nextjs): Fix faulty import in Next.js .d.ts (#7175) Co-authored-by: Abhijeet Prasad --- .../create-next-app/sentry.server.config.ts | 2 ++ packages/nextjs/src/index.types.ts | 17 +++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/e2e-tests/test-applications/create-next-app/sentry.server.config.ts b/packages/e2e-tests/test-applications/create-next-app/sentry.server.config.ts index e3d70f17e126..b5673c598d71 100644 --- a/packages/e2e-tests/test-applications/create-next-app/sentry.server.config.ts +++ b/packages/e2e-tests/test-applications/create-next-app/sentry.server.config.ts @@ -18,6 +18,8 @@ Sentry.init({ // Note: if you want to override the automatic release value, do not set a // `release` value here - use the environment variable `SENTRY_RELEASE`, so // that it will also get attached to your source maps + + integrations: [new Sentry.Integrations.LocalVariables()], }); Sentry.addGlobalEventProcessor(event => { diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index aca9f9d48211..0b115d213d5d 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -9,18 +9,19 @@ export * from './edge'; import type { Integration, Options, StackParser } from '@sentry/types'; -import type { BrowserOptions } from './client'; -import * as clientSdk from './client'; -import type { EdgeOptions } from './edge'; -import * as edgeSdk from './edge'; -import type { NodeOptions } from './server'; -import * as serverSdk from './server'; +import type * as clientSdk from './client'; +import type * as edgeSdk from './edge'; +import type * as serverSdk from './server'; /** Initializes Sentry Next.js SDK */ -export declare function init(options: Options | BrowserOptions | NodeOptions | EdgeOptions): void; +export declare function init( + options: Options | clientSdk.BrowserOptions | serverSdk.NodeOptions | edgeSdk.EdgeOptions, +): void; // We export a merged Integrations object so that users can (at least typing-wise) use all integrations everywhere. -export const Integrations = { ...clientSdk.Integrations, ...serverSdk.Integrations, ...edgeSdk.Integrations }; +export declare const Integrations: typeof clientSdk.Integrations & + typeof serverSdk.Integrations & + typeof edgeSdk.Integrations; export declare const defaultIntegrations: Integration[]; export declare const defaultStackParser: StackParser; From 1c9ed4ff4fd4c67f9018cfb129a88730ef5d5c85 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 15 Feb 2023 09:07:07 +0100 Subject: [PATCH 10/21] ref(replay): Improve logging for stopped replay (#7174) --- packages/replay/src/replay.ts | 17 +++++++++++++---- packages/replay/src/types.ts | 2 +- packages/replay/src/util/addEvent.ts | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index c162a26cd5da..413286af5e82 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -233,13 +233,22 @@ export class ReplayContainer implements ReplayContainerInterface { * Currently, this needs to be manually called (e.g. for tests). Sentry SDK * does not support a teardown */ - public stop(): void { + public stop(reason?: string): void { if (!this._isEnabled) { return; } try { - __DEBUG_BUILD__ && logger.log('[Replay] Stopping Replays'); + if (__DEBUG_BUILD__) { + const msg = `[Replay] Stopping Replay${reason ? ` triggered by ${reason}` : ''}`; + + // When `traceInternals` is enabled, we want to log this to the console + // Else, use the regular debug output + // eslint-disable-next-line + const log = this.getOptions()._experiments.traceInternals ? console.warn : logger.log; + log(msg); + } + this._isEnabled = false; this._removeListeners(); this.stopRecording(); @@ -426,7 +435,7 @@ export class ReplayContainer implements ReplayContainerInterface { this.session = session; if (!this.session.sampled) { - this.stop(); + this.stop('session unsampled'); return false; } @@ -810,7 +819,7 @@ export class ReplayContainer implements ReplayContainerInterface { // This means we retried 3 times and all of them failed, // or we ran into a problem we don't want to retry, like rate limiting. // In this case, we want to completely stop the replay - otherwise, we may get inconsistent segments - this.stop(); + this.stop('sendReplay'); const client = getCurrentHub().getClient(); diff --git a/packages/replay/src/types.ts b/packages/replay/src/types.ts index 27c878a3edba..174f1da240f8 100644 --- a/packages/replay/src/types.ts +++ b/packages/replay/src/types.ts @@ -293,7 +293,7 @@ export interface ReplayContainer { isPaused(): boolean; getContext(): InternalEventContext; start(): void; - stop(): void; + stop(reason?: string): void; pause(): void; resume(): void; startRecording(): void; diff --git a/packages/replay/src/util/addEvent.ts b/packages/replay/src/util/addEvent.ts index e243ce11b56b..5cf351fd6f9c 100644 --- a/packages/replay/src/util/addEvent.ts +++ b/packages/replay/src/util/addEvent.ts @@ -46,7 +46,7 @@ export async function addEvent( return await replay.eventBuffer.addEvent(event, isCheckout); } catch (error) { __DEBUG_BUILD__ && logger.error(error); - replay.stop(); + replay.stop('addEvent'); const client = getCurrentHub().getClient(); From 4c07d0218b234a8f19ec45d6189ad93f6a47f4cf Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 15 Feb 2023 11:37:08 +0100 Subject: [PATCH 11/21] fix(otel): Make otel.kind be a string (#7182) --- packages/opentelemetry-node/src/spanprocessor.ts | 4 ++-- packages/opentelemetry-node/test/spanprocessor.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-node/src/spanprocessor.ts b/packages/opentelemetry-node/src/spanprocessor.ts index b96cc424fb0d..08d14e9fa671 100644 --- a/packages/opentelemetry-node/src/spanprocessor.ts +++ b/packages/opentelemetry-node/src/spanprocessor.ts @@ -1,5 +1,5 @@ import type { Context } from '@opentelemetry/api'; -import { trace } from '@opentelemetry/api'; +import { SpanKind, trace } from '@opentelemetry/api'; import type { Span as OtelSpan, SpanProcessor as OtelSpanProcessor } from '@opentelemetry/sdk-trace-base'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; @@ -223,7 +223,7 @@ function updateSpanWithOtelData(sentrySpan: SentrySpan, otelSpan: OtelSpan): voi const { attributes, kind } = otelSpan; sentrySpan.setStatus(mapOtelStatus(otelSpan)); - sentrySpan.setData('otel.kind', kind.valueOf()); + sentrySpan.setData('otel.kind', SpanKind[kind]); Object.keys(attributes).forEach(prop => { const value = attributes[prop]; diff --git a/packages/opentelemetry-node/test/spanprocessor.test.ts b/packages/opentelemetry-node/test/spanprocessor.test.ts index 27a72a1943d9..4e3c975d00cf 100644 --- a/packages/opentelemetry-node/test/spanprocessor.test.ts +++ b/packages/opentelemetry-node/test/spanprocessor.test.ts @@ -221,7 +221,7 @@ describe('SentrySpanProcessor', () => { child.end(); expect(sentrySpan?.data).toEqual({ - 'otel.kind': 0, + 'otel.kind': 'INTERNAL', 'test-attribute': 'test-value', 'test-attribute-2': [1, 2, 3], 'test-attribute-3': 0, From 5359ba9cee969d46508ead6c34d92bd738e324bc Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 16 Feb 2023 12:06:09 +0100 Subject: [PATCH 12/21] feat(tracing|core): Remove transaction name change recording (#7197) --- packages/core/src/baseclient.ts | 9 -- packages/core/test/lib/base.test.ts | 20 --- .../test/server/errorServerSideProps.test.ts | 2 - .../test/server/tracing200.test.ts | 2 - .../test/server/tracing500.test.ts | 2 - .../test/server/tracingHttp.test.ts | 2 - .../tracingServerGetInitialProps.test.ts | 2 - .../tracingServerGetServerSideProps.test.ts | 2 - ...ServerSidePropsCustomPageExtension.test.ts | 2 - .../test/server/tracingWithSentryAPI.test.ts | 2 - .../suites/express/tracing/test.ts | 24 --- packages/node/src/integrations/http.ts | 5 - packages/node/test/integrations/http.test.ts | 14 -- .../integration/test/server/loader.test.ts | 2 - packages/replay/test/fixtures/transaction.ts | 4 - packages/tracing/src/browser/request.ts | 4 - packages/tracing/src/transaction.ts | 17 +-- packages/tracing/test/browser/request.test.ts | 26 ---- packages/tracing/test/span.test.ts | 2 - packages/tracing/test/transaction.test.ts | 138 ------------------ packages/types/src/event.ts | 4 +- packages/types/src/index.ts | 1 - packages/types/src/transaction.ts | 23 --- 23 files changed, 2 insertions(+), 307 deletions(-) diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 09200e30c503..7a90b07925f6 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -545,15 +545,6 @@ export abstract class BaseClient implements Client { processedEvent.transaction_info = { ...transactionInfo, source, - changes: [ - ...transactionInfo.changes, - { - source, - // use the same timestamp as the processed event. - timestamp: processedEvent.timestamp as number, - propagations: transactionInfo.propagations, - }, - ], }; } diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 2925c6e213c7..68e24ec58afa 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -1368,8 +1368,6 @@ describe('BaseClient', () => { type: 'transaction', transaction_info: { source: 'url', - changes: [], - propagations: 3, }, }; @@ -1383,14 +1381,6 @@ describe('BaseClient', () => { expect(TestClient.instance!.event!.transaction).toEqual('/adopt/dont/shop'); expect(TestClient.instance!.event!.transaction_info).toEqual({ source: 'custom', - changes: [ - { - propagations: 3, - source: 'custom', - timestamp: expect.any(Number), - }, - ], - propagations: 3, }); }); @@ -1407,8 +1397,6 @@ describe('BaseClient', () => { type: 'transaction', transaction_info: { source: 'url', - changes: [], - propagations: 3, }, }); @@ -1416,14 +1404,6 @@ describe('BaseClient', () => { expect(TestClient.instance!.event!.transaction).toBe('/adopt/dont/shop'); expect(TestClient.instance!.event!.transaction_info).toEqual({ source: 'custom', - changes: [ - { - propagations: 3, - source: 'custom', - timestamp: expect.any(Number), - }, - ], - propagations: 3, }); }); diff --git a/packages/nextjs/test/integration/test/server/errorServerSideProps.test.ts b/packages/nextjs/test/integration/test/server/errorServerSideProps.test.ts index fe455719d760..bd64fd7247bc 100644 --- a/packages/nextjs/test/integration/test/server/errorServerSideProps.test.ts +++ b/packages/nextjs/test/integration/test/server/errorServerSideProps.test.ts @@ -48,8 +48,6 @@ describe('Error Server-side Props', () => { transaction: '/withErrorServerSideProps', transaction_info: { source: 'route', - changes: [], - propagations: 0, }, type: 'transaction', request: { diff --git a/packages/nextjs/test/integration/test/server/tracing200.test.ts b/packages/nextjs/test/integration/test/server/tracing200.test.ts index f26a2feffb43..f68279138558 100644 --- a/packages/nextjs/test/integration/test/server/tracing200.test.ts +++ b/packages/nextjs/test/integration/test/server/tracing200.test.ts @@ -21,8 +21,6 @@ describe('Tracing 200', () => { transaction: 'GET /api/users', transaction_info: { source: 'route', - changes: [], - propagations: 0, }, type: 'transaction', request: { diff --git a/packages/nextjs/test/integration/test/server/tracing500.test.ts b/packages/nextjs/test/integration/test/server/tracing500.test.ts index a1a2fb24edff..79b23dcfb786 100644 --- a/packages/nextjs/test/integration/test/server/tracing500.test.ts +++ b/packages/nextjs/test/integration/test/server/tracing500.test.ts @@ -21,8 +21,6 @@ describe('Tracing 500', () => { transaction: 'GET /api/broken', transaction_info: { source: 'route', - changes: [], - propagations: 0, }, type: 'transaction', request: { diff --git a/packages/nextjs/test/integration/test/server/tracingHttp.test.ts b/packages/nextjs/test/integration/test/server/tracingHttp.test.ts index c09e64a13cd8..912c54e0996b 100644 --- a/packages/nextjs/test/integration/test/server/tracingHttp.test.ts +++ b/packages/nextjs/test/integration/test/server/tracingHttp.test.ts @@ -33,8 +33,6 @@ describe('Tracing HTTP', () => { transaction: 'GET /api/http', transaction_info: { source: 'route', - changes: [], - propagations: 1, }, type: 'transaction', request: { diff --git a/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.test.ts b/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.test.ts index 530a9b1e9b63..9ff20342bf12 100644 --- a/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.test.ts +++ b/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.test.ts @@ -20,8 +20,6 @@ describe('getInitialProps', () => { transaction: '/[id]/withInitialProps', transaction_info: { source: 'route', - changes: [], - propagations: 0, }, type: 'transaction', request: { diff --git a/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.test.ts b/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.test.ts index cfcad2f59dfc..361f06acffa2 100644 --- a/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.test.ts +++ b/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.test.ts @@ -20,8 +20,6 @@ describe('getServerSideProps', () => { transaction: '/[id]/withServerSideProps', transaction_info: { source: 'route', - changes: [], - propagations: 0, }, type: 'transaction', request: { diff --git a/packages/nextjs/test/integration/test/server/tracingServerGetServerSidePropsCustomPageExtension.test.ts b/packages/nextjs/test/integration/test/server/tracingServerGetServerSidePropsCustomPageExtension.test.ts index 896123e9ce9e..4f257da0b77d 100644 --- a/packages/nextjs/test/integration/test/server/tracingServerGetServerSidePropsCustomPageExtension.test.ts +++ b/packages/nextjs/test/integration/test/server/tracingServerGetServerSidePropsCustomPageExtension.test.ts @@ -20,8 +20,6 @@ describe('tracingServerGetServerSidePropsCustomPageExtension', () => { transaction: '/customPageExtension', transaction_info: { source: 'route', - changes: [], - propagations: 0, }, type: 'transaction', request: { diff --git a/packages/nextjs/test/integration/test/server/tracingWithSentryAPI.test.ts b/packages/nextjs/test/integration/test/server/tracingWithSentryAPI.test.ts index c0f91f20c98e..73302842e15e 100644 --- a/packages/nextjs/test/integration/test/server/tracingWithSentryAPI.test.ts +++ b/packages/nextjs/test/integration/test/server/tracingWithSentryAPI.test.ts @@ -54,8 +54,6 @@ describe('getServerSideProps', () => { transaction: `GET ${transactionName}`, transaction_info: { source: 'route', - changes: [], - propagations: 0, }, type: 'transaction', request: { diff --git a/packages/node-integration-tests/suites/express/tracing/test.ts b/packages/node-integration-tests/suites/express/tracing/test.ts index 0ecd2e403aeb..ae9d619c48cc 100644 --- a/packages/node-integration-tests/suites/express/tracing/test.ts +++ b/packages/node-integration-tests/suites/express/tracing/test.ts @@ -38,14 +38,6 @@ test('should set a correct transaction name for routes specified in RegEx', asyn transaction: 'GET /\\/test\\/regex/', transaction_info: { source: 'route', - changes: [ - { - propagations: 0, - source: 'url', - timestamp: expect.any(Number), - }, - ], - propagations: 0, }, contexts: { trace: { @@ -74,14 +66,6 @@ test.each([['array1'], ['array5']])( transaction: 'GET /test/array1,/\\/test\\/array[2-9]', transaction_info: { source: 'route', - changes: [ - { - propagations: 0, - source: 'url', - timestamp: expect.any(Number), - }, - ], - propagations: 0, }, contexts: { trace: { @@ -118,14 +102,6 @@ test.each([ transaction: 'GET /test/arr/:id,/\\/test\\/arr[0-9]*\\/required(path)?(\\/optionalPath)?\\/(lastParam)?', transaction_info: { source: 'route', - changes: [ - { - propagations: 0, - source: 'url', - timestamp: expect.any(Number), - }, - ], - propagations: 0, }, contexts: { trace: { diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index 99060b8c6f64..ea7a9eae173d 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -243,11 +243,6 @@ function _createWrappedRequestMethodFactory( `[Tracing] Not adding sentry-trace header to outgoing request (${requestUrl}) due to mismatching tracePropagationTargets option.`, ); } - - const transaction = parentSpan.transaction; - if (transaction) { - transaction.metadata.propagations++; - } } } diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index ab2370ec19c6..446aa4ec0f82 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -175,20 +175,6 @@ describe('tracing', () => { expect(baggage).not.toBeDefined(); }); - it('records outgoing propagations on the transaction', () => { - nock('http://dogs.are.great').get('/').reply(200); - - const transaction = createTransactionOnScope(); - - expect(transaction.metadata.propagations).toBe(0); - - http.get('http://dogs.are.great/'); - expect(transaction.metadata.propagations).toBe(1); - - http.get('http://dogs.are.great/'); - expect(transaction.metadata.propagations).toBe(2); - }); - it("doesn't attach when using otel instrumenter", () => { const loggerLogSpy = jest.spyOn(logger, 'log'); diff --git a/packages/remix/test/integration/test/server/loader.test.ts b/packages/remix/test/integration/test/server/loader.test.ts index f2cb90962d88..907875949f30 100644 --- a/packages/remix/test/integration/test/server/loader.test.ts +++ b/packages/remix/test/integration/test/server/loader.test.ts @@ -55,8 +55,6 @@ describe.each(['builtin', 'express'])('Remix API Loaders with adapter = %s', ada transaction: 'routes/loader-json-response/$id', transaction_info: { source: 'route', - changes: [], - propagations: 0, }, spans: [ { diff --git a/packages/replay/test/fixtures/transaction.ts b/packages/replay/test/fixtures/transaction.ts index e1d686eadd01..2899740c5e52 100644 --- a/packages/replay/test/fixtures/transaction.ts +++ b/packages/replay/test/fixtures/transaction.ts @@ -225,14 +225,10 @@ export function Transaction(obj?: Partial): any { "[Tracing] Starting 'resource.other' span on transaction '/organizations/:orgId/replays/:replaySlug/' (b44b173b1c74a782).", }, }, - changes: [], - propagations: 2, sampleRate: 1, }, // }}} transaction_info: { source: 'route', - changes: [], - propagations: 2, }, measurements: { longTaskCount: { diff --git a/packages/tracing/src/browser/request.ts b/packages/tracing/src/browser/request.ts index c8b9411e7c3f..57d0178509ef 100644 --- a/packages/tracing/src/browser/request.ts +++ b/packages/tracing/src/browser/request.ts @@ -219,8 +219,6 @@ export function fetchCallback( span, options, ); - - activeTransaction.metadata.propagations++; } } } @@ -356,8 +354,6 @@ export function xhrCallback( // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader handlerData.xhr.setRequestHeader(BAGGAGE_HEADER_NAME, sentryBaggageHeader); } - - activeTransaction.metadata.propagations++; } catch (_) { // Error: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED. } diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 5b32612302ee..627478da744e 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -11,7 +11,7 @@ import type { TransactionContext, TransactionMetadata, } from '@sentry/types'; -import { dropUndefinedKeys, logger, timestampInSeconds } from '@sentry/utils'; +import { dropUndefinedKeys, logger } from '@sentry/utils'; import { Span as SpanClass, SpanRecorder } from './span'; @@ -52,8 +52,6 @@ export class Transaction extends SpanClass implements TransactionInterface { source: 'custom', ...transactionContext.metadata, spanMetadata: {}, - changes: [], - propagations: 0, }; this._trimEnd = transactionContext.trimEnd; @@ -84,17 +82,6 @@ export class Transaction extends SpanClass implements TransactionInterface { * JSDoc */ public setName(name: string, source: TransactionMetadata['source'] = 'custom'): void { - // `source` could change without the name changing if we discover that an unparameterized route is actually - // parameterized by virtue of having no parameters in its path - if (name !== this.name || source !== this.metadata.source) { - this.metadata.changes.push({ - // log previous source - source: this.metadata.source, - timestamp: timestampInSeconds(), - propagations: this.metadata.propagations, - }); - } - this._name = name; this.metadata.source = source; } @@ -197,8 +184,6 @@ export class Transaction extends SpanClass implements TransactionInterface { ...(metadata.source && { transaction_info: { source: metadata.source, - changes: metadata.changes, - propagations: metadata.propagations, }, }), }; diff --git a/packages/tracing/test/browser/request.test.ts b/packages/tracing/test/browser/request.test.ts index e2d60aa9edb2..3dd107d708dc 100644 --- a/packages/tracing/test/browser/request.test.ts +++ b/packages/tracing/test/browser/request.test.ts @@ -213,19 +213,6 @@ describe('callbacks', () => { expect(newSpan).toBeUndefined(); }); - - it('records outgoing propogations', () => { - const firstReqData = { ...fetchHandlerData }; - const secondReqData = { ...fetchHandlerData }; - - expect(transaction.metadata.propagations).toBe(0); - - fetchCallback(firstReqData, alwaysCreateSpan, alwaysAttachHeaders, {}); - expect(transaction.metadata.propagations).toBe(1); - - fetchCallback(secondReqData, alwaysCreateSpan, alwaysAttachHeaders, {}); - expect(transaction.metadata.propagations).toBe(2); - }); }); describe('xhrCallback()', () => { @@ -370,19 +357,6 @@ describe('callbacks', () => { expect(newSpan).toBeUndefined(); }); - - it('records outgoing propogations', () => { - const firstReqData = { ...xhrHandlerData }; - const secondReqData = { ...xhrHandlerData }; - - expect(transaction.metadata.propagations).toBe(0); - - xhrCallback(firstReqData, alwaysCreateSpan, alwaysAttachHeaders, {}); - expect(transaction.metadata.propagations).toBe(1); - - xhrCallback(secondReqData, alwaysCreateSpan, alwaysAttachHeaders, {}); - expect(transaction.metadata.propagations).toBe(2); - }); }); }); diff --git a/packages/tracing/test/span.test.ts b/packages/tracing/test/span.test.ts index 6de0d79b7125..96a71811eec9 100644 --- a/packages/tracing/test/span.test.ts +++ b/packages/tracing/test/span.test.ts @@ -508,8 +508,6 @@ describe('Span', () => { expect.objectContaining({ transaction_info: { source: 'url', - changes: [], - propagations: 0, }, }), ); diff --git a/packages/tracing/test/transaction.test.ts b/packages/tracing/test/transaction.test.ts index 6fd36701a65b..fb49dcc4d909 100644 --- a/packages/tracing/test/transaction.test.ts +++ b/packages/tracing/test/transaction.test.ts @@ -44,104 +44,6 @@ describe('`Transaction` class', () => { expect(transaction.instrumenter).toEqual('otel'); }); - it('updates transaction name changes with correct variables needed', () => { - const transaction = new Transaction({ name: 'dogpark', metadata: { source: 'url' } }); - expect(transaction.metadata.changes).toEqual([]); - - transaction.name = 'ballpit'; - - expect(transaction.metadata.changes).toEqual([ - { - source: 'url', - timestamp: expect.any(Number), - propagations: 0, - }, - ]); - - transaction.metadata.propagations += 3; - - expect(transaction.metadata.changes).toEqual([ - { - source: 'url', - timestamp: expect.any(Number), - propagations: 0, - }, - ]); - - transaction.name = 'playground'; - - expect(transaction.metadata.changes).toEqual([ - { - source: 'url', - timestamp: expect.any(Number), - propagations: 0, - }, - { - source: 'custom', - timestamp: expect.any(Number), - propagations: 3, - }, - ]); - - // Only change `source` - transaction.setName('playground', 'route'); - - expect(transaction.metadata.changes).toEqual([ - { - source: 'url', - timestamp: expect.any(Number), - propagations: 0, - }, - { - source: 'custom', - timestamp: expect.any(Number), - propagations: 3, - }, - { - source: 'custom', - timestamp: expect.any(Number), - propagations: 3, - }, - ]); - }); - - it("doesn't update transaction name changes if no change in data", () => { - const transaction = new Transaction({ name: 'dogpark' }); - expect(transaction.metadata.changes).toEqual([]); - - transaction.name = 'ballpit'; - - expect(transaction.metadata.changes).toEqual([ - { - source: 'custom', - timestamp: expect.any(Number), - propagations: 0, - }, - ]); - - transaction.name = 'ballpit'; - - // Still only one entry - expect(transaction.metadata.changes).toEqual([ - { - source: 'custom', - timestamp: expect.any(Number), - propagations: 0, - }, - ]); - - transaction.setName('ballpit', 'custom'); - - // Still only one entry - expect(transaction.metadata.changes).toEqual([ - { - source: 'custom', - timestamp: expect.any(Number), - propagations: 0, - }, - ]); - }); - describe('`setName` method', () => { it("sets source to `'custom'` if no source provided", () => { const transaction = new Transaction({ name: 'dogpark' }); @@ -158,46 +60,6 @@ describe('`Transaction` class', () => { expect(transaction.name).toEqual('ballpit'); expect(transaction.metadata.source).toEqual('route'); }); - - it('updates transaction name changes with correct variables needed', () => { - const transaction = new Transaction({ name: 'dogpark', metadata: { source: 'url' } }); - expect(transaction.metadata.changes).toEqual([]); - - transaction.name = 'ballpit'; - - expect(transaction.metadata.changes).toEqual([ - { - source: 'url', - timestamp: expect.any(Number), - propagations: 0, - }, - ]); - - transaction.metadata.propagations += 3; - - expect(transaction.metadata.changes).toEqual([ - { - source: 'url', - timestamp: expect.any(Number), - propagations: 0, - }, - ]); - - transaction.setName('playground', 'task'); - - expect(transaction.metadata.changes).toEqual([ - { - source: 'url', - timestamp: expect.any(Number), - propagations: 0, - }, - { - source: 'custom', - timestamp: expect.any(Number), - propagations: 3, - }, - ]); - }); }); }); diff --git a/packages/types/src/event.ts b/packages/types/src/event.ts index 7c00bcef4718..7d21456d8044 100644 --- a/packages/types/src/event.ts +++ b/packages/types/src/event.ts @@ -12,7 +12,7 @@ import type { SdkInfo } from './sdkinfo'; import type { Severity, SeverityLevel } from './severity'; import type { Span } from './span'; import type { Thread } from './thread'; -import type { TransactionNameChange, TransactionSource } from './transaction'; +import type { TransactionSource } from './transaction'; import type { User } from './user'; /** JSDoc */ @@ -50,8 +50,6 @@ export interface Event { sdkProcessingMetadata?: { [key: string]: any }; transaction_info?: { source: TransactionSource; - changes: TransactionNameChange[]; - propagations: number; }; threads?: { values: Thread[]; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 0b52fac74e02..da57a0c6a97e 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -73,7 +73,6 @@ export type { TransactionContext, TransactionMetadata, TransactionSource, - TransactionNameChange, } from './transaction'; export type { DurationUnit, diff --git a/packages/types/src/transaction.ts b/packages/types/src/transaction.ts index e60132a8e550..42d266abbda7 100644 --- a/packages/types/src/transaction.ts +++ b/packages/types/src/transaction.ts @@ -176,12 +176,6 @@ export interface TransactionMetadata { /** Metadata for the transaction's spans, keyed by spanId */ spanMetadata: { [spanId: string]: { [key: string]: unknown } }; - - /** Metadata representing information about transaction name changes */ - changes: TransactionNameChange[]; - - /** The total number of propagations that happened */ - propagations: number; } /** @@ -201,20 +195,3 @@ export type TransactionSource = | 'component' /** Name of a background task (e.g. a Celery task) */ | 'task'; - -/** - * Object representing metadata about when a transaction name was changed. - */ -export interface TransactionNameChange { - /** - * Unix timestamp when the name was changed. Same type as the start and - * end timestamps of a transaction and span. - */ - timestamp: number; - - /** Previous source before transaction name change */ - source: TransactionSource; - - /** Number of propagations since start of transaction */ - propagations: number; -} From 79babe9cf4205122acb3bb676cdf82798e6f092a Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 16 Feb 2023 12:23:00 +0100 Subject: [PATCH 13/21] fix(react): Make fallback render types more accurate (#7198) --- packages/react/src/errorboundary.tsx | 31 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/react/src/errorboundary.tsx b/packages/react/src/errorboundary.tsx index 7e68416a67ce..1553028195a2 100644 --- a/packages/react/src/errorboundary.tsx +++ b/packages/react/src/errorboundary.tsx @@ -13,8 +13,8 @@ export const UNKNOWN_COMPONENT = 'unknown'; export type FallbackRender = (errorData: { error: Error; - componentStack: string | null; - eventId: string | null; + componentStack: string; + eventId: string; resetError(): void; }) => React.ReactElement; @@ -48,11 +48,17 @@ export type ErrorBoundaryProps = { beforeCapture?(scope: Scope, error: Error | null, componentStack: string | null): void; }; -type ErrorBoundaryState = { - componentStack: React.ErrorInfo['componentStack'] | null; - error: Error | null; - eventId: string | null; -}; +type ErrorBoundaryState = + | { + componentStack: null; + error: null; + eventId: null; + } + | { + componentStack: React.ErrorInfo['componentStack']; + error: Error; + eventId: string; + }; const INITIAL_STATE = { componentStack: null, @@ -133,12 +139,17 @@ class ErrorBoundary extends React.Component Date: Thu, 16 Feb 2023 12:06:13 +0000 Subject: [PATCH 14/21] feat(tracing): Support Apollo/GraphQL with NestJS (#7194) Co-authored-by: Abhijeet Prasad --- .../tracing/src/integrations/node/apollo.ts | 154 +++++++++++++----- .../test/integrations/apollo-nestjs.test.ts | 120 ++++++++++++++ 2 files changed, 230 insertions(+), 44 deletions(-) create mode 100644 packages/tracing/test/integrations/apollo-nestjs.test.ts diff --git a/packages/tracing/src/integrations/node/apollo.ts b/packages/tracing/src/integrations/node/apollo.ts index 41b136abff42..3a076444af5e 100644 --- a/packages/tracing/src/integrations/node/apollo.ts +++ b/packages/tracing/src/integrations/node/apollo.ts @@ -4,6 +4,10 @@ import { arrayify, fill, isThenable, loadModule, logger } from '@sentry/utils'; import { shouldDisableAutoInstrumentation } from './utils/node-utils'; +interface ApolloOptions { + useNestjs?: boolean; +} + type ApolloResolverGroup = { [key: string]: () => unknown; }; @@ -24,6 +28,19 @@ export class Apollo implements Integration { */ public name: string = Apollo.id; + private readonly _useNest: boolean; + + /** + * @inheritDoc + */ + public constructor( + options: ApolloOptions = { + useNestjs: false, + }, + ) { + this._useNest = !!options.useNestjs; + } + /** * @inheritDoc */ @@ -33,62 +50,111 @@ export class Apollo implements Integration { return; } - const pkg = loadModule<{ - ApolloServerBase: { - prototype: { - constructSchema: () => unknown; + if (this._useNest) { + const pkg = loadModule<{ + GraphQLFactory: { + prototype: { + create: (resolvers: ApolloModelResolvers[]) => unknown; + }; }; - }; - }>('apollo-server-core'); + }>('@nestjs/graphql'); - if (!pkg) { - __DEBUG_BUILD__ && logger.error('Apollo Integration was unable to require apollo-server-core package.'); - return; - } + if (!pkg) { + __DEBUG_BUILD__ && logger.error('Apollo-NestJS Integration was unable to require @nestjs/graphql package.'); + return; + } + + /** + * Iterate over resolvers of NestJS ResolversExplorerService before schemas are constructed. + */ + fill( + pkg.GraphQLFactory.prototype, + 'mergeWithSchema', + function (orig: (this: unknown, ...args: unknown[]) => unknown) { + return function ( + this: { resolversExplorerService: { explore: () => ApolloModelResolvers[] } }, + ...args: unknown[] + ) { + fill(this.resolversExplorerService, 'explore', function (orig: () => ApolloModelResolvers[]) { + return function (this: unknown) { + const resolvers = arrayify(orig.call(this)); + + const instrumentedResolvers = instrumentResolvers(resolvers, getCurrentHub); + + return instrumentedResolvers; + }; + }); + + return orig.call(this, ...args); + }; + }, + ); + } else { + const pkg = loadModule<{ + ApolloServerBase: { + prototype: { + constructSchema: (config: unknown) => unknown; + }; + }; + }>('apollo-server-core'); + + if (!pkg) { + __DEBUG_BUILD__ && logger.error('Apollo Integration was unable to require apollo-server-core package.'); + return; + } + + /** + * Iterate over resolvers of the ApolloServer instance before schemas are constructed. + */ + fill(pkg.ApolloServerBase.prototype, 'constructSchema', function (orig: (config: unknown) => unknown) { + return function (this: { + config: { resolvers?: ApolloModelResolvers[]; schema?: unknown; modules?: unknown }; + }) { + if (!this.config.resolvers) { + if (__DEBUG_BUILD__) { + if (this.config.schema) { + logger.warn( + 'Apollo integration is not able to trace `ApolloServer` instances constructed via `schema` property.' + + 'If you are using NestJS with Apollo, please use `Sentry.Integrations.Apollo({ useNestjs: true })` instead.', + ); + logger.warn(); + } else if (this.config.modules) { + logger.warn( + 'Apollo integration is not able to trace `ApolloServer` instances constructed via `modules` property.', + ); + } - /** - * Iterate over resolvers of the ApolloServer instance before schemas are constructed. - */ - fill(pkg.ApolloServerBase.prototype, 'constructSchema', function (orig: () => unknown) { - return function (this: { config: { resolvers?: ApolloModelResolvers[]; schema?: unknown; modules?: unknown } }) { - if (!this.config.resolvers) { - if (__DEBUG_BUILD__) { - if (this.config.schema) { - logger.warn( - 'Apollo integration is not able to trace `ApolloServer` instances constructed via `schema` property.', - ); - } else if (this.config.modules) { - logger.warn( - 'Apollo integration is not able to trace `ApolloServer` instances constructed via `modules` property.', - ); + logger.error('Skipping tracing as no resolvers found on the `ApolloServer` instance.'); } - logger.error('Skipping tracing as no resolvers found on the `ApolloServer` instance.'); + return orig.call(this); } - return orig.call(this); - } + const resolvers = arrayify(this.config.resolvers); - const resolvers = arrayify(this.config.resolvers); - - this.config.resolvers = resolvers.map(model => { - Object.keys(model).forEach(resolverGroupName => { - Object.keys(model[resolverGroupName]).forEach(resolverName => { - if (typeof model[resolverGroupName][resolverName] !== 'function') { - return; - } + this.config.resolvers = instrumentResolvers(resolvers, getCurrentHub); - wrapResolver(model, resolverGroupName, resolverName, getCurrentHub); - }); - }); + return orig.call(this); + }; + }); + } + } +} - return model; - }); +function instrumentResolvers(resolvers: ApolloModelResolvers[], getCurrentHub: () => Hub): ApolloModelResolvers[] { + return resolvers.map(model => { + Object.keys(model).forEach(resolverGroupName => { + Object.keys(model[resolverGroupName]).forEach(resolverName => { + if (typeof model[resolverGroupName][resolverName] !== 'function') { + return; + } - return orig.call(this); - }; + wrapResolver(model, resolverGroupName, resolverName, getCurrentHub); + }); }); - } + + return model; + }); } /** diff --git a/packages/tracing/test/integrations/apollo-nestjs.test.ts b/packages/tracing/test/integrations/apollo-nestjs.test.ts new file mode 100644 index 000000000000..117cfd6ab704 --- /dev/null +++ b/packages/tracing/test/integrations/apollo-nestjs.test.ts @@ -0,0 +1,120 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +import { Hub, Scope } from '@sentry/core'; +import { logger } from '@sentry/utils'; + +import { Apollo } from '../../src/integrations/node/apollo'; +import { Span } from '../../src/span'; +import { getTestClient } from '../testutils'; + +type ApolloResolverGroup = { + [key: string]: () => unknown; +}; + +type ApolloModelResolvers = { + [key: string]: ApolloResolverGroup; +}; + +class GraphQLFactory { + _resolvers: ApolloModelResolvers[]; + resolversExplorerService = { + explore: () => this._resolvers, + }; + constructor() { + this._resolvers = [ + { + Query: { + res_1(..._args: unknown[]) { + return 'foo'; + }, + }, + Mutation: { + res_2(..._args: unknown[]) { + return 'bar'; + }, + }, + }, + ]; + + this.mergeWithSchema(); + } + + public mergeWithSchema(..._args: unknown[]) { + return this.resolversExplorerService.explore(); + } +} + +// mock for @nestjs/graphql package +jest.mock('@sentry/utils', () => { + const actual = jest.requireActual('@sentry/utils'); + return { + ...actual, + loadModule() { + return { + GraphQLFactory, + }; + }, + }; +}); + +describe('setupOnce', () => { + let scope = new Scope(); + let parentSpan: Span; + let childSpan: Span; + let GraphQLFactoryInstance: GraphQLFactory; + + beforeAll(() => { + new Apollo({ + useNestjs: true, + }).setupOnce( + () => undefined, + () => new Hub(undefined, scope), + ); + + GraphQLFactoryInstance = new GraphQLFactory(); + }); + + beforeEach(() => { + scope = new Scope(); + parentSpan = new Span(); + childSpan = parentSpan.startChild(); + jest.spyOn(scope, 'getSpan').mockReturnValueOnce(parentSpan); + jest.spyOn(scope, 'setSpan'); + jest.spyOn(parentSpan, 'startChild').mockReturnValueOnce(childSpan); + jest.spyOn(childSpan, 'finish'); + }); + + it('should wrap a simple resolver', () => { + GraphQLFactoryInstance._resolvers[0]?.['Query']?.['res_1']?.(); + expect(scope.getSpan).toBeCalled(); + expect(parentSpan.startChild).toBeCalledWith({ + description: 'Query.res_1', + op: 'graphql.resolve', + }); + expect(childSpan.finish).toBeCalled(); + }); + + it('should wrap another simple resolver', () => { + GraphQLFactoryInstance._resolvers[0]?.['Mutation']?.['res_2']?.(); + expect(scope.getSpan).toBeCalled(); + expect(parentSpan.startChild).toBeCalledWith({ + description: 'Mutation.res_2', + op: 'graphql.resolve', + }); + expect(childSpan.finish).toBeCalled(); + }); + + it("doesn't attach when using otel instrumenter", () => { + const loggerLogSpy = jest.spyOn(logger, 'log'); + + const client = getTestClient({ instrumenter: 'otel' }); + const hub = new Hub(client); + + const integration = new Apollo({ useNestjs: true }); + integration.setupOnce( + () => {}, + () => hub, + ); + + expect(loggerLogSpy).toBeCalledWith('Apollo Integration is skipped because of instrumenter configuration.'); + }); +}); From 5c4af0c0853aa91c0a49f4e80c7d0f924675eecd Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 16 Feb 2023 15:50:33 +0100 Subject: [PATCH 15/21] fix(core): Skip empty integrations (#7204) In the case when a user adds an empty integration, we want to handle this more gracefully instead of erroring out. --- packages/core/src/integration.ts | 5 ++++- packages/core/test/lib/base.test.ts | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/core/src/integration.ts b/packages/core/src/integration.ts index 77d232b00987..cc7c977bb5d9 100644 --- a/packages/core/src/integration.ts +++ b/packages/core/src/integration.ts @@ -88,7 +88,10 @@ export function setupIntegrations(integrations: Integration[]): IntegrationIndex const integrationIndex: IntegrationIndex = {}; integrations.forEach(integration => { - setupIntegration(integration, integrationIndex); + // guard against empty provided integrations + if (integration) { + setupIntegration(integration, integrationIndex); + } }); return integrationIndex; diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 68e24ec58afa..d7382ceeeacb 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -697,6 +697,22 @@ describe('BaseClient', () => { ); }); + test('skips empty integrations', () => { + const options = getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + // @ts-ignore we want to force invalid integrations here + integrations: [new TestIntegration(), null, undefined], + }); + const client = new TestClient(options); + client.setupIntegrations(); + + client.captureEvent({ message: 'message' }); + + expect(TestClient.instance!.event!.sdk).toEqual({ + integrations: ['TestIntegration'], + }); + }); + test('normalizes event with default depth of 3', () => { expect.assertions(1); From 68655e3c1009488c28596dde2aa1677d4d9c8f02 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Thu, 16 Feb 2023 15:54:19 +0100 Subject: [PATCH 16/21] feat(integrations): Deprecate `Offline` integration (#7063) --- packages/integrations/src/index.ts | 1 + packages/integrations/src/offline.ts | 4 ++++ packages/integrations/test/offline.test.ts | 1 + 3 files changed, 6 insertions(+) diff --git a/packages/integrations/src/index.ts b/packages/integrations/src/index.ts index 2f3708075ac1..372a730e3a5c 100644 --- a/packages/integrations/src/index.ts +++ b/packages/integrations/src/index.ts @@ -2,6 +2,7 @@ export { CaptureConsole } from './captureconsole'; export { Debug } from './debug'; export { Dedupe } from './dedupe'; export { ExtraErrorData } from './extraerrordata'; +// eslint-disable-next-line deprecation/deprecation export { Offline } from './offline'; export { ReportingObserver } from './reportingobserver'; export { RewriteFrames } from './rewriteframes'; diff --git a/packages/integrations/src/offline.ts b/packages/integrations/src/offline.ts index e20ea96d8e14..f48856023848 100644 --- a/packages/integrations/src/offline.ts +++ b/packages/integrations/src/offline.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable deprecation/deprecation */ import type { Event, EventProcessor, Hub, Integration } from '@sentry/types'; import { GLOBAL_OBJ, logger, normalize, uuid4 } from '@sentry/utils'; import localForage from 'localforage'; @@ -20,6 +21,9 @@ export type Item = { key: string; value: Event }; /** * cache offline errors and send when connected + * @deprecated The offline integration has been deprecated in favor of the offline transport wrapper. + * + * http://docs.sentry.io/platforms/javascript/configuration/transports/#offline-caching */ export class Offline implements Integration { /** diff --git a/packages/integrations/test/offline.test.ts b/packages/integrations/test/offline.test.ts index d7ca8099be31..624c166763b8 100644 --- a/packages/integrations/test/offline.test.ts +++ b/packages/integrations/test/offline.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable deprecation/deprecation */ import { WINDOW } from '@sentry/browser'; import type { Event, EventProcessor, Hub, Integration, IntegrationClass } from '@sentry/types'; From 1cf89886e829bf491d984e07ab575f5e32e9bea3 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 16 Feb 2023 15:59:22 +0100 Subject: [PATCH 17/21] build: Add size limit info to releases (#7203) --- .github/workflows/build.yml | 2 +- .github/workflows/release-size-info.yml | 38 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release-size-info.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dd293aedac8f..6a9f6372afb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -275,7 +275,7 @@ jobs: needs: [job_get_metadata, job_build] timeout-minutes: 15 runs-on: ubuntu-20.04 - if: github.event_name == 'pull_request' || needs.job_get_metadata.outputs.is_develop == 'true' + if: github.event_name == 'pull_request' || needs.job_get_metadata.outputs.is_develop == 'true' || needs.job_get_metadata.outputs.is_release == 'true' steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v3 diff --git a/.github/workflows/release-size-info.yml b/.github/workflows/release-size-info.yml new file mode 100644 index 000000000000..eae4a766d662 --- /dev/null +++ b/.github/workflows/release-size-info.yml @@ -0,0 +1,38 @@ +name: Add size info to release +on: + release: + types: + - published + workflow_dispatch: + inputs: + version: + description: Which version to add size info for + required: false + +# This workflow is triggered when a release is published +# It fetches the size-limit info from the release branch and adds it to the release +jobs: + release-size-info: + runs-on: ubuntu-20.04 + name: 'Add size-limit info to release' + + steps: + # https://github.com/actions-ecosystem/action-regex-match + - uses: actions-ecosystem/action-regex-match@v2 + id: head_version + with: + # Parse version from head ref, which is refs/tags/ + text: ${{ github.head_ref }} + regex: '^refs\/tags\/([\d.]+)$' + + - name: Get version + id: get_version + run: echo "version=${{ github.event.inputs.version || steps.head_version.outputs.match }}" >> $GITHUB_OUTPUT + + - name: Update Github Release + if: steps.get_version.outputs.version != '' + uses: getsentry/size-limit-release@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + version: ${{ steps.get_version.outputs.version }} + workflow_name: 'Build & Test' From b66559fd414cfc5f83fc87ac63f6d08e1e8319b4 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Thu, 16 Feb 2023 14:47:13 -0500 Subject: [PATCH 18/21] fix(replay): Debounced flushes not respecting `maxWait` (#7207) This is happening due to a bug in the `debounce` function where it does not respect `maxWait` if its value is the same as `wait` (https://github.com/getsentry/sentry-javascript/blob/develop/packages/replay/src/util/debounce.ts#L60) . This is causing poor fidelity in Replays and possibly memory issues if it never flushes after the initial checkout. This is just a quick fix until we fix `debounce`. --- packages/replay/src/constants.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/replay/src/constants.ts b/packages/replay/src/constants.ts index 87bf1823b056..85c482133ba9 100644 --- a/packages/replay/src/constants.ts +++ b/packages/replay/src/constants.ts @@ -22,7 +22,9 @@ export const MAX_SESSION_LIFE = 3_600_000; // 60 minutes /** Default flush delays */ export const DEFAULT_FLUSH_MIN_DELAY = 5_000; -export const DEFAULT_FLUSH_MAX_DELAY = 5_000; +// XXX: Temp fix for our debounce logic where `maxWait` would never occur if it +// was the same as `wait` +export const DEFAULT_FLUSH_MAX_DELAY = 5_500; /* How long to wait for error checkouts */ export const ERROR_CHECKOUT_TIME = 60_000; From 8305b949efe212fd7ee9f5f8f7602f94fa73313d Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Thu, 16 Feb 2023 15:26:17 -0500 Subject: [PATCH 19/21] feat(replay): Change LCP calculation (#7187) Fixes up calculating LCP timing based on `web-vitals`. Previously we were using the `duration` field on the PerformanceEntry, but it was always 0. Decided to change the `data.duration` field to `data.value` so that we do not break the frontend when trying to display this new field. --- .../utils/replayEventTemplates.ts | 2 +- .../src/util/createPerformanceEntries.ts | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/integration-tests/utils/replayEventTemplates.ts b/packages/integration-tests/utils/replayEventTemplates.ts index 6a86ef8f45c7..17aa903a0391 100644 --- a/packages/integration-tests/utils/replayEventTemplates.ts +++ b/packages/integration-tests/utils/replayEventTemplates.ts @@ -91,7 +91,7 @@ export const expectedLCPPerformanceSpan = { startTimestamp: expect.any(Number), endTimestamp: expect.any(Number), data: { - duration: expect.any(Number), + value: expect.any(Number), nodeId: expect.any(Number), size: expect.any(Number), }, diff --git a/packages/replay/src/util/createPerformanceEntries.ts b/packages/replay/src/util/createPerformanceEntries.ts index f90dc7d0af32..333a7fe5d809 100644 --- a/packages/replay/src/util/createPerformanceEntries.ts +++ b/packages/replay/src/util/createPerformanceEntries.ts @@ -104,17 +104,29 @@ function createResourceEntry(entry: PerformanceResourceTiming) { // TODO: type definition! // eslint-disable-next-line @typescript-eslint/explicit-function-return-type function createLargestContentfulPaint(entry: PerformanceEntry & { size: number; element: Node }) { - const { duration, entryType, startTime, size } = entry; + const { entryType, startTime, size } = entry; - const start = getAbsoluteTime(startTime); + let startTimeOrNavigationActivation = 0; + + if (WINDOW.performance) { + const navEntry = WINDOW.performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming & { + activationStart: number; + }; + + // See https://github.com/GoogleChrome/web-vitals/blob/9f11c4c6578fb4c5ee6fa4e32b9d1d756475f135/src/lib/getActivationStart.ts#L21 + startTimeOrNavigationActivation = (navEntry && navEntry.activationStart) || 0; + } + + const start = getAbsoluteTime(startTimeOrNavigationActivation); + const value = Math.max(startTime - startTimeOrNavigationActivation, 0); return { type: entryType, name: entryType, start, - end: start + duration, + end: start + value, data: { - duration, + value, size, // Not sure why this errors, Node should be correct (Argument of type 'Node' is not assignable to parameter of type 'INode') // eslint-disable-next-line @typescript-eslint/no-explicit-any From aacdf2b478f59516ec4d79f080b7434a2f168d0b Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Fri, 17 Feb 2023 03:41:44 -0500 Subject: [PATCH 20/21] fix(replay): Fix debounce when `maxWait` == `wait` (#7208) Co-authored-by: Francesco Novy --- packages/replay/src/util/debounce.ts | 2 +- packages/replay/test/unit/util/debounce.test.ts | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/replay/src/util/debounce.ts b/packages/replay/src/util/debounce.ts index a6774a62ca7e..88057c14a4c5 100644 --- a/packages/replay/src/util/debounce.ts +++ b/packages/replay/src/util/debounce.ts @@ -57,7 +57,7 @@ export function debounce(func: CallbackFunction, wait: number, options?: Debounc } timerId = setTimeout(invokeFunc, wait); - if (maxWait && maxTimerId === undefined && maxWait !== wait) { + if (maxWait && maxTimerId === undefined) { maxTimerId = setTimeout(invokeFunc, maxWait); } diff --git a/packages/replay/test/unit/util/debounce.test.ts b/packages/replay/test/unit/util/debounce.test.ts index b75adc6bf3bd..d52f9d456b49 100644 --- a/packages/replay/test/unit/util/debounce.test.ts +++ b/packages/replay/test/unit/util/debounce.test.ts @@ -246,14 +246,27 @@ describe('Unit | util | debounce', () => { const debouncedCallback = debounce(callback, 100, { maxWait: 100 }); debouncedCallback(); - jest.advanceTimersByTime(100); + jest.advanceTimersByTime(25); + debouncedCallback(); + jest.advanceTimersByTime(25); + debouncedCallback(); + jest.advanceTimersByTime(25); + debouncedCallback(); + jest.advanceTimersByTime(25); expect(callback).toHaveBeenCalledTimes(1); const retval = debouncedCallback(); expect(retval).toBe('foo'); - jest.advanceTimersByTime(100); + jest.advanceTimersByTime(25); + debouncedCallback(); + jest.advanceTimersByTime(25); + debouncedCallback(); + jest.advanceTimersByTime(25); + debouncedCallback(); + jest.advanceTimersByTime(25); + expect(callback).toHaveBeenCalledTimes(2); }); }); From bbe3c30ffb782df4fcc0811171dbb6ea09328118 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 17 Feb 2023 09:01:09 +0100 Subject: [PATCH 21/21] meta(changelog): Update CHANGELOG for 7.38.0 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56c5223e65e9..f87c29af8eaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 7.38.0 + +- feat: Put `abs_path` into stack frame object (#7167) +- feat(integrations): Deprecate `Offline` integration (#7063) +- feat(otel): Convert exception otel events to sentry errors (#7165) +- feat(replay): Change LCP calculation (#7187) +- feat(tracing): Support Apollo/GraphQL with NestJS (#7194) +- feat(tracing): Track `PerformanceResourceTiming.renderBlockingStatus` (#7127) +- feat(tracing|core): Remove transaction name change recording (#7197) +- fix(browser): Ensure dedupe integration ignores non-errors (#7172) +- fix(core): Skip empty integrations (#7204) +- fix(nextjs): Fix faulty import in Next.js .d.ts (#7175) +- fix(otel): Make otel.kind be a string (#7182) +- fix(react): Make fallback render types more accurate (#7198) +- fix(replay): Debounced flushes not respecting `maxWait` (#7207, #7208) +- ref(replay): Improve logging for stopped replay (#7174) + +Work in this release contributed by @lucas-zimermann. Thank you for your contribution! + ## 7.37.2 This release includes changes and fixes around text masking and blocking in Replay's `rrweb` dependency. See versions [1.102.0](https://github.com/getsentry/rrweb/releases/tag/1.102.0) and [1.103.0](https://github.com/getsentry/rrweb/releases/tag/1.103.0).