mini airbrake para poder hacer seguimiento de js
This commit is contained in:
parent
cc6efb095b
commit
42c7e7006a
11 changed files with 249 additions and 0 deletions
|
@ -22,3 +22,7 @@ DEBUG_FORMS=
|
|||
COOKIE_DURATION=30
|
||||
# Dominio de la tienda
|
||||
TIENDA=tienda.sutty.local
|
||||
# Obtener esto con Site.find_by_name('panel').airbrake_api_key
|
||||
PANEL_URL=https://panel.sutty.nl
|
||||
AIRBRAKE_SITE_ID=1
|
||||
AIRBRAKE_API_KEY=
|
||||
|
|
5
Gemfile
5
Gemfile
|
@ -86,6 +86,11 @@ gem 'rack-mini-profiler'
|
|||
gem 'stackprof'
|
||||
gem 'prometheus_exporter'
|
||||
|
||||
# debug
|
||||
gem 'fast_jsonparser'
|
||||
gem 'down'
|
||||
gem 'sourcemap'
|
||||
|
||||
group :themes do
|
||||
gem 'adhesiones-jekyll-theme', require: false
|
||||
gem 'editorial-autogestiva-jekyll-theme', require: false
|
||||
|
|
|
@ -161,6 +161,8 @@ GEM
|
|||
dotenv-rails (2.7.6)
|
||||
dotenv (= 2.7.6)
|
||||
railties (>= 3.2)
|
||||
down (5.2.0)
|
||||
addressable (~> 2.5)
|
||||
editorial-autogestiva-jekyll-theme (0.2.8)
|
||||
jekyll (~> 4.0)
|
||||
jekyll-data (~> 1.1)
|
||||
|
@ -190,6 +192,7 @@ GEM
|
|||
factory_bot (~> 6.1.0)
|
||||
railties (>= 5.0.0)
|
||||
fast_blank (1.0.0)
|
||||
fast_jsonparser (0.5.0)
|
||||
ffi (1.13.1)
|
||||
flamegraph (0.9.5)
|
||||
forwardable-extended (2.6.0)
|
||||
|
@ -479,6 +482,7 @@ GEM
|
|||
jekyll-seo-tag (~> 2.1)
|
||||
simpleidn (0.1.1)
|
||||
unf (~> 0.1.4)
|
||||
sourcemap (0.1.1)
|
||||
spring (2.1.1)
|
||||
spring-watcher-listen (2.0.1)
|
||||
listen (>= 2.7, < 4.0)
|
||||
|
@ -578,11 +582,13 @@ DEPENDENCIES
|
|||
devise-i18n
|
||||
devise_invitable
|
||||
dotenv-rails
|
||||
down
|
||||
editorial-autogestiva-jekyll-theme
|
||||
email_address
|
||||
exception_notification
|
||||
factory_bot_rails
|
||||
fast_blank
|
||||
fast_jsonparser
|
||||
flamegraph
|
||||
friendly_id
|
||||
haml-lint
|
||||
|
@ -624,6 +630,7 @@ DEPENDENCIES
|
|||
sassc-rails
|
||||
selenium-webdriver
|
||||
share-to-fediverse-jekyll-theme
|
||||
sourcemap
|
||||
spring
|
||||
spring-watcher-listen (~> 2.0.0)
|
||||
sqlite3
|
||||
|
|
46
app/controllers/api/v1/notices_controller.rb
Normal file
46
app/controllers/api/v1/notices_controller.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V1
|
||||
# Recibe notificaciones desde Airbrake
|
||||
#
|
||||
# TODO: Autenticación
|
||||
class NoticesController < BaseController
|
||||
skip_before_action :verify_authenticity_token
|
||||
|
||||
# Generar un stacktrace en segundo plano y enviarlo por correo
|
||||
# solo si la API key es verificable. Del otro lado siempre
|
||||
# respondemos con lo mismo.
|
||||
def create
|
||||
if verify_api_key
|
||||
BacktraceJob.perform_later site_id: params[:site_id],
|
||||
errors: airbrake_params.map(&:permit!).map(&:to_h)
|
||||
end
|
||||
|
||||
render status: 201, json: { id: 1, url: root_url }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# XXX: Por alguna razón Airbrake envía los datos con Content-Type:
|
||||
# text/plain.
|
||||
def airbrake_params
|
||||
@airbrake_params ||= params.merge!(JSON.parse(request.raw_post) || {}).require(:errors)
|
||||
end
|
||||
|
||||
def site
|
||||
@site ||= Site.find(params[:site_id])
|
||||
end
|
||||
|
||||
def verify_api_key
|
||||
site.verifier.verify(airbrake_token, purpose: :airbrake) === Site::Api::AIRBRAKE_SECRET
|
||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||
false
|
||||
end
|
||||
|
||||
def airbrake_token
|
||||
@airbrake_token ||= params[:key]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -15,6 +15,14 @@
|
|||
// const images = require.context('../images', true)
|
||||
// const imagePath = (name) => images(name, true)
|
||||
|
||||
import { Notifier } from '@airbrake/browser'
|
||||
|
||||
window.airbrake = new Notifier({
|
||||
projectId: process.env.AIRBRAKE_SITE_ID,
|
||||
projectKey: process.env.AIRBRAKE_API_KEY,
|
||||
host: process.env.PANEL_URL
|
||||
})
|
||||
|
||||
import 'core-js/stable'
|
||||
import 'regenerator-runtime/runtime'
|
||||
import 'controllers'
|
||||
|
|
58
app/jobs/backtrace_job.rb
Normal file
58
app/jobs/backtrace_job.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Procesa los errores de JavaScript
|
||||
class BacktraceJob < ApplicationJob
|
||||
queue_as :low_priority
|
||||
|
||||
attr_reader :errors
|
||||
|
||||
def perform(site_id:, errors:)
|
||||
@errors = errors
|
||||
|
||||
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
|
||||
|
||||
begin
|
||||
raise NoMethodError
|
||||
rescue NoMethodError => e
|
||||
ExceptionNotifier.notify_exception(e, data: { errors: errors })
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Obtiene todos los archivos del backtrace
|
||||
def files
|
||||
@files ||= errors.map { |x| x['backtrace'] }.flatten.map { |x| x['file'].split('@').last }.uniq
|
||||
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
|
|
@ -6,6 +6,7 @@ class Site < ApplicationRecord
|
|||
include FriendlyId
|
||||
include Site::Forms
|
||||
include Site::FindAndReplace
|
||||
include Site::Api
|
||||
include Tienda
|
||||
|
||||
# Cifrar la llave privada que cifra y decifra campos ocultos. Sutty
|
||||
|
|
30
app/models/site/api.rb
Normal file
30
app/models/site/api.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Site
|
||||
module Api
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
AIRBRAKE_SECRET = 'an api key for airbrake'
|
||||
|
||||
included do
|
||||
encrypts :api_key
|
||||
before_save :add_api_key_if_missing!
|
||||
|
||||
# Genera mensajes secretos que podemos usar para la API de cada sitio.
|
||||
def verifier
|
||||
@verifier ||= ActiveSupport::MessageVerifier.new api_key
|
||||
end
|
||||
|
||||
def airbrake_api_key
|
||||
@airbrake_api_key ||= verifier.generate(AIRBRAKE_SECRET, purpose: :airbrake)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Asegurarse que el sitio tenga una llave para la API
|
||||
def add_api_key_if_missing!
|
||||
self.api_key ||= SecureRandom.hex(64)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -35,6 +35,8 @@ Rails.application.routes.draw do
|
|||
# Obtener archivos estáticos desde el directorio público
|
||||
get '/sites/:site_id/static_file/(*file)', to: 'sites#static_file', as: 'site_static_file', constraints: { site_id: %r{[^/]+} }
|
||||
|
||||
match '/api/v3/projects/:site_id/notices' => 'api/v1/notices#create', via: %i[post]
|
||||
|
||||
resources :sites, constraints: { site_id: %r{[^/]+}, id: %r{[^/]+} } do
|
||||
# Gestionar actualizaciones del sitio
|
||||
get 'pull', to: 'sites#fetch'
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"name": "sutty",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@airbrake/browser": "^1.4.1",
|
||||
"@rails/actiontext": "^6.0.0",
|
||||
"@rails/webpacker": "5.2.1",
|
||||
"commonmark": "^0.29.0",
|
||||
|
|
87
yarn.lock
87
yarn.lock
|
@ -2,6 +2,18 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@airbrake/browser@^1.4.1":
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@airbrake/browser/-/browser-1.4.1.tgz#c0832eed3096498e51ff947e1e35bda23021a7be"
|
||||
integrity sha512-4KChn2eGDllwqYJ4c6MFqIiJ2RZgg49inYiIsdpAj5MLtOpZRlPF8MsOSLGHxkEDX6u5M9KRidUfktuQ/C9lKA==
|
||||
dependencies:
|
||||
"@types/promise-polyfill" "^6.0.3"
|
||||
"@types/request" "2.48.5"
|
||||
cross-fetch "^3.0.4"
|
||||
error-stack-parser "^2.0.4"
|
||||
promise-polyfill "^8.1.3"
|
||||
tdigest "^0.1.1"
|
||||
|
||||
"@babel/code-frame@^7.0.0":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
|
||||
|
@ -983,6 +995,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@stimulus/webpack-helpers/-/webpack-helpers-1.1.1.tgz#eff60cd4e58b921d1a2764dc5215f5141510f2c2"
|
||||
integrity sha512-XOkqSw53N9072FLHvpLM25PIwy+ndkSSbnTtjKuyzsv8K5yfkFB2rv68jU1pzqYa9FZLcvZWP4yazC0V38dx9A==
|
||||
|
||||
"@types/caseless@*":
|
||||
version "0.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8"
|
||||
integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==
|
||||
|
||||
"@types/events@*":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
||||
|
@ -1017,11 +1034,31 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||
|
||||
"@types/promise-polyfill@^6.0.3":
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/promise-polyfill/-/promise-polyfill-6.0.3.tgz#e2f38fcd244a9e0df2cc7528e0711abcbc707b5e"
|
||||
integrity sha512-f/BFgF9a+cgsMseC7rpv9+9TAE3YNjhfYrtwCo/pIeCDDfQtE6PY0b5bao2eIIEpZCBUy8Y5ToXd4ObjPSJuFw==
|
||||
|
||||
"@types/q@^1.5.1":
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
|
||||
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
|
||||
|
||||
"@types/request@2.48.5":
|
||||
version "2.48.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.5.tgz#019b8536b402069f6d11bee1b2c03e7f232937a0"
|
||||
integrity sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==
|
||||
dependencies:
|
||||
"@types/caseless" "*"
|
||||
"@types/node" "*"
|
||||
"@types/tough-cookie" "*"
|
||||
form-data "^2.5.0"
|
||||
|
||||
"@types/tough-cookie@*":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d"
|
||||
integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==
|
||||
|
||||
"@webassemblyjs/ast@1.9.0":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
|
||||
|
@ -1533,6 +1570,11 @@ binary-extensions@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
||||
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
||||
|
||||
bintrees@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.1.tgz#0e655c9b9c2435eaab68bf4027226d2b55a34524"
|
||||
integrity sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=
|
||||
|
||||
block-stream@*:
|
||||
version "0.0.9"
|
||||
resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
|
||||
|
@ -2261,6 +2303,13 @@ crelt@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.4.tgz#9a05d7829aedf79538f2b26f7de319cf45a25b47"
|
||||
integrity sha512-l1cwMUOssGLEj5zgbut4lxJq95ZabOXVZnDybNqQRUtXh1lvUK7e7kJNm8SfvTQzYpE3AVJhIVUJKf382lMA7A==
|
||||
|
||||
cross-fetch@^3.0.4:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c"
|
||||
integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==
|
||||
dependencies:
|
||||
node-fetch "2.6.1"
|
||||
|
||||
cross-spawn@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
|
||||
|
@ -2832,6 +2881,13 @@ error-ex@^1.2.0, error-ex@^1.3.1:
|
|||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
error-stack-parser@^2.0.4:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8"
|
||||
integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==
|
||||
dependencies:
|
||||
stackframe "^1.1.1"
|
||||
|
||||
es-abstract@^1.12.0, es-abstract@^1.5.1:
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9"
|
||||
|
@ -3199,6 +3255,15 @@ forever-agent@~0.6.1:
|
|||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
|
||||
|
||||
form-data@^2.5.0:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
|
||||
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
form-data@~2.3.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
|
||||
|
@ -4840,6 +4905,11 @@ nice-try@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
node-fetch@2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
node-forge@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
|
||||
|
@ -6167,6 +6237,11 @@ promise-inflight@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
|
||||
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
|
||||
|
||||
promise-polyfill@^8.1.3:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.0.tgz#367394726da7561457aba2133c9ceefbd6267da0"
|
||||
integrity sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g==
|
||||
|
||||
prosemirror-commands@^1.0.0:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.1.4.tgz#991563e67623acab4f8c510fad1570f8b4693780"
|
||||
|
@ -7241,6 +7316,11 @@ stable@^0.1.8:
|
|||
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
|
||||
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
|
||||
|
||||
stackframe@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303"
|
||||
integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==
|
||||
|
||||
static-extend@^0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
|
||||
|
@ -7497,6 +7577,13 @@ tar@^6.0.2:
|
|||
mkdirp "^1.0.3"
|
||||
yallist "^4.0.0"
|
||||
|
||||
tdigest@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.1.tgz#2e3cb2c39ea449e55d1e6cd91117accca4588021"
|
||||
integrity sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=
|
||||
dependencies:
|
||||
bintrees "1.0.1"
|
||||
|
||||
terser-webpack-plugin@^1.4.3:
|
||||
version "1.4.5"
|
||||
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b"
|
||||
|
|
Loading…
Reference in a new issue