From f87a2a1e398a42b32844a01c165ea8fc732c8309 Mon Sep 17 00:00:00 2001 From: k-fish Date: Mon, 14 Sep 2020 21:01:13 -0700 Subject: [PATCH 01/10] feat(ember): Add more render instrumentation to @sentry/ember This PR will fill out the rest of the instrumentation for Ember to provide meaningful spans across most of the transaction. It adds instrumentation for: - initial load (which issues the build tools to inject a small script into head to measure script evaluation, which includes initial render) - the runloop, to capture queues that take a long time, this can primarily be used to capture long render queues when someone is using the new glimmer components as they don't have instrumentation. - components (non-glimmer) render times Additionally it fixes a couple bugs with routes, especially when nesting, such as beforeModel not consistently getting instrumented. --- packages/ember/README.md | 53 +++- packages/ember/addon/config.d.ts | 6 + packages/ember/addon/ember.d.ts | 6 + packages/ember/addon/index.ts | 58 +++-- .../sentry-performance.ts | 236 ++++++++++++++++-- packages/ember/addon/runloop.d.ts | 18 ++ packages/ember/index.js | 23 +- .../tests/acceptance/sentry-errors-test.js | 50 +--- .../acceptance/sentry-performance-test.js | 143 ++++++----- .../app/components/slow-loading-gc-list.ts | 3 + .../dummy/app/components/slow-loading-list.ts | 3 + .../controllers/slow-loading-route/index.js | 9 + packages/ember/tests/dummy/app/router.js | 4 +- .../dummy/app/routes/slow-loading-route.js | 34 +-- .../app/routes/slow-loading-route/index.js | 25 ++ packages/ember/tests/dummy/app/styles/app.css | 6 + .../components/slow-loading-gc-list.hbs | 10 + .../components/slow-loading-list.hbs | 2 +- .../app/templates/slow-loading-route.hbs | 7 +- .../templates/slow-loading-route/index.hbs | 5 + .../ember/tests/dummy/config/environment.js | 5 + .../ember/tests/helpers/setup-sentry-test.js | 64 +++++ packages/ember/vendor/initial-load-body.js | 3 + packages/ember/vendor/initial-load-head.js | 3 + 24 files changed, 595 insertions(+), 181 deletions(-) create mode 100644 packages/ember/addon/ember.d.ts create mode 100644 packages/ember/addon/runloop.d.ts create mode 100644 packages/ember/tests/dummy/app/components/slow-loading-gc-list.ts create mode 100644 packages/ember/tests/dummy/app/controllers/slow-loading-route/index.js create mode 100644 packages/ember/tests/dummy/app/routes/slow-loading-route/index.js create mode 100644 packages/ember/tests/dummy/app/templates/components/slow-loading-gc-list.hbs create mode 100644 packages/ember/tests/dummy/app/templates/slow-loading-route/index.hbs create mode 100644 packages/ember/tests/helpers/setup-sentry-test.js create mode 100644 packages/ember/vendor/initial-load-body.js create mode 100644 packages/ember/vendor/initial-load-head.js diff --git a/packages/ember/README.md b/packages/ember/README.md index 60e3dd09e041..ad37ba1b019c 100644 --- a/packages/ember/README.md +++ b/packages/ember/README.md @@ -62,6 +62,7 @@ Aside from configuration passed from this addon into `@sentry/browser` via the ` sentry: ... // See sentry-javascript configuration https://docs.sentry.io/error-reporting/configuration/?platform=javascript }; ``` + #### Disabling Performance `@sentry/ember` captures performance by default, if you would like to disable the automatic performance instrumentation, you can add the following to your `config/environment.js`: @@ -82,13 +83,53 @@ you can import `instrumentRoutePerformance` and wrap your route with it. import Route from '@ember/routing/route'; import { instrumentRoutePerformance } from '@sentry/ember'; -export default instrumentRoutePerformance( - class MyRoute extends Route { - model() { - //... - } +class MyRoute extends Route { + model() { + //... } -); +} + +export default instrumentRoutePerformance(SlowLoadingRoute); +``` + +#### Runloop +The runloop queue durations are instrumented by default, as long as they are longer than a threshold (by default 5ms). +This helps (via the render queue) capturing the entire render in case component render times aren't fully instrumented, +such as when using glimmer components. + +If you would like to change the runloop queue threshold, add the following to your config: +```javascript + ENV['@sentry/ember'] = { + minimumRunloopQueueDuration: 0, // All runloop queue durations will be added as spans. + }; +``` + +#### Components +Non-glimmer component render times will automatically get captured. + +If you would like to disable component render being instrumented, add the following to your config: +```javascript + ENV['@sentry/ember'] = { + disableInstrumentComponents: true, // Will disable automatic instrumentation for components. + }; +``` + +Additionally, components whose render time is below a threshold (by default 2ms) will not be included as spans. +If you would like to change this threshold, add the following to your config: +```javascript + ENV['@sentry/ember'] = { + minimumComponentRenderDuration: 0, // All (non-glimmer) component render durations will be added as spans. + }; +``` + +#### Glimmer components +Currently glimmer component render durations can only be captured indirectly via the runloop instrumentation. You can +optionally enable a setting to show component definitions (which will indicate which components are being rendered) be +adding the following to your config: +```javascript + ENV['@sentry/ember'] = { + enableComponentDefinition: true, // All component definitions will be added as spans. + }; ``` ### Supported Versions diff --git a/packages/ember/addon/config.d.ts b/packages/ember/addon/config.d.ts index 193d08f2db93..8b1b6f5b55c6 100644 --- a/packages/ember/addon/config.d.ts +++ b/packages/ember/addon/config.d.ts @@ -4,8 +4,14 @@ declare module 'ember-get-config' { sentry: BrowserOptions; transitionTimeout: number; ignoreEmberOnErrorWarning: boolean; + disableInstrumentComponents: boolean; disablePerformance: boolean; disablePostTransitionRender: boolean; + disableRunloopPerformance: boolean; + disableInitialLoadInstrumentation: boolean; + enableComponentDefinitions: boolean; + minimumRunloopQueueDuration: number; + minimumComponentRenderDuration: number; }; const config: { '@sentry/ember': EmberSentryConfig; diff --git a/packages/ember/addon/ember.d.ts b/packages/ember/addon/ember.d.ts new file mode 100644 index 000000000000..3ac2c5464828 --- /dev/null +++ b/packages/ember/addon/ember.d.ts @@ -0,0 +1,6 @@ +import Ember from 'ember'; +declare module 'ember' { + namespace Ember { + export function subscribe(pattern: string, object: {}): any; + } +} diff --git a/packages/ember/addon/index.ts b/packages/ember/addon/index.ts index 1069225c4934..038a3b0e0655 100644 --- a/packages/ember/addon/index.ts +++ b/packages/ember/addon/index.ts @@ -5,6 +5,7 @@ import environmentConfig from 'ember-get-config'; import { next } from '@ember/runloop'; import { assert, warn, runInDebug } from '@ember/debug'; import Ember from 'ember'; +import { timestampWithMs } from '@sentry/utils'; declare module '@ember/debug' { export function assert(desc: string, test: unknown): void; @@ -37,38 +38,49 @@ export function InitSentryForEmber(_runtimeConfig: BrowserOptions | undefined) { }); } -const getCurrentTransaction = () => { +export const getActiveTransaction = () => { return Sentry.getCurrentHub() ?.getScope() ?.getTransaction(); }; -const instrumentFunction = async (op: string, description: string, fn: Function, args: any) => { - const currentTransaction = getCurrentTransaction(); - const span = currentTransaction?.startChild({ op, description }); - const result = await fn(...args); - span?.finish(); - return result; -}; - export const instrumentRoutePerformance = (BaseRoute: any) => { - return class InstrumentedRoute extends BaseRoute { - beforeModel(...args: any[]) { - return instrumentFunction('ember.route.beforeModel', (this).fullRouteName, super.beforeModel, args); - } - - async model(...args: any[]) { - return instrumentFunction('ember.route.model', (this).fullRouteName, super.model, args); - } - - async afterModel(...args: any[]) { - return instrumentFunction('ember.route.afterModel', (this).fullRouteName, super.afterModel, args); - } + const instrumentFunction = async (op: string, description: string, fn: Function, args: any) => { + const startTimestamp = timestampWithMs(); + const result = await fn(...args); - async setupController(...args: any[]) { - return instrumentFunction('ember.route.setupController', (this).fullRouteName, super.setupController, args); + const currentTransaction = getActiveTransaction(); + if (!currentTransaction) { + return result; } + currentTransaction.startChild({ op, description, startTimestamp }).finish(); + return result; }; + + return { + [BaseRoute.name]: class extends BaseRoute { + beforeModel(...args: any[]) { + return instrumentFunction('ember.route.beforeModel', (this).fullRouteName, super.beforeModel, args); + } + + async model(...args: any[]) { + return instrumentFunction('ember.route.model', (this).fullRouteName, super.model, args); + } + + async afterModel(...args: any[]) { + return instrumentFunction('ember.route.afterModel', (this).fullRouteName, super.afterModel, args); + } + + async setupController(...args: any[]) { + return instrumentFunction( + 'ember.route.setupController', + (this).fullRouteName, + super.setupController, + args, + ); + } + }, + }[BaseRoute.name]; }; function createEmberEventProcessor(): void { diff --git a/packages/ember/addon/instance-initializers/sentry-performance.ts b/packages/ember/addon/instance-initializers/sentry-performance.ts index f76b605a8fb3..161ad19f87c6 100644 --- a/packages/ember/addon/instance-initializers/sentry-performance.ts +++ b/packages/ember/addon/instance-initializers/sentry-performance.ts @@ -1,9 +1,12 @@ import ApplicationInstance from '@ember/application/instance'; import Ember from 'ember'; -import { scheduleOnce } from '@ember/runloop'; +import { run } from '@ember/runloop'; import environmentConfig from 'ember-get-config'; import Sentry from '@sentry/browser'; -import { Integration } from '@sentry/types'; +import { Span, Transaction, Integration } from '@sentry/types'; +import { EmberRunQueues } from '@ember/runloop/-private/types'; +import { getActiveTransaction } from '..'; +import { timestampWithMs } from '@sentry/utils'; export function initialize(appInstance: ApplicationInstance): void { const config = environmentConfig['@sentry/ember']; @@ -32,15 +35,16 @@ export function _instrumentEmberRouter( startTransaction: Function, startTransactionOnPageLoad?: boolean, ) { - const { disablePostTransitionRender } = config; + const { disableRunloopPerformance } = config; const location = routerMain.location; - let activeTransaction: any; - let transitionSpan: any; + let activeTransaction: Transaction; + let transitionSpan: Span; const url = location && location.getURL && location.getURL(); if (Ember.testing) { routerService._sentryInstrumented = true; + routerService._startTransaction = startTransaction; } if (startTransactionOnPageLoad && url) { @@ -56,8 +60,17 @@ export function _instrumentEmberRouter( }); } + const finishActiveTransaction = function(_: any, nextInstance: any) { + if (nextInstance) { + return; + } + activeTransaction.finish(); + run.backburner.off('end', finishActiveTransaction); + }; + routerService.on('routeWillChange', (transition: any) => { const { fromRoute, toRoute } = getTransitionInformation(transition, routerService); + activeTransaction?.finish(); activeTransaction = startTransaction({ name: `route:${toRoute}`, op: 'navigation', @@ -73,38 +86,216 @@ export function _instrumentEmberRouter( }); }); - routerService.on('routeDidChange', (transition: any) => { - const { toRoute } = getTransitionInformation(transition, routerService); - let renderSpan: any; + routerService.on('routeDidChange', () => { if (!transitionSpan || !activeTransaction) { return; } transitionSpan.finish(); - if (disablePostTransitionRender) { + if (disableRunloopPerformance) { activeTransaction.finish(); + return; + } + + run.backburner.on('end', finishActiveTransaction); + }); + + return { + startTransaction, + }; +} + +function _instrumentEmberRunloop(config: typeof environmentConfig['@sentry/ember']) { + const { disableRunloopPerformance, minimumRunloopQueueDuration } = config; + if (disableRunloopPerformance) { + return; + } + + let currentQueueStart: number | undefined; + let currentQueueSpan: Span | undefined; + const instrumentedEmberQueues = [ + 'actions', + 'routerTransitions', + 'render', + 'afterRender', + 'destroy', + ] as EmberRunQueues[]; + + run.backburner.on('begin', (_: any, previousInstance: any) => { + if (previousInstance) { + return; + } + const activeTransaction = getActiveTransaction(); + if (!activeTransaction) { + return; + } + if (currentQueueSpan) { + currentQueueSpan.finish(); } + currentQueueStart = timestampWithMs(); + + instrumentedEmberQueues.forEach(queue => { + run.scheduleOnce(queue, null, () => { + run.scheduleOnce(queue, null, () => { + // Process this queue using the end of the previous queue. + if (currentQueueStart) { + const now = timestampWithMs(); + const minQueueDuration = minimumRunloopQueueDuration ?? 5; + + if ((now - currentQueueStart) * 1000 >= minQueueDuration) { + activeTransaction + ?.startChild({ + op: `ember.runloop.${queue}`, + startTimestamp: currentQueueStart, + endTimestamp: now, + }) + .finish(); + } + currentQueueStart = undefined; + } + + // Setup for next queue - function startRenderSpan() { - renderSpan = activeTransaction.startChild({ - op: 'ember.runloop.render', - description: `post-transition render route:${toRoute}`, + const stillActiveTransaction = getActiveTransaction(); + if (!stillActiveTransaction) { + return; + } + currentQueueStart = timestampWithMs(); + }); }); + }); + }); + run.backburner.on('end', (_: any, nextInstance: any) => { + if (nextInstance) { + return; } + if (currentQueueSpan) { + currentQueueSpan.finish(); + currentQueueSpan = undefined; + } + }); +} - function finishRenderSpan() { - renderSpan.finish(); - activeTransaction.finish(); +type Payload = { + containerKey: string; + initialRender: true; + object: string; +}; + +type RenderEntry = { + payload: Payload; + now: number; +}; + +interface RenderEntries { + [name: string]: RenderEntry; +} + +function processComponentRenderBefore(payload: Payload, beforeEntries: RenderEntries) { + const info = { + payload, + now: timestampWithMs(), + }; + beforeEntries[payload.object] = info; +} + +function processComponentRenderAfter( + payload: Payload, + beforeEntries: RenderEntries, + op: string, + minComponentDuration: number, +) { + const begin = beforeEntries[payload.object]; + + if (!begin) { + return; + } + + const now = timestampWithMs(); + const componentRenderDuration = now - begin.now; + + if (componentRenderDuration * 1000 >= minComponentDuration) { + const activeTransaction = getActiveTransaction(); + + activeTransaction?.startChild({ + op, + description: payload.containerKey || payload.object, + startTimestamp: begin.now, + endTimestamp: now, + }); + } +} + +function _instrumentComponents(config: typeof environmentConfig['@sentry/ember']) { + const { disableInstrumentComponents, minimumComponentRenderDuration, enableComponentDefinitions } = config; + if (disableInstrumentComponents) { + return; + } + + const minComponentDuration = minimumComponentRenderDuration ?? 2; + + const beforeEntries = {} as RenderEntries; + const beforeComponentDefinitionEntries = {} as RenderEntries; + + const subscribe = Ember.subscribe; + function _subscribeToRenderEvents() { + subscribe('render.component', { + before(_name: string, _timestamp: number, payload: Payload) { + processComponentRenderBefore(payload, beforeEntries); + }, + + after(_name: string, _timestamp: number, payload: any, _beganIndex: number) { + processComponentRenderAfter(payload, beforeEntries, 'ember.component.render', minComponentDuration); + }, + }); + if (enableComponentDefinitions) { + subscribe('render.getComponentDefinition', { + before(_name: string, _timestamp: number, payload: Payload) { + processComponentRenderBefore(payload, beforeComponentDefinitionEntries); + }, + + after(_name: string, _timestamp: number, payload: any, _beganIndex: number) { + processComponentRenderAfter(payload, beforeComponentDefinitionEntries, 'ember.component.definition', 0); + }, + }); } + } + _subscribeToRenderEvents(); +} + +function _instrumentInitialLoad(config: typeof environmentConfig['@sentry/ember']) { + const startName = '@sentry/ember:initial-load-start'; + const endName = '@sentry/ember:initial-load-end'; + if (config.disableInitialLoadInstrumentation) { + performance.clearMarks(startName); + performance.clearMarks(endName); + return; + } + const measureName = '@sentry/ember:initial-load'; + + performance.measure(measureName, startName, endName); + const measures = performance.getEntriesByName(measureName); + const measure = measures[0]; + + const startTimestamp = (measure.startTime + performance.timeOrigin) / 1000; + const endTimestamp = startTimestamp + measure.duration / 1000; - scheduleOnce('routerTransitions', null, startRenderSpan); - scheduleOnce('afterRender', null, finishRenderSpan); + const transaction = getActiveTransaction(); + const span = transaction?.startChild({ + op: 'ember.initial-load', + startTimestamp, }); + span?.finish(endTimestamp); + performance.clearMarks(startName); + performance.clearMarks(endName); + + performance.clearMeasures(measureName); } export async function instrumentForPerformance(appInstance: ApplicationInstance) { const config = environmentConfig['@sentry/ember']; const sentryConfig = config.sentry; + const tracing = await import('@sentry/tracing'); const idleTimeout = config.transitionTimeout || 5000; @@ -123,7 +314,16 @@ export async function instrumentForPerformance(appInstance: ApplicationInstance) }), ]; + if (Ember.testing && Sentry.getCurrentHub()?.getIntegration(tracing.Integrations.BrowserTracing)) { + // Initializers are called more than once in tests, causing the integrations to not be setup correctly. + return; + } + Sentry.init(sentryConfig); // Call init again to rebind client with new integration list in addition to the defaults + + _instrumentEmberRunloop(config); + _instrumentComponents(config); + _instrumentInitialLoad(config); } export default { diff --git a/packages/ember/addon/runloop.d.ts b/packages/ember/addon/runloop.d.ts new file mode 100644 index 000000000000..f6dbc7e82d7f --- /dev/null +++ b/packages/ember/addon/runloop.d.ts @@ -0,0 +1,18 @@ +import { Backburner } from '@ember/runloop/-private/backburner'; + +/** + * Backburner needs to be extended as it's missing the 'off' method. + */ +interface ExtendedBackburner extends Backburner { + off(...args: any[]): void; +} + +/** + * Runloop needs to be extended to expose backburner as suggested here: + * https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/ember__runloop/ember__runloop-tests.ts#L9 + */ +declare module '@ember/runloop' { + interface RunNamespace { + backburner: ExtendedBackburner; + } +} diff --git a/packages/ember/index.js b/packages/ember/index.js index 967d490abc24..cb5011e4ab8e 100644 --- a/packages/ember/index.js +++ b/packages/ember/index.js @@ -1,10 +1,29 @@ 'use strict'; +const fs = require('fs'); + +function readSnippet(fileName) { + return ``; +} module.exports = { name: require('./package').name, options: { babel: { - plugins: [ require.resolve('ember-auto-import/babel-plugin') ] + plugins: [require.resolve('ember-auto-import/babel-plugin')], + }, + }, + + contentFor(type, config) { + const addonConfig = config['@sentry/ember'] || {}; + const { disableInitialLoadInstrumentation } = addonConfig; + if (disableInitialLoadInstrumentation) { + return; + } + if (type === 'head') { + return readSnippet('initial-load-head.js'); + } + if (type === 'body-footer') { + return readSnippet('initial-load-body.js'); } - } + }, }; diff --git a/packages/ember/tests/acceptance/sentry-errors-test.js b/packages/ember/tests/acceptance/sentry-errors-test.js index 00647bf9288f..ad616db1f8f3 100644 --- a/packages/ember/tests/acceptance/sentry-errors-test.js +++ b/packages/ember/tests/acceptance/sentry-errors-test.js @@ -2,8 +2,7 @@ import { test, module } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { find, click, visit } from '@ember/test-helpers'; import { run } from '@ember/runloop'; -import Ember from 'ember'; -import sinon from 'sinon'; +import { setupSentryTest } from '../helpers/setup-sentry-test'; const defaultAssertOptions = { method: 'POST', @@ -37,52 +36,7 @@ function assertSentryCall(assert, callNumber, options) { module('Acceptance | Sentry Errors', function(hooks) { setupApplicationTest(hooks); - - hooks.beforeEach(async function() { - await window._sentryPerformanceLoad; - window._sentryTestEvents = []; - const errorMessages = []; - this.errorMessages = errorMessages; - - /** - * Stub out fetch function to assert on Sentry calls. - */ - this.fetchStub = sinon.stub(window, 'fetch'); - - /** - * Stops global test suite failures from unhandled rejections and allows assertion on them - */ - this.qunitOnUnhandledRejection = sinon.stub(QUnit, 'onUnhandledRejection'); - - QUnit.onError = function({ message }) { - errorMessages.push(message.split('Error: ')[1]); - return true; - }; - - Ember.onerror = function(...args) { - const [error] = args; - errorMessages.push(error.message); - throw error; - }; - - this._windowOnError = window.onerror; - /** - * Will collect errors when run via testem in cli - */ - - window.onerror = function(error, ...args) { - errorMessages.push(error.split('Error: ')[1]); - if (this._windowOnError) { - return this._windowOnError(error, ...args); - } - }; - }); - - hooks.afterEach(function() { - this.fetchStub.restore(); - this.qunitOnUnhandledRejection.restore(); - window.onerror = this._windowOnError; - }); + setupSentryTest(hooks); test('Check "Throw Generic Javascript Error"', async function(assert) { await visit('/'); diff --git a/packages/ember/tests/acceptance/sentry-performance-test.js b/packages/ember/tests/acceptance/sentry-performance-test.js index 312fc1a8d3cb..435e2e517798 100644 --- a/packages/ember/tests/acceptance/sentry-performance-test.js +++ b/packages/ember/tests/acceptance/sentry-performance-test.js @@ -1,10 +1,7 @@ import { test, module } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { find, click, visit } from '@ember/test-helpers'; -import Ember from 'ember'; -import sinon from 'sinon'; -import { _instrumentEmberRouter } from '@sentry/ember/instance-initializers/sentry-performance'; -import { startTransaction } from '@sentry/browser'; +import { setupSentryTest } from '../helpers/setup-sentry-test'; const SLOW_TRANSITION_WAIT = 3000; @@ -18,80 +15,50 @@ function assertSentryTransactionCount(assert, count) { function assertSentryCall(assert, callNumber, options) { const sentryTestEvents = getTestSentryTransactions(); - const event = sentryTestEvents[callNumber]; - assert.equal(event.spans.length, options.spanCount); + + assert.ok(options.spanCount || options.spans, 'Must add spanCount or spans to assertion'); + if (options.spanCount) { + assert.equal(event.spans.length, options.spanCount); + } + if (options.spans) { + assert.deepEqual( + event.spans.map(s => `${s.op} | ${s.description}`), + options.spans, + `Has correct spans`, + ); + } + assert.equal(event.transaction, options.transaction); assert.equal(event.tags.fromRoute, options.tags.fromRoute); assert.equal(event.tags.toRoute, options.tags.toRoute); if (options.durationCheck) { const duration = (event.timestamp - event.start_timestamp) * 1000; - assert.ok(options.durationCheck(duration), `duration (${duration}ms) didn't pass duration check`); + assert.ok(options.durationCheck(duration), `duration (${duration}ms) passes duration check`); } } module('Acceptance | Sentry Transactions', function(hooks) { setupApplicationTest(hooks); - - hooks.beforeEach(async function() { - await window._sentryPerformanceLoad; - window._sentryTestEvents = []; - const errorMessages = []; - this.errorMessages = errorMessages; - - const routerMain = this.owner.lookup('router:main'); - const routerService = this.owner.lookup('service:router'); - - if (!routerService._sentryInstrumented) { - _instrumentEmberRouter(routerService, routerMain, {}, startTransaction); - } - - /** - * Stub out fetch function to assert on Sentry calls. - */ - this.fetchStub = sinon.stub(window, 'fetch'); - - /** - * Stops global test suite failures from unhandled rejections and allows assertion on them - */ - this.qunitOnUnhandledRejection = sinon.stub(QUnit, 'onUnhandledRejection'); - - QUnit.onError = function({ message }) { - errorMessages.push(message.split('Error: ')[1]); - return true; - }; - - Ember.onerror = function(...args) { - const [error] = args; - errorMessages.push(error.message); - throw error; - }; - - this._windowOnError = window.onerror; - - /** - * Will collect errors when run via testem in cli - */ - window.onerror = function(error, ...args) { - errorMessages.push(error.split('Error: ')[1]); - if (this._windowOnError) { - return this._windowOnError(error, ...args); - } - }; - }); - - hooks.afterEach(function() { - this.fetchStub.restore(); - this.qunitOnUnhandledRejection.restore(); - window.onerror = this._windowOnError; - }); + setupSentryTest(hooks); test('Test transaction', async function(assert) { await visit('/tracing'); + assertSentryTransactionCount(assert, 1); assertSentryCall(assert, 0, { - spanCount: 2, + spans: [ + 'ember.transition | route:undefined -> route:tracing', + 'ember.component.render | component:link-to', + 'ember.component.render | component:link-to', + 'ember.component.render | component:test-section', + 'ember.runloop.actions | undefined', + 'ember.runloop.routerTransitions | undefined', + 'ember.runloop.render | undefined', + 'ember.runloop.afterRender | undefined', + 'ember.runloop.destroy | undefined', + ], transaction: 'route:tracing', tags: { fromRoute: undefined, @@ -108,12 +75,60 @@ module('Acceptance | Sentry Transactions', function(hooks) { assertSentryTransactionCount(assert, 2); assertSentryCall(assert, 1, { - spanCount: 2, - transaction: 'route:slow-loading-route', + spans: [ + 'ember.transition | route:tracing -> route:slow-loading-route.index', + 'ember.component.render | component:link-to', + 'ember.component.render | component:link-to', + 'ember.route.beforeModel | slow-loading-route', + 'ember.runloop.actions | undefined', + 'ember.runloop.routerTransitions | undefined', + 'ember.runloop.render | undefined', + 'ember.runloop.afterRender | undefined', + 'ember.runloop.destroy | undefined', + 'ember.route.model | slow-loading-route', + 'ember.runloop.actions | undefined', + 'ember.runloop.routerTransitions | undefined', + 'ember.runloop.render | undefined', + 'ember.runloop.afterRender | undefined', + 'ember.runloop.destroy | undefined', + 'ember.route.afterModel | slow-loading-route', + 'ember.runloop.actions | undefined', + 'ember.runloop.routerTransitions | undefined', + 'ember.component.render | component:link-to', + 'ember.component.render | component:link-to', + 'ember.runloop.render | undefined', + 'ember.runloop.afterRender | undefined', + 'ember.runloop.destroy | undefined', + 'ember.route.beforeModel | slow-loading-route.index', + 'ember.runloop.actions | undefined', + 'ember.runloop.routerTransitions | undefined', + 'ember.runloop.render | undefined', + 'ember.runloop.afterRender | undefined', + 'ember.runloop.destroy | undefined', + 'ember.route.model | slow-loading-route.index', + 'ember.runloop.actions | undefined', + 'ember.runloop.routerTransitions | undefined', + 'ember.runloop.render | undefined', + 'ember.runloop.afterRender | undefined', + 'ember.runloop.destroy | undefined', + 'ember.route.afterModel | slow-loading-route.index', + 'ember.runloop.actions | undefined', + 'ember.route.setupController | slow-loading-route', + 'ember.route.setupController | slow-loading-route.index', + 'ember.runloop.routerTransitions | undefined', + 'ember.component.render | component:link-to', + 'ember.component.render | component:link-to', + 'ember.component.render | component:slow-loading-list', + 'ember.component.render | component:slow-loading-list', + 'ember.runloop.render | undefined', + 'ember.runloop.afterRender | undefined', + 'ember.runloop.destroy | undefined', + ], + transaction: 'route:slow-loading-route.index', durationCheck: duration => duration > SLOW_TRANSITION_WAIT, tags: { fromRoute: 'tracing', - toRoute: 'slow-loading-route', + toRoute: 'slow-loading-route.index', }, }); }); diff --git a/packages/ember/tests/dummy/app/components/slow-loading-gc-list.ts b/packages/ember/tests/dummy/app/components/slow-loading-gc-list.ts new file mode 100644 index 000000000000..c5205d47e4f6 --- /dev/null +++ b/packages/ember/tests/dummy/app/components/slow-loading-gc-list.ts @@ -0,0 +1,3 @@ +import Component from '@glimmer/component'; + +export default class SlowLoadingGCList extends Component {} diff --git a/packages/ember/tests/dummy/app/components/slow-loading-list.ts b/packages/ember/tests/dummy/app/components/slow-loading-list.ts index cd30d34acea0..8df604bb92ff 100644 --- a/packages/ember/tests/dummy/app/components/slow-loading-list.ts +++ b/packages/ember/tests/dummy/app/components/slow-loading-list.ts @@ -2,6 +2,9 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; export default Component.extend({ + _title: computed('title', function() { + return this.title || 'Slow Loading List'; + }), rowItems: computed('items', function() { return new Array(parseInt(this.items)).fill(0).map((_, index) => { return { diff --git a/packages/ember/tests/dummy/app/controllers/slow-loading-route/index.js b/packages/ember/tests/dummy/app/controllers/slow-loading-route/index.js new file mode 100644 index 000000000000..1aae8ab4790e --- /dev/null +++ b/packages/ember/tests/dummy/app/controllers/slow-loading-route/index.js @@ -0,0 +1,9 @@ +import Controller from '@ember/controller'; +import { computed } from '@ember/object'; + +export default class SlowLoadingRouteController extends Controller { + @computed() + get slowLoadingTemplateOnlyItems() { + return new Array(2000).fill(0).map((_, index) => index); + } +} diff --git a/packages/ember/tests/dummy/app/router.js b/packages/ember/tests/dummy/app/router.js index 1eb50a98d851..eece01fb40ae 100644 --- a/packages/ember/tests/dummy/app/router.js +++ b/packages/ember/tests/dummy/app/router.js @@ -8,5 +8,7 @@ export default class Router extends EmberRouter { Router.map(function() { this.route('tracing'); - this.route('slow-loading-route'); + this.route('slow-loading-route', function() { + this.route('index', { path: '/' }); + }); }); diff --git a/packages/ember/tests/dummy/app/routes/slow-loading-route.js b/packages/ember/tests/dummy/app/routes/slow-loading-route.js index 678520c9c202..a28b0fd7eff6 100644 --- a/packages/ember/tests/dummy/app/routes/slow-loading-route.js +++ b/packages/ember/tests/dummy/app/routes/slow-loading-route.js @@ -2,24 +2,24 @@ import Route from '@ember/routing/route'; import timeout from '../helpers/utils'; import { instrumentRoutePerformance } from '@sentry/ember'; -const SLOW_TRANSITION_WAIT = 3000; +const SLOW_TRANSITION_WAIT = 1500; -export default instrumentRoutePerformance( - class SlowLoadingRoute extends Route { - beforeModel() { - return timeout(SLOW_TRANSITION_WAIT / 3); - } +class SlowDefaultLoadingRoute extends Route { + beforeModel() { + return timeout(SLOW_TRANSITION_WAIT / 3); + } - model() { - return timeout(SLOW_TRANSITION_WAIT / 3); - } + model() { + return timeout(SLOW_TRANSITION_WAIT / 3); + } - afterModel() { - return timeout(SLOW_TRANSITION_WAIT / 3); - } + afterModel() { + return timeout(SLOW_TRANSITION_WAIT / 3); + } - setupController() { - super.setupController(); - } - }, -); + setupController() { + super.setupController(); + } +} + +export default instrumentRoutePerformance(SlowDefaultLoadingRoute); diff --git a/packages/ember/tests/dummy/app/routes/slow-loading-route/index.js b/packages/ember/tests/dummy/app/routes/slow-loading-route/index.js new file mode 100644 index 000000000000..658fe8680bc8 --- /dev/null +++ b/packages/ember/tests/dummy/app/routes/slow-loading-route/index.js @@ -0,0 +1,25 @@ +import Route from '@ember/routing/route'; +import timeout from '../../helpers/utils'; +import { instrumentRoutePerformance } from '@sentry/ember'; + +const SLOW_TRANSITION_WAIT = 1500; + +class SlowLoadingRoute extends Route { + beforeModel() { + return timeout(SLOW_TRANSITION_WAIT / 3); + } + + model() { + return timeout(SLOW_TRANSITION_WAIT / 3); + } + + afterModel() { + return timeout(SLOW_TRANSITION_WAIT / 3); + } + + setupController() { + super.setupController(); + } +} + +export default instrumentRoutePerformance(SlowLoadingRoute); diff --git a/packages/ember/tests/dummy/app/styles/app.css b/packages/ember/tests/dummy/app/styles/app.css index 4c69b62ced41..4d9686bbd61f 100644 --- a/packages/ember/tests/dummy/app/styles/app.css +++ b/packages/ember/tests/dummy/app/styles/app.css @@ -183,3 +183,9 @@ button.primary:focus { box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.12); outline: none; } + +.list-grid { + display: grid; + grid-template-columns: 1fr 1fr; + column-gap: 10px; +} diff --git a/packages/ember/tests/dummy/app/templates/components/slow-loading-gc-list.hbs b/packages/ember/tests/dummy/app/templates/components/slow-loading-gc-list.hbs new file mode 100644 index 000000000000..800b0344a1c3 --- /dev/null +++ b/packages/ember/tests/dummy/app/templates/components/slow-loading-gc-list.hbs @@ -0,0 +1,10 @@ +
+

{{@title}}

+
+ {{#each @rowItems as |rowItem|}} +
+ {{rowItem}} +
+ {{/each}} +
+
diff --git a/packages/ember/tests/dummy/app/templates/components/slow-loading-list.hbs b/packages/ember/tests/dummy/app/templates/components/slow-loading-list.hbs index 8dce1ce0c319..88e1a0db9371 100644 --- a/packages/ember/tests/dummy/app/templates/components/slow-loading-list.hbs +++ b/packages/ember/tests/dummy/app/templates/components/slow-loading-list.hbs @@ -1,5 +1,5 @@
-

Slow Loading List

+

{{this._title}}

{{#each this.rowItems as |rowItem|}}
diff --git a/packages/ember/tests/dummy/app/templates/slow-loading-route.hbs b/packages/ember/tests/dummy/app/templates/slow-loading-route.hbs index a1177d563500..72981cae67d7 100644 --- a/packages/ember/tests/dummy/app/templates/slow-loading-route.hbs +++ b/packages/ember/tests/dummy/app/templates/slow-loading-route.hbs @@ -3,4 +3,9 @@ Go Back - +
+ +
+ {{outlet}} +
+
diff --git a/packages/ember/tests/dummy/app/templates/slow-loading-route/index.hbs b/packages/ember/tests/dummy/app/templates/slow-loading-route/index.hbs new file mode 100644 index 000000000000..6948ac6acccb --- /dev/null +++ b/packages/ember/tests/dummy/app/templates/slow-loading-route/index.hbs @@ -0,0 +1,5 @@ +
+ + +
diff --git a/packages/ember/tests/dummy/config/environment.js b/packages/ember/tests/dummy/config/environment.js index e8588c35c079..f9d98301748b 100644 --- a/packages/ember/tests/dummy/config/environment.js +++ b/packages/ember/tests/dummy/config/environment.js @@ -29,6 +29,8 @@ module.exports = function(environment) { dsn: process.env.SENTRY_DSN, }, ignoreEmberOnErrorWarning: true, + minimumRunloopQueueDuration: 5, + minimumComponentRenderDuration: 2, }; if (environment === 'development') { @@ -52,6 +54,9 @@ module.exports = function(environment) { // Include fake dsn so that instrumentation is enabled when running from cli ENV['@sentry/ember'].sentry.dsn = 'https://0@0.ingest.sentry.io/0'; + + ENV['@sentry/ember'].minimumRunloopQueueDuration = 0; + ENV['@sentry/ember'].minimumComponentRenderDuration = 0; } if (environment === 'production') { diff --git a/packages/ember/tests/helpers/setup-sentry-test.js b/packages/ember/tests/helpers/setup-sentry-test.js new file mode 100644 index 000000000000..034e7089f99f --- /dev/null +++ b/packages/ember/tests/helpers/setup-sentry-test.js @@ -0,0 +1,64 @@ +import Ember from 'ember'; +import sinon from 'sinon'; +import { _instrumentEmberRouter } from '@sentry/ember/instance-initializers/sentry-performance'; + +// Keep a reference to the original startTransaction as the application gets re-initialized and setup for +// the integration doesn't occur again after the first time. +let _routerStartTransaction; + +export function setupSentryTest(hooks) { + hooks.beforeEach(async function() { + await window._sentryPerformanceLoad; + window._sentryTestEvents = []; + const errorMessages = []; + this.errorMessages = errorMessages; + + const routerMain = this.owner.lookup('router:main'); + const routerService = this.owner.lookup('service:router'); + + if (routerService._sentryInstrumented) { + _routerStartTransaction = routerService._startTransaction; + } else { + _instrumentEmberRouter(routerService, routerMain, {}, _routerStartTransaction); + } + + /** + * Stub out fetch function to assert on Sentry calls. + */ + this.fetchStub = sinon.stub(window, 'fetch'); + + /** + * Stops global test suite failures from unhandled rejections and allows assertion on them + */ + this.qunitOnUnhandledRejection = sinon.stub(QUnit, 'onUnhandledRejection'); + + QUnit.onError = function({ message }) { + errorMessages.push(message.split('Error: ')[1]); + return true; + }; + + Ember.onerror = function(...args) { + const [error] = args; + errorMessages.push(error.message); + throw error; + }; + + this._windowOnError = window.onerror; + + /** + * Will collect errors when run via testem in cli + */ + window.onerror = function(error, ...args) { + errorMessages.push(error.split('Error: ')[1]); + if (this._windowOnError) { + return this._windowOnError(error, ...args); + } + }; + }); + + hooks.afterEach(function() { + this.fetchStub.restore(); + this.qunitOnUnhandledRejection.restore(); + window.onerror = this._windowOnError; + }); +} diff --git a/packages/ember/vendor/initial-load-body.js b/packages/ember/vendor/initial-load-body.js new file mode 100644 index 000000000000..c538bf091d70 --- /dev/null +++ b/packages/ember/vendor/initial-load-body.js @@ -0,0 +1,3 @@ +if (window.performance && window.performance.mark) { + window.performance.mark('@sentry/ember:initial-load-end'); +} diff --git a/packages/ember/vendor/initial-load-head.js b/packages/ember/vendor/initial-load-head.js new file mode 100644 index 000000000000..27152f5aa5ef --- /dev/null +++ b/packages/ember/vendor/initial-load-head.js @@ -0,0 +1,3 @@ +if (window.performance && window.performance.mark) { + window.performance.mark('@sentry/ember:initial-load-start'); +} From d36ae34f39894faf1241d92a9d59432f0e723a5f Mon Sep 17 00:00:00 2001 From: k-fish Date: Mon, 14 Sep 2020 21:27:24 -0700 Subject: [PATCH 02/10] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b4311ec0e93..d2ef41bbfe80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +- [ember] feat: Add more render instrumentation (#2902) ## 5.23.0 From eb4b6e245322dfe96061e5f5e030508d1ceaf1cb Mon Sep 17 00:00:00 2001 From: k-fish Date: Mon, 14 Sep 2020 21:30:12 -0700 Subject: [PATCH 03/10] Fix template lint issue --- .../tests/dummy/app/templates/slow-loading-route/index.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ember/tests/dummy/app/templates/slow-loading-route/index.hbs b/packages/ember/tests/dummy/app/templates/slow-loading-route/index.hbs index 6948ac6acccb..45de2bc44207 100644 --- a/packages/ember/tests/dummy/app/templates/slow-loading-route/index.hbs +++ b/packages/ember/tests/dummy/app/templates/slow-loading-route/index.hbs @@ -1,5 +1,5 @@
+ @rowItems={{@slowLoadingTemplateOnlyItems}} />
From f041b3cffcbdfdae2da65db6fd3f12df02fb1508 Mon Sep 17 00:00:00 2001 From: k-fish Date: Tue, 15 Sep 2020 10:14:29 -0700 Subject: [PATCH 04/10] Fix test helper file name to not cause import issues --- packages/ember/tests/acceptance/sentry-errors-test.js | 2 +- packages/ember/tests/acceptance/sentry-performance-test.js | 2 +- .../tests/helpers/{setup-sentry-test.js => setup-sentry.js} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename packages/ember/tests/helpers/{setup-sentry-test.js => setup-sentry.js} (100%) diff --git a/packages/ember/tests/acceptance/sentry-errors-test.js b/packages/ember/tests/acceptance/sentry-errors-test.js index ad616db1f8f3..19909b1f7b27 100644 --- a/packages/ember/tests/acceptance/sentry-errors-test.js +++ b/packages/ember/tests/acceptance/sentry-errors-test.js @@ -2,7 +2,7 @@ import { test, module } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { find, click, visit } from '@ember/test-helpers'; import { run } from '@ember/runloop'; -import { setupSentryTest } from '../helpers/setup-sentry-test'; +import { setupSentryTest } from '../helpers/setup-sentry'; const defaultAssertOptions = { method: 'POST', diff --git a/packages/ember/tests/acceptance/sentry-performance-test.js b/packages/ember/tests/acceptance/sentry-performance-test.js index 435e2e517798..82fa0f931f0d 100644 --- a/packages/ember/tests/acceptance/sentry-performance-test.js +++ b/packages/ember/tests/acceptance/sentry-performance-test.js @@ -1,7 +1,7 @@ import { test, module } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { find, click, visit } from '@ember/test-helpers'; -import { setupSentryTest } from '../helpers/setup-sentry-test'; +import { setupSentryTest } from '../helpers/setup-sentry'; const SLOW_TRANSITION_WAIT = 3000; diff --git a/packages/ember/tests/helpers/setup-sentry-test.js b/packages/ember/tests/helpers/setup-sentry.js similarity index 100% rename from packages/ember/tests/helpers/setup-sentry-test.js rename to packages/ember/tests/helpers/setup-sentry.js From d65f2367b5bf6b1f33e6655820e41eebe228ee78 Mon Sep 17 00:00:00 2001 From: k-fish Date: Tue, 15 Sep 2020 11:03:06 -0700 Subject: [PATCH 05/10] Update ember to latest --- packages/ember/package.json | 5 +- yarn.lock | 257 ++++++++++++++++++++++++------------ 2 files changed, 173 insertions(+), 89 deletions(-) diff --git a/packages/ember/package.json b/packages/ember/package.json index 48a4ea7a8184..b5fbd08f5eb2 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -35,6 +35,7 @@ "@sentry/types": "5.23.0", "@sentry/utils": "5.23.0", "ember-auto-import": "^1.6.0", + "ember-cli": "^3.21.2", "ember-cli-babel": "^7.20.5", "ember-cli-htmlbars": "^5.1.2", "ember-cli-typescript": "^3.1.4", @@ -52,7 +53,7 @@ "@types/rsvp": "^4.0.3", "babel-eslint": "^10.1.0", "broccoli-asset-rev": "^3.0.0", - "ember-cli": "~3.19.0", + "ember-cli": "^3.19.0", "ember-cli-dependency-checker": "^3.2.0", "ember-cli-inject-live-reload": "^2.0.2", "ember-cli-sri": "^2.1.1", @@ -65,7 +66,7 @@ "ember-qunit": "^4.6.0", "ember-resolver": "^8.0.0", "ember-sinon-qunit": "^5.0.0", - "ember-source": "~3.12.0", + "ember-source": "^3.21.1", "ember-source-channel-url": "^2.0.1", "ember-template-lint": "^2.9.1", "ember-test-selectors": "^4.1.0", diff --git a/yarn.lock b/yarn.lock index 85a467929948..b2e7daf2fe5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48,7 +48,7 @@ invariant "^2.2.4" semver "^5.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.10.2", "@babel/core@^7.2.2", "@babel/core@^7.3.4", "@babel/core@^7.9.6": +"@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.10.2", "@babel/core@^7.2.2", "@babel/core@^7.3.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.5.tgz#1f15e2cca8ad9a1d78a38ddba612f5e7cdbbd330" integrity sha512-O34LQooYVDXPl7QWCdW9p4NR+QlzOr7xShPPJz8GsuCU3/8ua/wqTr7gmnxXv+WBESiGU/G5s16i6tUvHkNb+w== @@ -204,7 +204,7 @@ dependencies: "@babel/types" "^7.10.5" -"@babel/helper-module-imports@^7.10.4": +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== @@ -610,6 +610,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-block-scoping@^7.8.3": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215" + integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-classes@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" @@ -690,7 +697,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-modules-amd@^7.10.1", "@babel/plugin-transform-modules-amd@^7.10.4", "@babel/plugin-transform-modules-amd@^7.10.5", "@babel/plugin-transform-modules-amd@^7.9.6": +"@babel/plugin-transform-modules-amd@^7.10.1", "@babel/plugin-transform-modules-amd@^7.10.4", "@babel/plugin-transform-modules-amd@^7.10.5": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw== @@ -741,6 +748,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-object-assign@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.10.4.tgz#f7c8f54ce8052ccd8b9da9b3358848423221c338" + integrity sha512-6zccDhYEICfMeQqIjuY5G09/yhKzG30DKHJeYBQUHIsJH7c2jXSGvgwRalufLAXAq432OSlsEfAOLlzEsQzxVw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-object-super@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894" @@ -1144,6 +1158,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.7.2": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -3525,11 +3548,32 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= -agent-base@4, agent-base@5, agent-base@6, agent-base@^4.3.0, agent-base@~4.2.1: +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +agent-base@5: version "5.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== +agent-base@6: + version "6.0.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" + integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg== + dependencies: + debug "4" + +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + agentkeepalive@^3.4.1: version "3.5.2" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" @@ -3916,11 +3960,6 @@ ast-types@0.13.3: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.3.tgz#50da3f28d17bdbc7969a3a2d83a0e4a72ae755a7" integrity sha512-XTZ7xGML849LkQP86sWdQzfhwbt3YwIO6MqbX9mUNYY98VKaaVZP7YNNm70IpwecbkkxmfC5IYAzOQ/2p29zRA== -ast-types@0.9.6: - version "0.9.6" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" - integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk= - astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -4282,6 +4321,14 @@ babel-plugin-ember-modules-api-polyfill@^3.1.1: dependencies: ember-rfc176-data "^0.3.15" +babel-plugin-filter-imports@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-filter-imports/-/babel-plugin-filter-imports-4.0.0.tgz#068f8da15236a96a9602c36dc6f4a6eeca70a4f4" + integrity sha512-jDLlxI8QnfKd7PtieH6pl4tZJzymzfCDCPGdTq/grgbiYAikwDPp/oL0IlFJn0HQjLpcLkyYhPKkUVneRESw5w== + dependencies: + "@babel/types" "^7.7.2" + lodash "^4.17.15" + babel-plugin-htmlbars-inline-precompile@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/babel-plugin-htmlbars-inline-precompile/-/babel-plugin-htmlbars-inline-precompile-1.0.0.tgz#a9d2f6eaad8a3f3d361602de593a8cbef8179c22" @@ -4972,7 +5019,7 @@ broccoli-babel-transpiler@^6.5.0: rsvp "^4.8.2" workerpool "^2.3.0" -broccoli-babel-transpiler@^7.4.0, broccoli-babel-transpiler@^7.5.0: +broccoli-babel-transpiler@^7.5.0: version "7.6.0" resolved "https://registry.yarnpkg.com/broccoli-babel-transpiler/-/broccoli-babel-transpiler-7.6.0.tgz#819e6d228b8aa3b4f3b6dfc4b546c13dd304b462" integrity sha512-VjqIl4qe3zEkGjsPA2jgw/jakjfLjw2IpQ62d4f1kArZyMW/cQ6o54VD0Cd6cai4J08nb4WPLu4cr4Z62vr5uw== @@ -5447,7 +5494,7 @@ broccoli-uglify-sourcemap@^3.1.0: walk-sync "^1.1.3" workerpool "^5.0.1" -broccoli@^3.4.1: +broccoli@^3.4.2: version "3.4.2" resolved "https://registry.yarnpkg.com/broccoli/-/broccoli-3.4.2.tgz#a0c2605bea285c50cac304f482b86670630f4701" integrity sha512-OZ0QEyL2i08xJWwhg9Fe0x5IScjBur986QRWrj5mAyHRZqF1nShEz01BPFoLt6L2tqJT0gyZsf8nfUvm8CcJgA== @@ -7613,7 +7660,7 @@ ember-cli-babel@^7.0.0, ember-cli-babel@^7.11.0, ember-cli-babel@^7.12.0, ember- rimraf "^3.0.1" semver "^5.5.0" -ember-cli-babel@^7.21.0, ember-cli-babel@^7.7.0: +ember-cli-babel@^7.21.0: version "7.22.1" resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.22.1.tgz#cad28b89cf0e184c93b863d09bc5ba4ce1d2e453" integrity sha512-kCT8WbC1AYFtyOpU23ESm22a+gL6fWv8Nzwe8QFQ5u0piJzM9MEudfbjADEaoyKTrjMQTDsrWwEf3yjggDsOng== @@ -7896,7 +7943,7 @@ ember-cli-version-checker@^4.1.0: semver "^6.3.0" silent-error "^1.1.1" -ember-cli-version-checker@^5.0.2: +ember-cli-version-checker@^5.0.2, ember-cli-version-checker@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/ember-cli-version-checker/-/ember-cli-version-checker-5.1.1.tgz#3185c526c14671609cbd22ab0d0925787fc84f3d" integrity sha512-YziSW1MgOuVdJSyUY2CKSC4vXrGQIHF6FgygHkJOxYGjZNQYwf5MK0sbliKatvJf7kzDSnXs+r8JLrD74W/A8A== @@ -7905,20 +7952,20 @@ ember-cli-version-checker@^5.0.2: semver "^7.3.2" silent-error "^1.1.1" -ember-cli@~3.19.0: - version "3.19.0" - resolved "https://registry.yarnpkg.com/ember-cli/-/ember-cli-3.19.0.tgz#8c46d1f7c028867c6816da6a60f7a1080be58e4d" - integrity sha512-MVBfC8rSdoQIITXCWX/1eAD/uoXZlH0YSO6NOxshsUaQxpo/SP1bSotwvZo4Hvd8novODNm/zQfVzDwAyJrJHw== +ember-cli@^3.21.2: + version "3.21.2" + resolved "https://registry.yarnpkg.com/ember-cli/-/ember-cli-3.21.2.tgz#af99af5eba9882a326d8528fda31f219300d5591" + integrity sha512-sWOFy74DJ1dZqct0BxGEeGBAEjQUk4ZYZOj+J/PCy3JhDBYHnyBthE+4DdMj4Q211TInoI1UBetpt7x57M7JrA== dependencies: - "@babel/core" "^7.9.6" - "@babel/plugin-transform-modules-amd" "^7.9.6" + "@babel/core" "^7.11.0" + "@babel/plugin-transform-modules-amd" "^7.10.5" amd-name-resolver "^1.3.1" babel-plugin-module-resolver "^4.0.0" bower-config "^1.4.3" bower-endpoint-parser "0.2.2" - broccoli "^3.4.1" + broccoli "^3.4.2" broccoli-amd-funnel "^2.0.1" - broccoli-babel-transpiler "^7.4.0" + broccoli-babel-transpiler "^7.7.0" broccoli-builder "^0.18.14" broccoli-concat "^4.2.4" broccoli-config-loader "^1.0.1" @@ -7933,7 +7980,7 @@ ember-cli@~3.19.0: broccoli-stew "^3.0.0" calculate-cache-key-for-tree "^2.0.0" capture-exit "^2.0.0" - chalk "^3.0.0" + chalk "^4.0.0" ci-info "^2.0.0" clean-base-url "^1.0.0" compression "^1.7.4" @@ -7949,12 +7996,12 @@ ember-cli@~3.19.0: ember-cli-string-utils "^1.1.0" ember-source-channel-url "^2.0.1" ensure-posix-path "^1.1.1" - execa "^1.0.0" + execa "^4.0.3" exit "^0.1.2" express "^4.17.1" filesize "^6.1.0" find-up "^4.1.0" - find-yarn-workspace-root "^1.2.1" + find-yarn-workspace-root "^2.0.0" fixturify-project "^2.1.0" fs-extra "^9.0.0" fs-tree-diff "^2.0.1" @@ -7965,16 +8012,17 @@ ember-cli@~3.19.0: heimdalljs-fs-monitor "^0.2.3" heimdalljs-graph "^1.0.0" heimdalljs-logger "^0.1.10" - http-proxy "^1.18.0" + http-proxy "^1.18.1" inflection "^1.12.0" is-git-url "^1.0.0" + is-language-code "^1.0.9" isbinaryfile "^4.0.6" js-yaml "^3.13.1" json-stable-stringify "^1.0.1" leek "0.0.24" lodash.template "^4.5.0" - markdown-it "^10.0.0" - markdown-it-terminal "0.1.1" + markdown-it "^11.0.0" + markdown-it-terminal "0.2.1" minimatch "^3.0.4" morgan "^1.10.0" nopt "^3.0.6" @@ -7989,15 +8037,16 @@ ember-cli@~3.19.0: sane "^4.1.0" semver "^7.3.2" silent-error "^1.1.1" - sort-package-json "^1.42.2" + sort-package-json "^1.44.0" symlink-or-copy "^1.3.1" temp "0.9.1" - testem "^3.1.0" + testem "^3.2.0" tiny-lr "^1.1.1" - tree-sync "^2.0.0" - uuid "^7.0.3" - walk-sync "^2.1.0" + tree-sync "^2.1.0" + uuid "^8.3.0" + walk-sync "^2.2.0" watch-detector "^1.0.0" + workerpool "^6.0.0" yam "^1.0.0" ember-compatibility-helpers@^1.1.2: @@ -8080,13 +8129,6 @@ ember-rfc176-data@^0.3.15: resolved "https://registry.yarnpkg.com/ember-rfc176-data/-/ember-rfc176-data-0.3.15.tgz#af3f1da5a0339b6feda380edc2f7190e0f416c2d" integrity sha512-GPKa7zRDBblRy0orxTXt5yrpp/Pf5CkuRFSIR8qMFDww0CqCKjCRwdZnWYzCM4kAEfZnXRIDDefe1tBaFw7v7w== -ember-router-generator@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/ember-router-generator/-/ember-router-generator-1.2.3.tgz#8ed2ca86ff323363120fc14278191e9e8f1315ee" - integrity sha1-jtLKhv8yM2MSD8FCeBkeno8TFe4= - dependencies: - recast "^0.11.3" - ember-router-generator@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ember-router-generator/-/ember-router-generator-2.0.0.tgz#d04abfed4ba8b42d166477bbce47fccc672dbde0" @@ -8129,25 +8171,35 @@ ember-source-channel-url@^2.0.1: dependencies: got "^8.0.1" -ember-source@~3.12.0: - version "3.12.4" - resolved "https://registry.yarnpkg.com/ember-source/-/ember-source-3.12.4.tgz#c7d43954097aafefaa14fc88e3f3466a5e4bb254" - integrity sha512-e4c9ZB1aO2HxwRSWjCuKtZNhRkCxwZ4bENe8jUEreIPXp0hmiuviRMANAkRaMGbIXm0/RbAuYDX+KBmQlIY/Qw== +ember-source@^3.21.1: + version "3.21.1" + resolved "https://registry.yarnpkg.com/ember-source/-/ember-source-3.21.1.tgz#e1bfb20a3db91c21415256e5949a32085a2c23ab" + integrity sha512-zG3FCd++91/OQgYeMeGJequ1u0uLFK3xeilHYL4CNrwgQAit0vju/s8x7H4fVnVxOxYhsFUXAsTMSsvgqbaqpQ== dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/plugin-transform-block-scoping" "^7.8.3" + "@babel/plugin-transform-object-assign" "^7.8.3" + "@ember/edition-utils" "^1.2.0" + babel-plugin-debug-macros "^0.3.3" + babel-plugin-filter-imports "^4.0.0" + broccoli-concat "^4.2.4" + broccoli-debug "^0.6.4" broccoli-funnel "^2.0.2" - broccoli-merge-trees "^3.0.2" - chalk "^2.4.2" - ember-cli-babel "^7.7.0" + broccoli-merge-trees "^4.2.0" + chalk "^4.0.0" + ember-cli-babel "^7.19.0" ember-cli-get-component-path-option "^1.0.0" ember-cli-is-package-missing "^1.0.0" ember-cli-normalize-entity-name "^1.0.0" ember-cli-path-utils "^1.0.0" ember-cli-string-utils "^1.1.0" - ember-cli-version-checker "^3.1.3" - ember-router-generator "^1.2.3" + ember-cli-version-checker "^5.1.1" + ember-router-generator "^2.0.0" inflection "^1.12.0" - jquery "^3.4.1" - resolve "^1.11.1" + jquery "^3.5.0" + resolve "^1.17.0" + semver "^6.1.1" + silent-error "^1.1.1" ember-template-lint@^2.9.1: version "2.9.1" @@ -8453,6 +8505,18 @@ es6-object-assign@^1.1.0: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + escalade@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" @@ -8717,11 +8781,6 @@ esprima@~3.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.0.0.tgz#53cf247acda77313e551c3aa2e73342d3fb4f7d9" integrity sha1-U88kes2ncxPlUcOqLnM0LT+099k= -esprima@~3.1.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= - esquery@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" @@ -8856,6 +8915,21 @@ execa@^3.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2" + integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + exists-sync@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/exists-sync/-/exists-sync-0.0.4.tgz#9744c2c428cc03b01060db454d4b12f0ef3c8879" @@ -9241,7 +9315,7 @@ find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-yarn-workspace-root@^1.1.0, find-yarn-workspace-root@^1.2.1: +find-yarn-workspace-root@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz#40eb8e6e7c2502ddfaa2577c176f221422f860db" integrity sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q== @@ -9249,6 +9323,13 @@ find-yarn-workspace-root@^1.1.0, find-yarn-workspace-root@^1.2.1: fs-extra "^4.0.3" micromatch "^3.1.4" +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + findup-sync@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-4.0.0.tgz#956c9cdde804052b881b428512905c4a5f2cdef0" @@ -10457,7 +10538,7 @@ http-proxy-agent@^4.0.0: agent-base "6" debug "4" -http-proxy@^1.13.0, http-proxy@^1.13.1, http-proxy@^1.18.0: +http-proxy@^1.13.0, http-proxy@^1.13.1, http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== @@ -11014,6 +11095,11 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-language-code@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-language-code/-/is-language-code-1.0.10.tgz#19bacf9f0f97368853a3544cdba53c9075d55522" + integrity sha512-kp9PYib4sMAZCo56ViCWtjtYWz4ePZQ/QFzMvdxYEm2uSJaGjaEgOt3AkIR8SBxXffcV7ZYYkEGZpWa+No2Xiw== + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -11706,7 +11792,7 @@ jest@^24.7.1: import-local "^2.0.0" jest-cli "^24.9.0" -jquery@^3.4.1: +jquery@^3.5.0: version "3.5.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5" integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg== @@ -12367,6 +12453,13 @@ linkify-it@^2.0.0: dependencies: uc.micro "^1.0.1" +linkify-it@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.2.tgz#f55eeb8bc1d3ae754049e124ab3bb56d97797fb8" + integrity sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ== + dependencies: + uc.micro "^1.0.1" + livereload-js@^2.3.0: version "2.4.0" resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" @@ -12896,10 +12989,10 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-it-terminal@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/markdown-it-terminal/-/markdown-it-terminal-0.1.1.tgz#7665d28a97f6f7f61f8738304d409a8505620192" - integrity sha512-8v4pHGEh7eiw+UbD28PRyrpu+WrnRR/HefC6NRs+Ttbk1ZQoOY6ViMrkZcdO9Y+PoBsfxNsmiJZtG9BRHEGZ2A== +markdown-it-terminal@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/markdown-it-terminal/-/markdown-it-terminal-0.2.1.tgz#670fd5ea824a7dcaa1591dcbeef28bf70aff1705" + integrity sha512-e8hbK9L+IyFac2qY05R7paP+Fqw1T4pSQW3miK3VeG9QmpqBjg5Qzjv/v6C7YNxSNRS2Kp8hUFtm5lWU9eK4lw== dependencies: ansi-styles "^3.0.0" cardinal "^1.0.0" @@ -12907,14 +13000,14 @@ markdown-it-terminal@0.1.1: lodash.merge "^4.6.2" markdown-it "^8.3.1" -markdown-it@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc" - integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg== +markdown-it@^11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-11.0.1.tgz#b54f15ec2a2193efa66dda1eb4173baea08993d6" + integrity sha512-aU1TzmBKcWNNYvH9pjq6u92BML+Hz3h5S/QpfTFwiQF852pLT+9qHsrhM9JYipkOXZxGn+sGH8oyJE9FD9WezQ== dependencies: argparse "^1.0.7" entities "~2.0.0" - linkify-it "^2.0.0" + linkify-it "^3.0.1" mdurl "^1.0.1" uc.micro "^1.0.5" @@ -14981,7 +15074,7 @@ printf@^0.5.1: resolved "https://registry.yarnpkg.com/printf/-/printf-0.5.3.tgz#8b7eec278d886833312238b2bf42b2b6f250880a" integrity sha512-t3lYN6vPU5PZXDiEZZqoyXvN8wCsBfi8gPoxTKo2e5hhV673t/KUh+mfO8P8lCOCDC/BWcOGIxKyebxc5FuqLA== -private@^0.1.6, private@^0.1.8, private@~0.1.5: +private@^0.1.6, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== @@ -15560,16 +15653,6 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" -recast@^0.11.3: - version "0.11.23" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3" - integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM= - dependencies: - ast-types "0.9.6" - esprima "~3.1.0" - private "~0.1.5" - source-map "~0.5.0" - recast@^0.18.1: version "0.18.10" resolved "https://registry.yarnpkg.com/recast/-/recast-0.18.10.tgz#605ebbe621511eb89b6356a7e224bff66ed91478" @@ -16303,7 +16386,7 @@ semver@7.3.2, semver@^7.0.0, semver@^7.2.1, semver@^7.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== -semver@^6.0.0, semver@^6.1.0, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -16713,7 +16796,7 @@ sort-object-keys@^1.1.3: resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg== -sort-package-json@^1.42.2: +sort-package-json@^1.44.0: version "1.44.0" resolved "https://registry.yarnpkg.com/sort-package-json/-/sort-package-json-1.44.0.tgz#470330be868f8a524a4607b26f2a0233e93d8b6d" integrity sha512-u9GUZvpavUCXV5SbEqXu9FRbsJrYU6WM10r3zA0gymGPufK5X82MblCLh9GW9l46pXKEZvK+FA3eVTqC4oMp4A== @@ -16778,7 +16861,7 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0, source-map@~0.5.3: +source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -17484,7 +17567,7 @@ test-exclude@^5.2.3: read-pkg-up "^4.0.0" require-main-filename "^2.0.0" -testem@^3.1.0: +testem@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/testem/-/testem-3.2.0.tgz#9924481f6a3b23e350fa77bb251c64d801c4c9a7" integrity sha512-FkFzNRCIzCxjbNSTxIQSC2tWn1Q2MTR/GTxusSw6uZA4byEQ7wc86TKutNnoCyZ5XIaD9wo4q+dmlK0GUEqFVA== @@ -17749,7 +17832,7 @@ tree-sync@^1.2.2: quick-temp "^0.1.5" walk-sync "^0.3.3" -tree-sync@^2.0.0: +tree-sync@^2.0.0, tree-sync@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tree-sync/-/tree-sync-2.1.0.tgz#31cbbd41f2936f5390b61e8c9d7cb27e75a212fe" integrity sha512-OLWW+Nd99NOM53aZ8ilT/YpEiOo6mXD3F4/wLbARqybSZ3Jb8IxHK5UGVbZaae0wtXAyQshVV+SeqVBik+Fbmw== @@ -18271,10 +18354,10 @@ uuid@^3.0.1, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== +uuid@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" + integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== v8-compile-cache@^2.0.3: version "2.1.1" From 8cee6a3689498e8a88ba04c2bcf057dd9633f44d Mon Sep 17 00:00:00 2001 From: k-fish Date: Tue, 15 Sep 2020 11:03:54 -0700 Subject: [PATCH 06/10] Ensure disabling performance also disabled measures from being added --- packages/ember/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ember/index.js b/packages/ember/index.js index cb5011e4ab8e..d173fd3385e3 100644 --- a/packages/ember/index.js +++ b/packages/ember/index.js @@ -15,8 +15,8 @@ module.exports = { contentFor(type, config) { const addonConfig = config['@sentry/ember'] || {}; - const { disableInitialLoadInstrumentation } = addonConfig; - if (disableInitialLoadInstrumentation) { + const { disablePerformance, disableInitialLoadInstrumentation } = addonConfig; + if (disablePerformance || disableInitialLoadInstrumentation) { return; } if (type === 'head') { From c720aa1f7f571fb2714cc41c5eabcad6ef0f4b2a Mon Sep 17 00:00:00 2001 From: k-fish Date: Tue, 15 Sep 2020 13:33:11 -0700 Subject: [PATCH 07/10] Add yarn link to scripts --- packages/ember/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ember/package.json b/packages/ember/package.json index b5fbd08f5eb2..41b4c205fb40 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -18,6 +18,7 @@ }, "scripts": { "build": "ember build --environment=production", + "link:yarn": "yarn link", "lint": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*", "lint:hbs": "ember-template-lint .", "lint:js": "eslint . --cache --cache-location '../../eslintcache/'", From d69385310758e1daece9486657dddb150e8d3a7f Mon Sep 17 00:00:00 2001 From: k-fish Date: Mon, 21 Sep 2020 09:43:15 -0700 Subject: [PATCH 08/10] Fix typo in readme --- packages/ember/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ember/README.md b/packages/ember/README.md index ad37ba1b019c..597384722323 100644 --- a/packages/ember/README.md +++ b/packages/ember/README.md @@ -89,7 +89,7 @@ class MyRoute extends Route { } } -export default instrumentRoutePerformance(SlowLoadingRoute); +export default instrumentRoutePerformance(MyRoute); ``` #### Runloop From 31df05c615078dd2d46b04ec375e723aaf4480a4 Mon Sep 17 00:00:00 2001 From: k-fish Date: Mon, 21 Sep 2020 09:43:25 -0700 Subject: [PATCH 09/10] Add performance check in initial load --- .../ember/addon/instance-initializers/sentry-performance.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/ember/addon/instance-initializers/sentry-performance.ts b/packages/ember/addon/instance-initializers/sentry-performance.ts index 161ad19f87c6..69398a200686 100644 --- a/packages/ember/addon/instance-initializers/sentry-performance.ts +++ b/packages/ember/addon/instance-initializers/sentry-performance.ts @@ -266,6 +266,12 @@ function _instrumentComponents(config: typeof environmentConfig['@sentry/ember'] function _instrumentInitialLoad(config: typeof environmentConfig['@sentry/ember']) { const startName = '@sentry/ember:initial-load-start'; const endName = '@sentry/ember:initial-load-end'; + + const HAS_PERFORMANCE = window.performance && window.performance.clearMarks && window.performance.clearMeasures + if (!HAS_PERFORMANCE) { + return; + } + if (config.disableInitialLoadInstrumentation) { performance.clearMarks(startName); performance.clearMarks(endName); From af4b45858847be2930e3dde5d8e8c7b5d8bba491 Mon Sep 17 00:00:00 2001 From: k-fish Date: Mon, 21 Sep 2020 11:58:52 -0700 Subject: [PATCH 10/10] Fix performance check and split into two so that clearing marks still happens if the rest of performance isn't available --- .../addon/instance-initializers/sentry-performance.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/ember/addon/instance-initializers/sentry-performance.ts b/packages/ember/addon/instance-initializers/sentry-performance.ts index 69398a200686..ee6404641f6a 100644 --- a/packages/ember/addon/instance-initializers/sentry-performance.ts +++ b/packages/ember/addon/instance-initializers/sentry-performance.ts @@ -267,7 +267,9 @@ function _instrumentInitialLoad(config: typeof environmentConfig['@sentry/ember' const startName = '@sentry/ember:initial-load-start'; const endName = '@sentry/ember:initial-load-end'; - const HAS_PERFORMANCE = window.performance && window.performance.clearMarks && window.performance.clearMeasures + const { performance } = window; + const HAS_PERFORMANCE = performance && performance.clearMarks && performance.clearMeasures; + if (!HAS_PERFORMANCE) { return; } @@ -277,6 +279,13 @@ function _instrumentInitialLoad(config: typeof environmentConfig['@sentry/ember' performance.clearMarks(endName); return; } + + // Split performance check in two so clearMarks still happens even if timeOrigin isn't available. + const HAS_PERFORMANCE_TIMING = + performance.measure && performance.getEntriesByName && performance.timeOrigin !== undefined; + if (!HAS_PERFORMANCE_TIMING) { + return; + } const measureName = '@sentry/ember:initial-load'; performance.measure(measureName, startName, endName);