# frozen_string_literal: true # Procesa los errores de JavaScript class BacktraceJob < ApplicationJob class BacktraceException < RuntimeError; end queue_as :low_priority attr_reader :params, :site_id def perform(site_id:, params:) @site_id = site_id @params = params unless files.empty? params['errors'].each do |error| error['backtrace'].each do |backtrace| offset = SourceMap::Offset.new(backtrace['line'], backtrace['column']) mapping = sourcemap.bsearch(offset) next unless mapping backtrace['file'] = mapping.source backtrace['line'] = mapping.original.line backtrace['column'] = mapping.original.column end end end begin raise BacktraceException, params['errors']&.first&.dig('message') rescue BacktraceException => e ExceptionNotifier.notify_exception(e, data: { site: site.name, params: params, _backtrace: true }) end end private def site @site ||= Site.find_by_id(site_id) end # Obtiene todos los archivos del backtrace def files @files ||= params['errors'].map do |x| x['backtrace'] end.flatten.map do |x| x['file'].split('@').last end.uniq.select do |x| x&.start_with?(site.url) end end # Asume que todos los sourcemaps comparten la misma URL, lo # correcto serĂ­a buscarlo en sourceMappingURL al final de cada # archivo. # # Descarga los archivos y obtiene el backtrace original. def sourcemap @sourcemap ||= begin files.map { |x| "#{x}.map" }.map do |x| data = FastJsonparser.parse(Rails.cache.fetch(x, expires_in: 12.hours) do Down.open(x).read end, symbolize_keys: false) SourceMap::Map.from_hash data rescue Down::Error, FastJsonparser::Error SourceMap::Map.new end.reduce(&:+) end end end