From 94404370c4bd856d0df18bbd66fcc38221eac953 Mon Sep 17 00:00:00 2001 From: Sebastian Serth Date: Mon, 20 May 2024 14:00:55 +0200 Subject: [PATCH] Upgrade Sentry to v8 and remove custom Dependabot grouping As part of the upgrade process, we need to rework the tracing instrumentation. Now, we are just wrapping all async functions in a new sentry transaction, which will automatically end once the function returns. Further, the structure of the Sentry packages got reworked, so that we only need a single package by now. This removes the need to group dependabot updates. Co-authored-by: Jan Graichen --- .github/dependabot.yml | 4 - .github/workflows/dependabot.yml | 6 - app/assets/javascripts/base.js | 2 +- app/assets/javascripts/community_solution.js | 19 +- app/assets/javascripts/editor/editor.js.erb | 28 ++- app/assets/javascripts/editor/evaluation.js | 20 +- app/assets/javascripts/editor/execution.js | 82 ++++---- .../editor/participantsupport.js.erb | 69 +++---- app/assets/javascripts/editor/submissions.js | 96 ++++----- app/javascript/application.js | 17 +- package.json | 5 +- yarn.lock | 186 ++++++------------ 12 files changed, 234 insertions(+), 300 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9a23cbff..edcccbaf 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -22,10 +22,6 @@ updates: labels: - dependencies - javascript - groups: - sentry-javascript: - patterns: - - "@sentry*" - package-ecosystem: github-actions directory: "/" diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index 4d70d8f9..66e3c584 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -13,9 +13,3 @@ jobs: uses: ahmadnassri/action-dependabot-auto-merge@v2 with: github-token: ${{ secrets.OPENHPI_BOT_TOKEN }} - - name: Automerge Sentry PRs - run: gh pr merge --auto --rebase "$PR_URL" # Use Github CLI to merge automatically the PR - if: contains(github.event.pull_request.title, 'sentry') - env: - PR_URL: ${{ github.event.pull_request.html_url }} - GITHUB_TOKEN: ${{ secrets.OPENHPI_BOT_TOKEN }} diff --git a/app/assets/javascripts/base.js b/app/assets/javascripts/base.js index 1cf0effc..62a08df5 100644 --- a/app/assets/javascripts/base.js +++ b/app/assets/javascripts/base.js @@ -46,7 +46,7 @@ $(document).on('turbolinks:load', function() { const sentrySettings = $('meta[name="sentry"]') // Workaround for Turbolinks: We must not re-initialize the Relay object when visiting another page - if (sentrySettings && sentrySettings.data()['enabled'] && !Sentry.Replay.prototype._isInitialized) { + if (sentrySettings && sentrySettings.data()['enabled'] && Sentry.getReplay() === undefined) { Sentry.init({ dsn: sentrySettings.data('dsn'), attachStacktrace: true, diff --git a/app/assets/javascripts/community_solution.js b/app/assets/javascripts/community_solution.js index d2730b19..d9638acc 100644 --- a/app/assets/javascripts/community_solution.js +++ b/app/assets/javascripts/community_solution.js @@ -27,14 +27,15 @@ $(document).on('turbolinks:load', function() { function submitCode(event) { const button = $(event.target) || $('#submit'); - this.startSentryTransaction(button); - const submission = await this.createSubmission(button, null).catch(this.ajaxError.bind(this)); - if (!submission) return; - if (!submission.redirect) return; + this.newSentryTransaction(button, async () => { + const submission = await this.createSubmission(button, null).catch(this.ajaxError.bind(this)); + if (!submission) return; + if (!submission.redirect) return; - this.autosaveIfChanged(); - this.stopCode(event); - this.editors = []; - Turbolinks.clearCache(); - Turbolinks.visit(submission.redirect); + this.autosaveIfChanged(); + this.stopCode(event); + this.editors = []; + Turbolinks.clearCache(); + Turbolinks.visit(submission.redirect); + }); } diff --git a/app/assets/javascripts/editor/editor.js.erb b/app/assets/javascripts/editor/editor.js.erb index e9bd9e5d..4734e3e6 100644 --- a/app/assets/javascripts/editor/editor.js.erb +++ b/app/assets/javascripts/editor/editor.js.erb @@ -202,15 +202,25 @@ var CodeOceanEditor = { $('button i.fa-spin').removeClass('d-inline-block').addClass('d-none'); }, - startSentryTransaction: function (initiator) { - const cause = initiator.data('cause') || initiator.prop('id'); - this.sentryTransaction = window.SentryUtils.startIdleTransaction( - Sentry.getCurrentHub(), - { name: cause, op: "transaction" }, - 0, // Idle Timeout - window.SentryUtils.TRACING_DEFAULTS.finalTimeout, - true); // onContext - Sentry.getCurrentHub().configureScope(scope => scope.setSpan(this.sentryTransaction)); + newSentryTransaction: function (initiator, callback) { + // based on Sentry recommendation. + // See https://github.com/getsentry/sentry-javascript/issues/12116 + return Sentry.continueTrace({ sentryTrace: '', baggage: '' }, () => { + // inside of this we have a new trace! + return Sentry.withActiveSpan(null, () => { + // inside of this there is no parent span, no matter what! + const cause = initiator.data('cause') || initiator.prop('id'); + return Sentry.startSpan({name: cause, op: "transaction", forceTransaction: true}, async () => { + // Execute the desired custom code + try { + return await callback(); + } catch (error) { + console.error(error); + Sentry.captureException(error, {mechanism: {handled: false}}); + } + }); + }); + }); }, resizeAceEditors: function (own_solution = false) { diff --git a/app/assets/javascripts/editor/evaluation.js b/app/assets/javascripts/editor/evaluation.js index 8f7a7fcd..2c2d143f 100644 --- a/app/assets/javascripts/editor/evaluation.js +++ b/app/assets/javascripts/editor/evaluation.js @@ -2,7 +2,6 @@ CodeOceanEditorEvaluation = { // A list of non-printable characters that are not allowed in the code output. // Taken from https://stackoverflow.com/a/69024306 nonPrintableRegEx: /[\u0000-\u0008\u000B\u000C\u000F-\u001F\u007F-\u009F\u2000-\u200F\u2028-\u202F\u205F-\u206F\u3000\uFEFF]/g, - sentryTransaction: null, /** * Scoring-Functions @@ -10,17 +9,18 @@ CodeOceanEditorEvaluation = { scoreCode: function (event) { event.preventDefault(); const cause = $('#assess'); - this.startSentryTransaction(cause); - this.stopCode(event); - this.clearScoringOutput(); - $('#submit').addClass("d-none"); + this.newSentryTransaction(cause, async () => { + this.stopCode(event); + this.clearScoringOutput(); + $('#submit').addClass("d-none"); - const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); - if (!submission) return; + const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); + if (!submission) return; - this.showSpinner($('#assess')); - $('#score_div').removeClass('d-none'); - await this.socketScoreCode(submission.id); + this.showSpinner($('#assess')); + $('#score_div').removeClass('d-none'); + await this.socketScoreCode(submission.id); + }); }, handleScoringResponse: function (results) { diff --git a/app/assets/javascripts/editor/execution.js b/app/assets/javascripts/editor/execution.js index 1622ee98..ca8a52d2 100644 --- a/app/assets/javascripts/editor/execution.js +++ b/app/assets/javascripts/editor/execution.js @@ -8,7 +8,7 @@ CodeOceanEditorWebsocket = { params.protocol = this.webSocketProtocol; params._options = true; - // 2. Create a new Sentry transaction. + // 2. Create a new Sentry span. // Since we want to group similar URLs, we use the URL without the ID and filename as the description. const cleanedUrl = urlHelper({ ...params, @@ -16,54 +16,54 @@ CodeOceanEditorWebsocket = { ...(params.filename && {filename: '*'}), // Overwrite the filename with a wildcard only if it is present. }); const sentryDescription = `WebSocket ${cleanedUrl}`; - const span = this.sentryTransaction?.startChild({op: 'websocket.client', description: sentryDescription, data: {...params}}) + return Sentry.startSpan({op: 'websocket.client', name: sentryDescription, attributes: {...params}}, async webSocketSpan => { - // 3. Create the actual WebSocket URL. - // This URL might contain Sentry Tracing headers to propagate the Sentry transaction. - if (span) { - const dynamicContext = this.sentryTransaction.getDynamicSamplingContext(); - const baggage = SentryUtils.dynamicSamplingContextToSentryBaggageHeader(dynamicContext); - if (baggage) { - params.HTTP_SENTRY_TRACE = span.toTraceparent(); - params.HTTP_BAGGAGE = baggage; + // 3. Create the actual WebSocket URL. + // This URL might contain Sentry Tracing headers to propagate the Sentry transaction. + if (webSocketSpan) { + params.HTTP_SENTRY_TRACE = Sentry.spanToTraceHeader(webSocketSpan); + + const baggage = Sentry.spanToBaggageHeader(webSocketSpan); + if (baggage) { + params.HTTP_BAGGAGE = Sentry.spanToBaggageHeader(webSocketSpan); + } } - } - const url = urlHelper({...params}); + const url = urlHelper({...params}); - // 4. Connect to the given URL. - this.websocket = new CommandSocket(url, - function (evt) { - this.resetOutputTab(); - }.bind(this) - ); + // 4. Connect to the given URL. + this.websocket = new CommandSocket(url, + function (evt) { + this.resetOutputTab(); + }.bind(this) + ); - // Attach custom handlers for messages received. - setupFunction(this.websocket); + // Attach custom handlers for messages received. + setupFunction(this.websocket); - CodeOceanEditorWebsocket.websocket = this.websocket; + CodeOceanEditorWebsocket.websocket = this.websocket; - // Create and return a new Promise. It will only resolve (or fail) once the connection has ended. - return new Promise((resolve, reject) => { - this.websocket.onError(this.showWebsocketError.bind(this)); + // Create and return a new Promise. It will only resolve (or fail) once the connection has ended. + return new Promise((resolve, reject) => { + this.websocket.onError(this.showWebsocketError.bind(this)); - // Remove event listeners for Promise handling. - // This is especially useful in case of an error, where a `close` event might follow the `error` event. - const teardown = () => { - this.websocket.websocket.removeEventListener(closeListener); - this.websocket.websocket.removeEventListener(errorListener); - }; + // Remove event listeners for Promise handling. + // This is especially useful in case of an error, where a `close` event might follow the `error` event. + const teardown = () => { + this.websocket.websocket.removeEventListener(closeListener); + this.websocket.websocket.removeEventListener(errorListener); + }; - // We are using event listeners (and not `onError` or `onClose`) here, since these listeners should never be overwritten. - // With `onError` or `onClose`, a new assignment would overwrite a previous one. - const closeListener = this.websocket.websocket.addEventListener('close', () => { - span?.finish(); - resolve(); - teardown(); - }); - const errorListener = this.websocket.websocket.addEventListener('error', (error) => { - reject(error); - teardown(); - this.websocket.killWebSocket(); // In case of error, ensure we always close the connection. + // We are using event listeners (and not `onError` or `onClose`) here, since these listeners should never be overwritten. + // With `onError` or `onClose`, a new assignment would overwrite a previous one. + const closeListener = this.websocket.websocket.addEventListener('close', () => { + resolve(); + teardown(); + }); + const errorListener = this.websocket.websocket.addEventListener('error', (error) => { + reject(error); + teardown(); + this.websocket.killWebSocket(); // In case of error, ensure we always close the connection. + }); }); }); }, diff --git a/app/assets/javascripts/editor/participantsupport.js.erb b/app/assets/javascripts/editor/participantsupport.js.erb index a782ebe9..0afc3fa3 100644 --- a/app/assets/javascripts/editor/participantsupport.js.erb +++ b/app/assets/javascripts/editor/participantsupport.js.erb @@ -110,46 +110,47 @@ CodeOceanEditorRequestForComments = { const cause = $('#requestComments'); const editor = $('#editor') const questionElement = $('#question') - this.startSentryTransaction(cause); - questionElement.prop("disabled", true); - $('#closeAskForCommentsButton').addClass('d-none'); + this.newSentryTransaction(cause, async () => { + questionElement.prop("disabled", true); + $('#closeAskForCommentsButton').addClass('d-none'); - const exercise_id = editor.data('exercise-id'); - const file_id = $('.editor').data('id'); - const question = questionElement.val(); + const exercise_id = editor.data('exercise-id'); + const file_id = $('.editor').data('id'); + const question = questionElement.val(); - const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); - if (!submission) return; + const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); + if (!submission) return; - this.showSpinner($('#askForCommentsButton')); + this.showSpinner($('#askForCommentsButton')); - const response = await $.ajax({ - method: 'POST', - url: Routes.request_for_comments_path(), - data: { - request_for_comment: { - exercise_id: exercise_id, - file_id: file_id, - submission_id: submission.id, - question: question + const response = await $.ajax({ + method: 'POST', + url: Routes.request_for_comments_path(), + data: { + request_for_comment: { + exercise_id: exercise_id, + file_id: file_id, + submission_id: submission.id, + question: question + } } + }).catch(this.ajaxError.bind(this)); + + bootstrap.Modal.getInstance($('#comment-modal')).hide(); + this.hideSpinner(); + $('#question').prop("disabled", false).val(''); + $('#closeAskForCommentsButton').removeClass('d-none'); + $('#askForCommentsButton').one('click', this.requestComments.bind(this)); + + // we disabled the button to prevent that the user spams RFCs, but decided against this now. + //var button = $('#requestComments'); + //button.prop('disabled', true); + + if (response) { + await this.runSubmission(submission); + $.flash.success({text: $('#askForCommentsButton').data('message-success')}); } - }).catch(this.ajaxError.bind(this)); - - bootstrap.Modal.getInstance($('#comment-modal')).hide(); - this.hideSpinner(); - $('#question').prop("disabled", false).val(''); - $('#closeAskForCommentsButton').removeClass('d-none'); - $('#askForCommentsButton').one('click', this.requestComments.bind(this)); - - // we disabled the button to prevent that the user spams RFCs, but decided against this now. - //var button = $('#requestComments'); - //button.prop('disabled', true); - - if (response) { - await this.runSubmission(submission); - $.flash.success({text: $('#askForCommentsButton').data('message-success')}); - } + }); } }; diff --git a/app/assets/javascripts/editor/submissions.js b/app/assets/javascripts/editor/submissions.js index 909d3f50..0ed45b53 100644 --- a/app/assets/javascripts/editor/submissions.js +++ b/app/assets/javascripts/editor/submissions.js @@ -108,19 +108,20 @@ CodeOceanEditorSubmissions = { }, resetCode: function(initiator, onlyActiveFile = false) { - this.startSentryTransaction(initiator); - this.showSpinner(initiator); + this.newSentryTransaction(initiator, async () => { + this.showSpinner(initiator); - const response = await this.ajax({ - method: 'GET', - url: $('#start-over').data('url') || $('#start-over-active-file').data('url') - }).catch(this.ajaxError.bind(this)); + const response = await this.ajax({ + method: 'GET', + url: $('#start-over').data('url') || $('#start-over-active-file').data('url') + }).catch(this.ajaxError.bind(this)); - this.hideSpinner(); + this.hideSpinner(); - if (!response) return; - App.synchronized_editor?.reset_content(response); - this.setEditorContent(response, onlyActiveFile); + if (!response) return; + App.synchronized_editor?.reset_content(response); + this.setEditorContent(response, onlyActiveFile); + }); }, setEditorContent: function(new_content, onlyActiveFile = false) { @@ -140,32 +141,33 @@ CodeOceanEditorSubmissions = { renderCode: function(event) { event.preventDefault(); const cause = $('#render'); - this.startSentryTransaction(cause); - if (!cause.is(':visible')) return; + this.newSentryTransaction(cause, async () => { + if (!cause.is(':visible')) return; - const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); - if (!submission) return; - if (submission.render_url === undefined) return; + const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); + if (!submission) return; + if (submission.render_url === undefined) return; - const active_file = CodeOceanEditor.active_file.filename; - const desired_file = submission.render_url.filter(hash => hash.filepath === active_file); - const url = desired_file[0].url; + const active_file = CodeOceanEditor.active_file.filename; + const desired_file = submission.render_url.filter(hash => hash.filepath === active_file); + const url = desired_file[0].url; - // Allow to open the new tab even in Safari. - // See: https://stackoverflow.com/a/70463940 - setTimeout(() => { - var pop_up_window = window.open(url, '_blank'); - if (pop_up_window) { - pop_up_window.onerror = function (message) { - this.clearOutput(); - this.printOutput({ - stderr: message - }, true, 0); - this.sendError(message, submission.id); - this.showOutputBar(); - }; - } - }) + // Allow to open the new tab even in Safari. + // See: https://stackoverflow.com/a/70463940 + setTimeout(() => { + var pop_up_window = window.open(url, '_blank'); + if (pop_up_window) { + pop_up_window.onerror = function (message) { + this.clearOutput(); + this.printOutput({ + stderr: message + }, true, 0); + this.sendError(message, submission.id); + this.showOutputBar(); + }; + } + }) + }); }, /** @@ -174,14 +176,15 @@ CodeOceanEditorSubmissions = { runCode: function(event) { event.preventDefault(); const cause = $('#run'); - this.startSentryTransaction(cause); - this.stopCode(event); - if (!cause.is(':visible')) return; + this.newSentryTransaction(cause, async () => { + this.stopCode(event); + if (!cause.is(':visible')) return; - const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); - if (!submission) return; + const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); + if (!submission) return; - await this.runSubmission(submission); + await this.runSubmission(submission); + }); }, runSubmission: async function (submission) { @@ -196,15 +199,16 @@ CodeOceanEditorSubmissions = { testCode: function(event) { event.preventDefault(); const cause = $('#test'); - this.startSentryTransaction(cause); - if (!cause.is(':visible')) return; + this.newSentryTransaction(cause, async () => { + if (!cause.is(':visible')) return; - const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); - if (!submission) return; + const submission = await this.createSubmission(cause, null).catch(this.ajaxError.bind(this)); + if (!submission) return; - this.showSpinner($('#test')); - $('#score_div').addClass('d-none'); - await this.socketTestCode(submission.id, CodeOceanEditor.active_file.filename); + this.showSpinner($('#test')); + $('#score_div').addClass('d-none'); + await this.socketTestCode(submission.id, CodeOceanEditor.active_file.filename); + }); }, /** diff --git a/app/javascript/application.js b/app/javascript/application.js index 487e82fe..deff96f5 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -16,9 +16,6 @@ import 'jstree'; import * as _ from 'underscore'; import * as d3 from 'd3'; import * as Sentry from '@sentry/browser'; -import * as SentryIntegration from '@sentry/integrations'; -import { startIdleTransaction, TRACING_DEFAULTS } from '@sentry/core'; -import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import 'sorttable'; window.bootstrap = bootstrap; // Publish bootstrap in global namespace window._ = _; // Publish underscore's `_` in global namespace @@ -26,15 +23,15 @@ window.d3 = d3; // Publish d3 in global namespace window.Sentry = Sentry; // Publish sentry in global namespace window.SentryIntegrations = function() { // Publish sentry integration in global namespace return [ - new SentryIntegration.ReportingObserver(), - new SentryIntegration.ExtraErrorData(), - new SentryIntegration.HttpClient(), - new Sentry.BrowserProfilingIntegration(), - new Sentry.BrowserTracing(), - new Sentry.Replay(), + Sentry.browserProfilingIntegration(), + Sentry.browserTracingIntegration(), + Sentry.extraErrorDataIntegration(), + Sentry.httpClientIntegration(), + Sentry.replayIntegration(), + Sentry.reportingObserverIntegration(), + Sentry.sessionTimingIntegration(), ] }; -window.SentryUtils = { dynamicSamplingContextToSentryBaggageHeader, startIdleTransaction, TRACING_DEFAULTS }; // CSS import 'chosen-js/chosen.css'; diff --git a/package.json b/package.json index 6862a54f..fd7f682a 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,7 @@ "@egjs/hammerjs": "^2.0.17", "@fortawesome/fontawesome-free": "^6.5.2", "@popperjs/core": "^2.11.8", - "@sentry/browser": "^7.110.1", - "@sentry/core": "^7.114.0", - "@sentry/integrations": "^7.114.0", - "@sentry/utils": "^7.114.0", + "@sentry/browser": "^8.4.0", "@toast-ui/editor": "^3.2.2", "@webpack-cli/serve": "^2.0.5", "ace-builds": "^1.34.0", diff --git a/yarn.lock b/yarn.lock index 2dcf132d..1692f7bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1813,128 +1813,90 @@ __metadata: languageName: node linkType: hard -"@sentry-internal/feedback@npm:7.110.1": - version: 7.110.1 - resolution: "@sentry-internal/feedback@npm:7.110.1" +"@sentry-internal/browser-utils@npm:8.4.0": + version: 8.4.0 + resolution: "@sentry-internal/browser-utils@npm:8.4.0" dependencies: - "@sentry/core": "npm:7.110.1" - "@sentry/types": "npm:7.110.1" - "@sentry/utils": "npm:7.110.1" - checksum: 10c0/5429a7dcd14986770647392558ed0d09f31d33d6771688304f90e88feff9b0af6edaa0195e60c5bdce6a8518409f6314b9d03293b661fc3e1b74ef1c154e4595 + "@sentry/core": "npm:8.4.0" + "@sentry/types": "npm:8.4.0" + "@sentry/utils": "npm:8.4.0" + checksum: 10c0/9e6709100182f8586ef24d304632eedee1f0f257a588bd861df779dee41064cc97afce53b22a4b669e05581f25bc61d7fdae901d238f93569e7042e07d9fbf50 languageName: node linkType: hard -"@sentry-internal/replay-canvas@npm:7.110.1": - version: 7.110.1 - resolution: "@sentry-internal/replay-canvas@npm:7.110.1" +"@sentry-internal/feedback@npm:8.4.0": + version: 8.4.0 + resolution: "@sentry-internal/feedback@npm:8.4.0" dependencies: - "@sentry/core": "npm:7.110.1" - "@sentry/replay": "npm:7.110.1" - "@sentry/types": "npm:7.110.1" - "@sentry/utils": "npm:7.110.1" - checksum: 10c0/125b8247db4631e298f3c8c6d8b486669dfa6356490cc7464a23131a5518d9d7ef314f2c1cbbbadad4f9738ba451fbf5f9457170f4bc899b8275e15ae28cb610 + "@sentry/core": "npm:8.4.0" + "@sentry/types": "npm:8.4.0" + "@sentry/utils": "npm:8.4.0" + checksum: 10c0/028eb28770eb85f1d09caa3ec98420cdb691d2de06be261106876efed572f7d83bfddf6d289468c0f1bbf551d5a75e7b01fe2c2bbac4b0da882c8e655a166b5c languageName: node linkType: hard -"@sentry-internal/tracing@npm:7.110.1": - version: 7.110.1 - resolution: "@sentry-internal/tracing@npm:7.110.1" +"@sentry-internal/replay-canvas@npm:8.4.0": + version: 8.4.0 + resolution: "@sentry-internal/replay-canvas@npm:8.4.0" dependencies: - "@sentry/core": "npm:7.110.1" - "@sentry/types": "npm:7.110.1" - "@sentry/utils": "npm:7.110.1" - checksum: 10c0/ee901b99b838a932f2adb1b085364c816fb0b3f61643ca368af37dd2aa9faf702ea1f3d28f7b0e2e79c57794e3260c8ce34376da0f8658b6ee844b28676ecd55 + "@sentry-internal/replay": "npm:8.4.0" + "@sentry/core": "npm:8.4.0" + "@sentry/types": "npm:8.4.0" + "@sentry/utils": "npm:8.4.0" + checksum: 10c0/6e62557e88f1422f645b022c33c034eccf53469dcdea8e1fbdef34f087e8e986411f50bbf384b7d41f83298b1f339866c8a024456ecf74f1158811f9b91d225b languageName: node linkType: hard -"@sentry/browser@npm:^7.110.1": - version: 7.110.1 - resolution: "@sentry/browser@npm:7.110.1" +"@sentry-internal/replay@npm:8.4.0": + version: 8.4.0 + resolution: "@sentry-internal/replay@npm:8.4.0" dependencies: - "@sentry-internal/feedback": "npm:7.110.1" - "@sentry-internal/replay-canvas": "npm:7.110.1" - "@sentry-internal/tracing": "npm:7.110.1" - "@sentry/core": "npm:7.110.1" - "@sentry/replay": "npm:7.110.1" - "@sentry/types": "npm:7.110.1" - "@sentry/utils": "npm:7.110.1" - checksum: 10c0/085a2cbd2b60dbb596b68d3cf7d0059e226bc8892aa90a50680fa9a9256889a715ff234e9210738a3205ce5513e52290e440ffa4f2d2e2429471ea584d075493 + "@sentry-internal/browser-utils": "npm:8.4.0" + "@sentry/core": "npm:8.4.0" + "@sentry/types": "npm:8.4.0" + "@sentry/utils": "npm:8.4.0" + checksum: 10c0/e9e6398eb3e245470bd464022a9e147038e6adc97212a8e0a28496093f8ed018f8c417bc0d73715f6e63a7506a2d199fadddd23a80246abc8c54d5875d8552c3 languageName: node linkType: hard -"@sentry/core@npm:7.110.1": - version: 7.110.1 - resolution: "@sentry/core@npm:7.110.1" +"@sentry/browser@npm:^8.4.0": + version: 8.4.0 + resolution: "@sentry/browser@npm:8.4.0" dependencies: - "@sentry/types": "npm:7.110.1" - "@sentry/utils": "npm:7.110.1" - checksum: 10c0/8eee39697a1173f233a83fa07b5099c2fdc1f345ec0ac0304ed38c7159214e6a0615fb2fe4dcd4152dbe11cf5ec14bfe3d7da19706e90b13a8bd7984256c4b2f + "@sentry-internal/browser-utils": "npm:8.4.0" + "@sentry-internal/feedback": "npm:8.4.0" + "@sentry-internal/replay": "npm:8.4.0" + "@sentry-internal/replay-canvas": "npm:8.4.0" + "@sentry/core": "npm:8.4.0" + "@sentry/types": "npm:8.4.0" + "@sentry/utils": "npm:8.4.0" + checksum: 10c0/bd1162ad13f246c30c1af2d65b798d729cfd4c94c50cc08bdb848a95cd734cedae235c2c2554863c81bc4e639c3d9b8120b6d6988fe6e3437ca45f3dbcf67de6 languageName: node linkType: hard -"@sentry/core@npm:7.114.0, @sentry/core@npm:^7.114.0": - version: 7.114.0 - resolution: "@sentry/core@npm:7.114.0" +"@sentry/core@npm:8.4.0": + version: 8.4.0 + resolution: "@sentry/core@npm:8.4.0" dependencies: - "@sentry/types": "npm:7.114.0" - "@sentry/utils": "npm:7.114.0" - checksum: 10c0/9ef9cd6e536180a53fdd11dfb30f19a1bf51115b6a828aae4f9dacb92233ced238e3100065429cdb50a654609519722b6cf99721cac21f09dd5d57f123aa4f3c + "@sentry/types": "npm:8.4.0" + "@sentry/utils": "npm:8.4.0" + checksum: 10c0/7c9ffc15a5cbc6cc9811b1444c2ea956dd42b576e63606e3064af0d617fd9c3d97b6ba878f14fc13818d2001a3cf07b73ecde648e31fdb8d9b7e316bd5549470 languageName: node linkType: hard -"@sentry/integrations@npm:^7.114.0": - version: 7.114.0 - resolution: "@sentry/integrations@npm:7.114.0" +"@sentry/types@npm:8.4.0": + version: 8.4.0 + resolution: "@sentry/types@npm:8.4.0" + checksum: 10c0/0fd8790b8b6c353029feb47caf4b85f3a972f6a37f90ab6719f6d9425919c53069e9e15976da4690a4c738c2c0c3fbebd00749ae3568aaa719e02552c38e3feb + languageName: node + linkType: hard + +"@sentry/utils@npm:8.4.0": + version: 8.4.0 + resolution: "@sentry/utils@npm:8.4.0" dependencies: - "@sentry/core": "npm:7.114.0" - "@sentry/types": "npm:7.114.0" - "@sentry/utils": "npm:7.114.0" - localforage: "npm:^1.8.1" - checksum: 10c0/180fedbd474d1141d90882446de023da38df906489e9abeee4d659b9e5498922b66271c6e85b154c0c393a9157329b3e36c4fa98ba88d1741644797dbeedec1d - languageName: node - linkType: hard - -"@sentry/replay@npm:7.110.1": - version: 7.110.1 - resolution: "@sentry/replay@npm:7.110.1" - dependencies: - "@sentry-internal/tracing": "npm:7.110.1" - "@sentry/core": "npm:7.110.1" - "@sentry/types": "npm:7.110.1" - "@sentry/utils": "npm:7.110.1" - checksum: 10c0/ffb300d229d6447ea0f2b99a21ab3d6fae6136cdef33fb9daaac417b1a6b070f385139c934a2acff68562a6f5acdcf96a6e606ddf3893b7a9d44955597343841 - languageName: node - linkType: hard - -"@sentry/types@npm:7.110.1": - version: 7.110.1 - resolution: "@sentry/types@npm:7.110.1" - checksum: 10c0/6892ae1bed6426ef10dba075d0bd68247bac7c024a7860f3385d320759e152bd9e7bb4b3eeec2600a58d81bb30084fd01e9586da09e372fb48d615c24c86fd10 - languageName: node - linkType: hard - -"@sentry/types@npm:7.114.0": - version: 7.114.0 - resolution: "@sentry/types@npm:7.114.0" - checksum: 10c0/25564ee09beb2362f43a4f96270acdb4e1c9d999ab4fd37b6e340717433b3e685301dc0c55a7e8883226f2f20023b99055cea04f68eaaf22f4d5f9c5d6976b43 - languageName: node - linkType: hard - -"@sentry/utils@npm:7.110.1": - version: 7.110.1 - resolution: "@sentry/utils@npm:7.110.1" - dependencies: - "@sentry/types": "npm:7.110.1" - checksum: 10c0/ab84862283881808e94e68d4abe19a38024fbe8de0bd55ddbb1238123772ce633fd507af7f636427333c6376767a85f6e665ece647c43183fdcac6a8e7ba3c77 - languageName: node - linkType: hard - -"@sentry/utils@npm:7.114.0, @sentry/utils@npm:^7.114.0": - version: 7.114.0 - resolution: "@sentry/utils@npm:7.114.0" - dependencies: - "@sentry/types": "npm:7.114.0" - checksum: 10c0/b20283d62df7ea3c71988e9fc2a6b7d3d04dd4a92a3599605413e2da3ff3cb408b84d65ee7c8e0bd8f99a92dd6e364c37485023e8d2c7cbcbd3f31dfb2d15383 + "@sentry/types": "npm:8.4.0" + checksum: 10c0/3306ba659874344b63ca72dbf7172641b38f590abf8c15291c6f98b2752dcfa9569a209cf83f7a83601acd1c22bc4ab8ead4bb55f7698b9b9b6690cfa6e5ebb5 languageName: node linkType: hard @@ -3074,10 +3036,7 @@ __metadata: "@egjs/hammerjs": "npm:^2.0.17" "@fortawesome/fontawesome-free": "npm:^6.5.2" "@popperjs/core": "npm:^2.11.8" - "@sentry/browser": "npm:^7.110.1" - "@sentry/core": "npm:^7.114.0" - "@sentry/integrations": "npm:^7.114.0" - "@sentry/utils": "npm:^7.114.0" + "@sentry/browser": "npm:^8.4.0" "@toast-ui/editor": "npm:^3.2.2" "@webpack-cli/serve": "npm:^2.0.5" ace-builds: "npm:^1.34.0" @@ -4771,13 +4730,6 @@ __metadata: languageName: node linkType: hard -"immediate@npm:~3.0.5": - version: 3.0.6 - resolution: "immediate@npm:3.0.6" - checksum: 10c0/f8ba7ede69bee9260241ad078d2d535848745ff5f6995c7c7cb41cfdc9ccc213f66e10fa5afb881f90298b24a3f7344b637b592beb4f54e582770cdce3f1f039 - languageName: node - linkType: hard - "immutable@npm:^4.0.0": version: 4.3.4 resolution: "immutable@npm:4.3.4" @@ -5200,15 +5152,6 @@ __metadata: languageName: node linkType: hard -"lie@npm:3.1.1": - version: 3.1.1 - resolution: "lie@npm:3.1.1" - dependencies: - immediate: "npm:~3.0.5" - checksum: 10c0/d62685786590351b8e407814acdd89efe1cb136f05cb9236c5a97b2efdca1f631d2997310ad2d565c753db7596799870140e4777c9c9b8c44a0f6bf42d1804a1 - languageName: node - linkType: hard - "lilconfig@npm:^3.1.1": version: 3.1.1 resolution: "lilconfig@npm:3.1.1" @@ -5234,15 +5177,6 @@ __metadata: languageName: node linkType: hard -"localforage@npm:^1.8.1": - version: 1.10.0 - resolution: "localforage@npm:1.10.0" - dependencies: - lie: "npm:3.1.1" - checksum: 10c0/00f19f1f97002e6721587ed5017f502d58faf80dae567d5065d4d1ee0caf0762f40d2e2dba7f0ef7d3f14ee6203242daae9ecad97359bfc10ecff36df11d85a3 - languageName: node - linkType: hard - "locate-path@npm:^5.0.0": version: 5.0.0 resolution: "locate-path@npm:5.0.0"