diff --git a/.devcontainer/.env.example b/.devcontainer/.env.example index 0f6af9fbb..24a575b5e 100644 --- a/.devcontainer/.env.example +++ b/.devcontainer/.env.example @@ -1,7 +1,18 @@ -IMAGE=bitnami/ruby +# Official Ruby images +IMAGE="ruby:3.4.5-slim-bookworm" +VERSION="3.4.5" -# Adjust as needed -TAG=3.4 +# IMAGE="jruby:latest" -# IMAGE=jruby -# TAG=latest +# E2E testing +SENTRY_DSN="http://user:pass@sentry.localhost/project/42" +SENTRY_DSN_JS="http://user:pass@sentry-js.localhost/project/43" + +SENTRY_E2E_RAILS_APP_PORT=4000 +SENTRY_E2E_SVELTE_APP_PORT=4001 + +SENTRY_E2E_RAILS_APP_URL="http://localhost:4000" +SENTRY_E2E_SVELTE_APP_URL="http://localhost:4001" + +# Faster builds with compose +COMPOSE_BAKE=true diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 4eba265f0..9871a2679 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,18 +1,15 @@ -ARG IMAGE="bitnami/ruby" -ARG TAG="latest" +ARG IMAGE="ruby" -FROM ${IMAGE}:${TAG} +FROM ${IMAGE} AS build -USER root -RUN apt-get update && apt-get install -y \ +RUN apt-get update && apt-get install -y --no-install-recommends \ + sudo \ gnupg \ git \ curl \ wget \ - zsh \ - vim \ build-essential \ - sudo \ + pkg-config \ libssl-dev \ libreadline-dev \ zlib1g-dev \ @@ -22,31 +19,32 @@ RUN apt-get update && apt-get install -y \ libncurses5-dev \ libffi-dev \ libgdbm-dev \ + sqlite3 \ + nodejs \ + npm \ + chromium \ + chromium-driver \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -RUN groupadd --gid 1000 sentry \ - && useradd --uid 1000 --gid sentry --shell /bin/zsh --create-home sentry - -# Add sentry to sudoers with NOPASSWD option RUN echo "sentry ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/sentry \ && chmod 0440 /etc/sudoers.d/sentry +RUN groupadd --gid 1000 sentry \ + && useradd --uid 1000 --gid sentry --shell /bin/bash --create-home sentry + WORKDIR /workspace/sentry RUN chown -R sentry:sentry /workspace/sentry -RUN mkdir /workspace/gems && chown -R sentry:sentry /workspace/gems -ARG TAG=latest -ARG GEM_HOME="/workspace/gems/${TAG}" +ARG VERSION +ARG GEM_HOME="/workspace/sentry/vendor/gems/${VERSION}" ENV LANG=C.UTF-8 \ BUNDLE_JOBS=4 \ BUNDLE_RETRY=3 \ - GEM_HOME=/workspace/gems/${TAG} \ + GEM_HOME=/workspace/sentry/vendor/gems/${VERSION} \ PATH=$PATH:${GEM_HOME}/bin \ REDIS_HOST=redis USER sentry - -CMD ["ruby", "--version"] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 13dfcf336..bf21c8d74 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,20 +1,29 @@ { "name": "sentry-ruby", "dockerComposeFile": "docker-compose.yml", - "service": "app", + "service": "sentry-dev", "workspaceFolder": "/workspace/sentry", + "features": { + "ghcr.io/devcontainers/features/github-cli": {}, + "ghcr.io/nils-geistmann/devcontainers-features/zsh": {}, + "ghcr.io/devcontainers-extra/features/npm-packages": {}, + "ghcr.io/rocker-org/devcontainer-features/apt-packages": { + "packages": "inotify-tools" + } + }, "customizations": { "vscode": { "extensions": [ "sleistner.vscode-fileutils", "Shopify.ruby-lsp" ], - "settings": {} - }, - "rubyLsp.rubyVersionManager": { - "identifier": "none" + "editor.formatOnSaveMode": "modifications", + "editor.formatOnSave": true, + "rubyLsp.rubyVersionManager": { + "identifier": "auto" + }, + "rubyLsp.formatter": "auto" } }, - "remoteUser": "sentry", - "postCreateCommand": "ruby --version" + "remoteUser": "sentry" } diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 484e24bc8..a85243cba 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -1,19 +1,32 @@ services: - app: + sentry-build: &sentry-build + image: ${DOCKER_IMAGE:-sentry-ruby-devcontainer}:${DOCKER_TAG:-latest} build: - context: . - dockerfile: Dockerfile + context: .. + dockerfile: .devcontainer/Dockerfile args: IMAGE: ${IMAGE} - TAG: ${TAG} + VERSION: ${VERSION} volumes: - ..:/workspace/sentry:cached - command: sleep infinity - environment: - - REDIS_URL=${REDIS_URL:-redis://redis:6379/0} + working_dir: /workspace/sentry + env_file: [".env"] + + sentry-dev: + <<: *sentry-build + entrypoint: ".devcontainer/run --service dev" + command: "sleep infinity" depends_on: - redis + sentry-test: + <<: *sentry-build + entrypoint: ".devcontainer/run --service test" + command: ["foreman", "start"] + ports: + - "${SENTRY_E2E_RAILS_APP_PORT}:4000" + - "${SENTRY_E2E_SVELTE_APP_PORT}:4001" + redis: image: redis:latest environment: diff --git a/.devcontainer/run b/.devcontainer/run new file mode 100755 index 000000000..ed22b3403 --- /dev/null +++ b/.devcontainer/run @@ -0,0 +1,92 @@ +#!/bin/bash + +set -e + +cd /workspace/sentry + +sudo mkdir -p vendor/gems +sudo chown -R sentry:sentry vendor/gems + +git config --global --add safe.directory /workspace/sentry +git config --global --add safe.directory /workspace/sentry/vendor/gems/* + +sudo chown -R sentry:sentry . + +run_service_setup() { + local service="$1" + + echo "๐Ÿš€ Running setup for service: $service" + + case "$service" in + "dev") + if ! .devcontainer/setup --with-foreman --only-bundle; then + echo "โŒ Setup failed for service: $service" + exit 1 + fi + ;; + "test") + if ! .devcontainer/setup --with-foreman --only .,spec/apps/rails-mini; then + echo "โŒ Setup failed for service: $service" + exit 1 + fi + ;; + *) + echo "โŒ Unknown service: $service" + echo "Available services: dev, test" + exit 1 + ;; + esac + + echo "โœ… Setup completed for service: $service" +} + +# Function to start services in background +start_services_if_needed() { + # Check if we're running tests (bundle exec rake) + if [[ "$*" == *"bundle exec rake"* ]]; then + echo "๐Ÿš€ Starting e2e services in background for test execution..." + + # Start foreman in background + foreman start & + FOREMAN_PID=$! + + # Wait for services to be ready + echo "โณ Waiting for services to start..." + for i in {1..30}; do + if curl -f http://localhost:4000/health >/dev/null 2>&1 && \ + curl -f http://localhost:4001/health >/dev/null 2>&1; then + echo "โœ… Services are ready!" + break + fi + + if [ $i -eq 30 ]; then + echo "โŒ Services failed to start within timeout" + kill $FOREMAN_PID 2>/dev/null || true + exit 1 + fi + + sleep 2 + done + + # Set up cleanup trap + trap "echo '๐Ÿงน Stopping services...'; kill $FOREMAN_PID 2>/dev/null || true; wait $FOREMAN_PID 2>/dev/null || true" EXIT + fi +} + +# Parse arguments +if [ "$1" = "--service" ] && [ -n "$2" ]; then + service="$2" + shift 2 + + run_service_setup "$service" + + if [ $# -gt 0 ]; then + start_services_if_needed "$@" + exec "$@" + else + exec bash + fi +else + start_services_if_needed "$@" + exec "$@" +fi diff --git a/.devcontainer/setup b/.devcontainer/setup new file mode 100755 index 000000000..177502732 --- /dev/null +++ b/.devcontainer/setup @@ -0,0 +1,194 @@ +#!/usr/bin/env ruby + +# frozen_string_literal: true + +require 'optparse' +require 'fileutils' +require 'pathname' + +class SetupScript + WORKSPACE_FOLDERS = %w[ + . + spec/apps/rails-mini + sentry-ruby + sentry-rails + sentry-sidekiq + sentry-delayed_job + sentry-resque + sentry-opentelemetry + ].freeze + + def initialize + @options = { + only_folders: [], + only_bundle: false, + only_npm: false, + with_foreman: false + } + @workspace_root = '/workspace/sentry' + end + + def run(args) + parse_options(args) + + Dir.chdir(@workspace_root) + + if should_run_bundle? + cleanup_ruby_lsp_directories + install_bundle_dependencies + install_foreman_gem if @options[:with_foreman] + end + + if should_run_npm? + install_npm_dependencies + end + + puts "โœ… Post-create setup completed!" + end + + private + + def parse_options(args) + parser = OptionParser.new do |opts| + opts.banner = "Usage: setup.rb [options]" + + opts.on("--only FOLDERS", Array, "Only process specified folders (comma-separated)") do |folders| + @options[:only_folders] = folders + end + + opts.on("--only-bundle", "Only run bundle operations (skip npm)") do + @options[:only_bundle] = true + end + + opts.on("--only-npm", "Only run npm operations (skip bundle)") do + @options[:only_npm] = true + end + + opts.on("--with-foreman", "Install foreman gem (default: false)") do + @options[:with_foreman] = true + end + + opts.on("-h", "--help", "Show this help message") do + puts opts + exit + end + end + + parser.parse!(args) + + if @options[:only_bundle] && @options[:only_npm] + puts "โŒ Error: --only-bundle and --only-npm are mutually exclusive" + exit 1 + end + end + + def should_run_bundle? + !@options[:only_npm] + end + + def should_run_npm? + !@options[:only_bundle] + end + + def target_folders + return @options[:only_folders] unless @options[:only_folders].empty? + WORKSPACE_FOLDERS + end + + def all_folders + target_folders + end + + def cleanup_ruby_lsp_directories + puts "๐Ÿงน Cleaning up .ruby-lsp directories..." + + all_folders.each do |folder| + ruby_lsp_path = File.join(folder, '.ruby-lsp') + if Dir.exist?(ruby_lsp_path) + puts " Removing #{folder}/.ruby-lsp" + FileUtils.rm_rf(ruby_lsp_path) + end + end + end + + def install_bundle_dependencies + puts "๐Ÿ“ฆ Installing bundle dependencies for workspace folders..." + + target_folders.each do |folder| + folder_path = File.join(@workspace_root, folder) + gemfile_path = File.join(folder_path, 'Gemfile') + + if Dir.exist?(folder_path) && File.exist?(gemfile_path) + Dir.chdir(folder_path) do + puts " Installing dependencies for #{folder_path}..." + unless system('bundle install') + puts "โŒ Bundle install failed for #{folder}" + exit 1 + end + end + + Dir.chdir(@workspace_root) + else + puts " Skipping #{folder} (no Gemfile found or directory doesn't exist)" + end + end + end + + def install_foreman_gem + unless system('gem install foreman') + puts "โŒ Foreman gem installation failed" + exit 1 + end + end + + def install_npm_dependencies + svelte_mini_path = File.join(@workspace_root, 'spec/apps/svelte-mini') + + if Dir.exist?(svelte_mini_path) + Dir.chdir(svelte_mini_path) do + unless system('npm install') + puts "โŒ npm install failed for svelte-mini" + exit 1 + end + end + else + puts "โŒ Svelte mini app directory not found: #{svelte_mini_path}" + exit 1 + end + end + + def run_with_spinner(message) + print "#{message} " + + spinner_chars = %w[- \\ | /] + spinner_index = 0 + spinner_running = true + + spinner_thread = Thread.new do + while spinner_running + print "\r#{message} #{spinner_chars[spinner_index]}" + spinner_index = (spinner_index + 1) % spinner_chars.length + sleep 0.1 + end + end + + # Run the actual command + success = yield + + # Stop spinner + spinner_running = false + spinner_thread.join + + if success + puts "\r#{message} โœ…" + else + puts "\r#{message} โŒ" + puts "โŒ Command failed. Please check for missing dependencies or run manually." + exit 1 + end + end +end + +if __FILE__ == $0 + SetupScript.new.run(ARGV) +end diff --git a/.github/workflows/build_images.yml b/.github/workflows/build_images.yml new file mode 100644 index 000000000..943477cb1 --- /dev/null +++ b/.github/workflows/build_images.yml @@ -0,0 +1,80 @@ +name: "Build & push devcontainer" + +on: + push: + branches: + - master + paths: + - .devcontainer/** + - .github/workflows/build_images.yml + + # Uncomment if you want to test things out in a PR + # + # pull_request: + # paths: + # - .devcontainer/** + # - .github/workflows/build_images.yml + +permissions: + contents: read + packages: write # Required for GHCR + +jobs: + build-and-push: + runs-on: ubuntu-latest + strategy: + matrix: + ruby_image: + - ruby:2.7.8-slim-bullseye + - ruby:3.0.7-slim-bullseye + - ruby:3.1.7-slim-bookworm + - ruby:3.2.9-slim-bookworm + - ruby:3.3.9-slim-bookworm + - ruby:3.4.5-slim-bookworm + + steps: + - name: Check out current commit + uses: actions/checkout@v4 + + - name: Generate short image name and extract version + id: image_name + run: | + ruby_image="${{ matrix.ruby_image }}" + + # Extract full version for GEM_HOME (e.g., ruby:3.4.5-slim-bookworm -> 3.4.5) + if [[ "$ruby_image" == ruby:* ]]; then + full_version=$(echo "$ruby_image" | cut -d: -f2 | cut -d- -f1) + version=$(echo "$ruby_image" | cut -d: -f2 | cut -d. -f1,2) + short_name="sentry-ruby-devcontainer-${version}" + elif [[ "$ruby_image" == jruby:latest ]]; then + full_version="latest" + short_name="sentry-ruby-devcontainer-jruby-latest" + elif [[ "$ruby_image" == jruby:* ]]; then + full_version=$(echo "$ruby_image" | cut -d: -f2 | cut -d- -f1) + version=$(echo "$ruby_image" | cut -d: -f2 | cut -d. -f1,2) + short_name="sentry-ruby-devcontainer-jruby-${version}" + else + full_version="latest" + short_name="sentry-ruby-devcontainer-${ruby_image}" + fi + + echo "short_name=${short_name}" >> $GITHUB_OUTPUT + echo "full_version=${full_version}" >> $GITHUB_OUTPUT + echo "Generated short image name: ${short_name}" + echo "Extracted full version: ${full_version}" + + - name: Build and push devcontainer image + id: build + uses: getsentry/action-build-and-push-images@main + with: + image_name: '${{ steps.image_name.outputs.short_name }}' + dockerfile_path: '.devcontainer/Dockerfile' + ghcr: true + publish_on_pr: true + build_args: | + IMAGE=${{ matrix.ruby_image }} + VERSION=${{ steps.image_name.outputs.full_version }} + + - name: Use outputs + run: | + echo "GHCR URL: ${{ steps.build.outputs.ghcr_image_url }}" diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml new file mode 100644 index 000000000..e5c7c1b6d --- /dev/null +++ b/.github/workflows/e2e_tests.yml @@ -0,0 +1,107 @@ +name: e2e tests + +on: + workflow_dispatch: + push: + branches: + - master + pull_request: + +permissions: + contents: read + +concurrency: + group: e2e-tests-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + e2e-tests: + name: e2e tests + runs-on: ubuntu-latest + timeout-minutes: 5 + + strategy: + fail-fast: false + matrix: + ruby_version: ["3.4.5"] + + env: + DOCKER_IMAGE: "ghcr.io/getsentry/sentry-ruby-devcontainer-3.4" + DOCKER_TAG: "c7f73e278f0f8ad6035d578685e7bfb34be5eb4c" + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up `.env` file + run: | + cd .devcontainer + cp .env.example .env + + - name: Restore rubygems cache + uses: actions/cache@v3 + with: + path: vendor/gems + key: ${{ runner.os }}-${{ matrix.ruby_version }}-gems-${{ hashFiles('Gemfile.lock', '*/Gemfile.lock', 'spec/apps/**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.ruby_version }}-gems- + + - name: Restore node_modules cache + uses: actions/cache@v3 + with: + path: spec/apps/svelte-mini/node_modules + key: ${{ runner.os }}-node-modules-${{ hashFiles('spec/apps/svelte-mini/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-modules- + + - name: Set up test container + run: | + docker compose \ + --file .devcontainer/docker-compose.yml \ + --env-file .devcontainer/.env \ + run --rm sentry-test \ + echo "Done" + + - name: Start test services + run: | + docker compose \ + --file .devcontainer/docker-compose.yml \ + --env-file .devcontainer/.env \ + up -d sentry-test + + - name: "Wait for rails-mini app to be ready" + uses: nev7n/wait_for_response@v1 + with: + url: 'http://localhost:4000/health' + responseCode: 200 + timeout: 90000 + interval: 500 + + - name: "Wait for svelte-mini app to be ready" + uses: nev7n/wait_for_response@v1 + with: + url: 'http://localhost:4001/health' + responseCode: 200 + timeout: 90000 + interval: 500 + + - name: Run e2e tests via sentry-test + run: | + docker compose \ + --file .devcontainer/docker-compose.yml \ + --env-file .devcontainer/.env \ + exec sentry-test \ + bundle exec rake + + - name: Stop e2e services + if: always() + run: docker compose --file .devcontainer/docker-compose.yml down + + - name: Upload test artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: e2e-test-logs-ruby-${{ matrix.ruby_version }} + path: | + log/sentry_debug_events.log + retention-days: 7 diff --git a/.gitignore b/.gitignore index 7e3c680b2..d08d6dcb0 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,8 @@ Gemfile.lock *.rdb .rgignore -examples/**/node_modules +node_modules +.vite + +.devcontainer/.env +vendor/gems diff --git a/.rspec b/.rspec new file mode 100644 index 000000000..b1a2596a0 --- /dev/null +++ b/.rspec @@ -0,0 +1,4 @@ +--require spec_helper +--format progress +--color +--order rand diff --git a/Gemfile b/Gemfile index 4c6001944..3cdd929ff 100644 --- a/Gemfile +++ b/Gemfile @@ -1,32 +1,13 @@ # frozen_string_literal: true -source "https://rubygems.org" -git_source(:github) { |name| "https://github.com/#{name}.git" } +eval_gemfile "Gemfile.dev" -gem "jar-dependencies" if RUBY_PLATFORM == "java" -gem "rake", "~> 12.0" - -ruby_version = Gem::Version.new(RUBY_VERSION) - -# Development tools -if ruby_version >= Gem::Version.new("2.7.0") - gem "debug", github: "ruby/debug", platform: :ruby - gem "irb" - gem "ruby-lsp-rspec" if ruby_version >= Gem::Version.new("3.0.0") && RUBY_PLATFORM != "java" +group :e2e do + gem "capybara" + gem "selenium-webdriver" end -if RUBY_VERSION >= "3.5" - gem "cgi" -end - -# For RSpec -gem "rspec", "~> 3.0" -gem "rspec-retry" -gem "simplecov" -gem "simplecov-cobertura", "~> 1.4" -gem "rexml" - -group :rubocop do - gem "rubocop-rails-omakase" - gem "rubocop-packaging" +group :sentry do + gem "sentry-ruby", path: "sentry-ruby" + gem "sentry-rails", path: "sentry-rails" end diff --git a/Gemfile.dev b/Gemfile.dev new file mode 100644 index 000000000..80f2b8920 --- /dev/null +++ b/Gemfile.dev @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +git_source(:github) { |name| "https://github.com/#{name}.git" } + +gem "jar-dependencies" if RUBY_PLATFORM == "java" +gem "rake", "~> 12.0" + +ruby_version = Gem::Version.new(RUBY_VERSION) + +# Development tools +if ruby_version >= Gem::Version.new("2.7.0") + gem "debug", github: "ruby/debug", platform: :ruby + gem "irb" + gem "ruby-lsp-rspec" if ruby_version >= Gem::Version.new("3.0.0") && RUBY_PLATFORM != "java" +end + +if RUBY_VERSION >= "3.5" + gem "cgi" +end + +# For RSpec +gem "rspec", "~> 3.0" +gem "rspec-retry" +gem "simplecov" +gem "simplecov-cobertura", "~> 1.4" +gem "rexml" + +if ruby_version >= Gem::Version.new("3.4") + gem "ostruct" +end + +group :rubocop do + gem "rubocop-rails-omakase" + gem "rubocop-packaging" +end diff --git a/Procfile b/Procfile new file mode 100644 index 000000000..321f75768 --- /dev/null +++ b/Procfile @@ -0,0 +1,2 @@ +rails: cd spec/apps/rails-mini && bundle exec ruby app.rb +svelte: cd spec/apps/svelte-mini && npm run dev diff --git a/Rakefile b/Rakefile new file mode 100644 index 000000000..d41b6a4e8 --- /dev/null +++ b/Rakefile @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require "rake/clean" +require_relative "lib/sentry/test/rake_tasks" + +Sentry::Test::RakeTasks.define_spec_tasks() + +task default: :spec diff --git a/sentry-delayed_job/Gemfile b/sentry-delayed_job/Gemfile index 622927537..ff1d677e2 100644 --- a/sentry-delayed_job/Gemfile +++ b/sentry-delayed_job/Gemfile @@ -3,7 +3,7 @@ source "https://rubygems.org" git_source(:github) { |name| "https://github.com/#{name}.git" } -eval_gemfile "../Gemfile" +eval_gemfile "../Gemfile.dev" # Specify your gem's dependencies in sentry-ruby.gemspec gemspec diff --git a/sentry-opentelemetry/Gemfile b/sentry-opentelemetry/Gemfile index b96a7ae54..4dde72bde 100644 --- a/sentry-opentelemetry/Gemfile +++ b/sentry-opentelemetry/Gemfile @@ -3,13 +3,11 @@ source "https://rubygems.org" git_source(:github) { |name| "https://github.com/#{name}.git" } -eval_gemfile "../Gemfile" +eval_gemfile "../Gemfile.dev" # Specify your gem's dependencies in sentry-ruby.gemspec gemspec -gem "ostruct" if RUBY_VERSION >= "3.4" - gem "opentelemetry-sdk" gem "opentelemetry-instrumentation-rails" diff --git a/sentry-rails/Gemfile b/sentry-rails/Gemfile index 7ad1838b3..f27453ac6 100644 --- a/sentry-rails/Gemfile +++ b/sentry-rails/Gemfile @@ -3,7 +3,7 @@ source "https://rubygems.org" git_source(:github) { |name| "https://github.com/#{name}.git" } -eval_gemfile "../Gemfile" +eval_gemfile "../Gemfile.dev" # Specify your gem's dependencies in sentry-ruby.gemspec gemspec diff --git a/sentry-resque/Gemfile b/sentry-resque/Gemfile index 2bfb6b8fa..1279c191c 100644 --- a/sentry-resque/Gemfile +++ b/sentry-resque/Gemfile @@ -3,13 +3,11 @@ source "https://rubygems.org" git_source(:github) { |name| "https://github.com/#{name}.git" } -eval_gemfile "../Gemfile" +eval_gemfile "../Gemfile.dev" # Specify your gem's dependencies in sentry-ruby.gemspec gemspec -gem "ostruct" if RUBY_VERSION >= "3.4" - gem "sentry-ruby", path: "../sentry-ruby" gem "resque-retry", "~> 1.8" diff --git a/sentry-ruby.code-workspace b/sentry-ruby.code-workspace index 3b112f467..c88597553 100644 --- a/sentry-ruby.code-workspace +++ b/sentry-ruby.code-workspace @@ -32,10 +32,12 @@ "sentry-sidekiq": true, "sentry-opentelemetry": true }, - "rubyLsp.formatter": "rubocop", + "editor.formatOnSaveMode": "file", + "editor.formatOnSave": true, "rubyLsp.rubyVersionManager": { "identifier": "auto" - } + }, + "rubyLsp.formatter": "auto" }, "extensions": { "recommendations": [ diff --git a/sentry-ruby/Gemfile b/sentry-ruby/Gemfile index 7bccebd8b..deb2dd79b 100644 --- a/sentry-ruby/Gemfile +++ b/sentry-ruby/Gemfile @@ -3,7 +3,7 @@ source "https://rubygems.org" git_source(:github) { |name| "https://github.com/#{name}.git" } -eval_gemfile "../Gemfile" +eval_gemfile "../Gemfile.dev" gem "sentry-ruby", path: "./" @@ -11,8 +11,6 @@ rack_version = ENV["RACK_VERSION"] rack_version = "3.0.0" if rack_version.nil? gem "rack", "~> #{Gem::Version.new(rack_version)}" unless rack_version == "0" -gem "ostruct" if RUBY_VERSION >= "3.4" - redis_rb_version = ENV.fetch("REDIS_RB_VERSION", "5.0") gem "redis", "~> #{redis_rb_version}" diff --git a/sentry-ruby/spec/spec_helper.rb b/sentry-ruby/spec/spec_helper.rb index 031fac7a4..084c0c304 100644 --- a/sentry-ruby/spec/spec_helper.rb +++ b/sentry-ruby/spec/spec_helper.rb @@ -27,8 +27,8 @@ require "sentry-ruby" require "sentry/test_helper" -require "webmock/rspec" +require "webmock/rspec" require_relative "support/profiler" require_relative "support/stacktrace_test_fixture" diff --git a/sentry-sidekiq/Gemfile b/sentry-sidekiq/Gemfile index d2b5ad780..a62e0da59 100644 --- a/sentry-sidekiq/Gemfile +++ b/sentry-sidekiq/Gemfile @@ -3,7 +3,7 @@ source "https://rubygems.org" git_source(:github) { |name| "https://github.com/#{name}.git" } -eval_gemfile "../Gemfile" +eval_gemfile "../Gemfile.dev" # Specify your gem's dependencies in sentry-ruby.gemspec gemspec diff --git a/spec/apps/rails-mini/Gemfile b/spec/apps/rails-mini/Gemfile new file mode 100644 index 000000000..6ba99b8af --- /dev/null +++ b/spec/apps/rails-mini/Gemfile @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +gem "rake" +gem "puma" +gem 'railties', '~> 8.0' +gem 'actionpack', '~> 8.0' +gem 'sentry-ruby', path: Pathname(__dir__).join("../../..").realpath +gem 'sentry-rails', path: Pathname(__dir__).join("../../..").realpath diff --git a/spec/apps/rails-mini/app.rb b/spec/apps/rails-mini/app.rb new file mode 100644 index 000000000..2c31fffca --- /dev/null +++ b/spec/apps/rails-mini/app.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require "bundler/setup" + +Bundler.require + +ENV["RAILS_ENV"] = "development" + +require "action_controller" + +class RailsMiniApp < Rails::Application + config.hosts = nil + config.secret_key_base = "test_secret_key_base_for_rails_mini_app" + config.eager_load = false + config.logger = Logger.new($stdout) + config.log_level = :debug + config.api_only = true + config.force_ssl = false + + initializer :configure_sentry do + Sentry.init do |config| + config.dsn = ENV["SENTRY_DSN"] + config.breadcrumbs_logger = [:active_support_logger, :http_logger, :redis_logger] + config.traces_sample_rate = 1.0 + config.send_default_pii = true + config.sdk_logger.level = ::Logger::DEBUG + config.sdk_logger = Sentry::Logger.new($stdout) + config.debug = true + config.include_local_variables = true + config.release = "sentry-ruby-rails-mini-#{Time.now.utc}" + + config.transport.transport_class = Sentry::DebugTransport + config.sdk_debug_transport_log_file = "/workspace/sentry/log/sentry_debug_events.log" + config.background_worker_threads = 0 + end + end +end + +class ErrorController < ActionController::Base + before_action :set_cors_headers + + def error + result = 1 / 0 + render json: { result: result } + end + + private + + def set_cors_headers + response.headers['Access-Control-Allow-Origin'] = '*' + response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS' + response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, sentry-trace, baggage' + end +end + +class EventsController < ActionController::Base + before_action :set_cors_headers + + def health + render json: { + status: "ok", + timestamp: Time.now.utc.iso8601, + sentry_initialized: Sentry.initialized?, + log_file_writable: check_log_file_writable + } + end + + def trace_headers + headers = Sentry.get_trace_propagation_headers || {} + render json: { headers: headers } + end + + private + + def check_log_file_writable + log_file_path = "/workspace/sentry/log/sentry_debug_events.log" + File.writable?(File.dirname(log_file_path)) && + (!File.exist?(log_file_path) || File.writable?(log_file_path)) + rescue + false + end + + def set_cors_headers + response.headers['Access-Control-Allow-Origin'] = '*' + response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS' + response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, sentry-trace, baggage' + end +end + +RailsMiniApp.initialize! + +RailsMiniApp.routes.draw do + get '/health', to: 'events#health' + get '/error', to: 'error#error' + get '/trace_headers', to: 'events#trace_headers' + + # Add CORS headers for cross-origin requests from JS app + match '*path', to: proc { |env| + [200, { + 'Access-Control-Allow-Origin' => '*', + 'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Headers' => 'Content-Type, Authorization, sentry-trace, baggage', + 'Content-Type' => 'application/json' + }, ['{"status": "ok"}']] + }, via: :options +end + +if __FILE__ == $0 + require "rack" + require "rack/handler/puma" + + port = ENV.fetch("SENTRY_E2E_RAILS_APP_PORT", "4000").to_i + Rack::Handler::Puma.run(RailsMiniApp, Host: "0.0.0.0", Port: port) +end diff --git a/spec/apps/svelte-mini/index.html b/spec/apps/svelte-mini/index.html new file mode 100644 index 000000000..d67ab617f --- /dev/null +++ b/spec/apps/svelte-mini/index.html @@ -0,0 +1,12 @@ + + + + + + Svelte Mini App + + +
+ + + diff --git a/spec/apps/svelte-mini/package-lock.json b/spec/apps/svelte-mini/package-lock.json new file mode 100644 index 000000000..5959a82b8 --- /dev/null +++ b/spec/apps/svelte-mini/package-lock.json @@ -0,0 +1,1287 @@ +{ + "name": "svelte-mini", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "svelte-mini", + "version": "1.0.0", + "dependencies": { + "@sentry/svelte": "^7.0.0" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0", + "vite": "^5.0.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", + "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", + "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", + "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", + "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", + "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", + "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", + "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", + "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", + "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", + "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", + "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", + "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", + "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", + "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", + "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", + "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", + "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", + "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", + "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", + "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sentry-internal/feedback": { + "version": "7.120.3", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.120.3.tgz", + "integrity": "sha512-ewJJIQ0mbsOX6jfiVFvqMjokxNtgP3dNwUv+4nenN+iJJPQsM6a0ocro3iscxwVdbkjw5hY3BUV2ICI5Q0UWoA==", + "dependencies": { + "@sentry/core": "7.120.3", + "@sentry/types": "7.120.3", + "@sentry/utils": "7.120.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sentry-internal/replay-canvas": { + "version": "7.120.3", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.120.3.tgz", + "integrity": "sha512-s5xy+bVL1eDZchM6gmaOiXvTqpAsUfO7122DxVdEDMtwVq3e22bS2aiGa8CUgOiJkulZ+09q73nufM77kOmT/A==", + "dependencies": { + "@sentry/core": "7.120.3", + "@sentry/replay": "7.120.3", + "@sentry/types": "7.120.3", + "@sentry/utils": "7.120.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sentry-internal/tracing": { + "version": "7.120.3", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.120.3.tgz", + "integrity": "sha512-Ausx+Jw1pAMbIBHStoQ6ZqDZR60PsCByvHdw/jdH9AqPrNE9xlBSf9EwcycvmrzwyKspSLaB52grlje2cRIUMg==", + "dependencies": { + "@sentry/core": "7.120.3", + "@sentry/types": "7.120.3", + "@sentry/utils": "7.120.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/browser": { + "version": "7.120.3", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.120.3.tgz", + "integrity": "sha512-i9vGcK9N8zZ/JQo1TCEfHHYZ2miidOvgOABRUc9zQKhYdcYQB2/LU1kqlj77Pxdxf4wOa9137d6rPrSn9iiBxg==", + "dependencies": { + "@sentry-internal/feedback": "7.120.3", + "@sentry-internal/replay-canvas": "7.120.3", + "@sentry-internal/tracing": "7.120.3", + "@sentry/core": "7.120.3", + "@sentry/integrations": "7.120.3", + "@sentry/replay": "7.120.3", + "@sentry/types": "7.120.3", + "@sentry/utils": "7.120.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/core": { + "version": "7.120.3", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.3.tgz", + "integrity": "sha512-vyy11fCGpkGK3qI5DSXOjgIboBZTriw0YDx/0KyX5CjIjDDNgp5AGgpgFkfZyiYiaU2Ww3iFuKo4wHmBusz1uA==", + "dependencies": { + "@sentry/types": "7.120.3", + "@sentry/utils": "7.120.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/integrations": { + "version": "7.120.3", + "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.120.3.tgz", + "integrity": "sha512-6i/lYp0BubHPDTg91/uxHvNui427df9r17SsIEXa2eKDwQ9gW2qRx5IWgvnxs2GV/GfSbwcx4swUB3RfEWrXrQ==", + "dependencies": { + "@sentry/core": "7.120.3", + "@sentry/types": "7.120.3", + "@sentry/utils": "7.120.3", + "localforage": "^1.8.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/replay": { + "version": "7.120.3", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.120.3.tgz", + "integrity": "sha512-CjVq1fP6bpDiX8VQxudD5MPWwatfXk8EJ2jQhJTcWu/4bCSOQmHxnnmBM+GVn5acKUBCodWHBN+IUZgnJheZSg==", + "dependencies": { + "@sentry-internal/tracing": "7.120.3", + "@sentry/core": "7.120.3", + "@sentry/types": "7.120.3", + "@sentry/utils": "7.120.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sentry/svelte": { + "version": "7.120.3", + "resolved": "https://registry.npmjs.org/@sentry/svelte/-/svelte-7.120.3.tgz", + "integrity": "sha512-rwCaHSHkzVetHguVMld/s9fiCndtgbpI0W79zlSJLE5Ww+hV/x/te0mHaDD/e6+ItxYPwxeH+7rnCppTKUjzSw==", + "dependencies": { + "@sentry/browser": "7.120.3", + "@sentry/core": "7.120.3", + "@sentry/types": "7.120.3", + "@sentry/utils": "7.120.3", + "magic-string": "^0.30.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "svelte": "3.x || 4.x" + } + }, + "node_modules/@sentry/types": { + "version": "7.120.3", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.120.3.tgz", + "integrity": "sha512-C4z+3kGWNFJ303FC+FxAd4KkHvxpNFYAFN8iMIgBwJdpIl25KZ8Q/VdGn0MLLUEHNLvjob0+wvwlcRBBNLXOow==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/utils": { + "version": "7.120.3", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.120.3.tgz", + "integrity": "sha512-UDAOQJtJDxZHQ5Nm1olycBIsz2wdGX8SdzyGVHmD8EOQYAeDZQyIlQYohDe9nazdIOQLZCIc3fU0G9gqVLkaGQ==", + "dependencies": { + "@sentry/types": "7.120.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", + "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", + "dev": true, + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", + "debug": "^4.3.4", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.10", + "svelte-hmr": "^0.16.0", + "vitefu": "^0.2.5" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", + "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/code-red": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", + "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1", + "acorn": "^8.10.0", + "estree-walker": "^3.0.3", + "periscopic": "^3.1.0" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dependencies": { + "lie": "3.1.1" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", + "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.45.1", + "@rollup/rollup-android-arm64": "4.45.1", + "@rollup/rollup-darwin-arm64": "4.45.1", + "@rollup/rollup-darwin-x64": "4.45.1", + "@rollup/rollup-freebsd-arm64": "4.45.1", + "@rollup/rollup-freebsd-x64": "4.45.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", + "@rollup/rollup-linux-arm-musleabihf": "4.45.1", + "@rollup/rollup-linux-arm64-gnu": "4.45.1", + "@rollup/rollup-linux-arm64-musl": "4.45.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-musl": "4.45.1", + "@rollup/rollup-linux-s390x-gnu": "4.45.1", + "@rollup/rollup-linux-x64-gnu": "4.45.1", + "@rollup/rollup-linux-x64-musl": "4.45.1", + "@rollup/rollup-win32-arm64-msvc": "4.45.1", + "@rollup/rollup-win32-ia32-msvc": "4.45.1", + "@rollup/rollup-win32-x64-msvc": "4.45.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svelte": { + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.20.tgz", + "integrity": "sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==", + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/estree": "^1.0.1", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^4.0.0", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/svelte-hmr": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", + "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", + "dev": true, + "engines": { + "node": "^12.20 || ^14.13.1 || >= 16" + }, + "peerDependencies": { + "svelte": "^3.19.0 || ^4.0.0" + } + }, + "node_modules/vite": { + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", + "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "dev": true, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + } + } +} diff --git a/spec/apps/svelte-mini/package.json b/spec/apps/svelte-mini/package.json new file mode 100644 index 000000000..ad0e356d6 --- /dev/null +++ b/spec/apps/svelte-mini/package.json @@ -0,0 +1,18 @@ +{ + "name": "svelte-mini", + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0", + "vite": "^5.0.0" + }, + "dependencies": { + "@sentry/svelte": "^7.0.0" + } +} diff --git a/spec/apps/svelte-mini/src/App.svelte b/spec/apps/svelte-mini/src/App.svelte new file mode 100644 index 000000000..cf4cbcbcd --- /dev/null +++ b/spec/apps/svelte-mini/src/App.svelte @@ -0,0 +1,85 @@ + + +
+

Svelte Mini App

+

Click the button to trigger an error in the Rails app and test distributed tracing:

+ + + + {#if result} +
+

Result:

+
{result}
+
+ {/if} +
+ + diff --git a/spec/apps/svelte-mini/src/main.js b/spec/apps/svelte-mini/src/main.js new file mode 100644 index 000000000..f35c07198 --- /dev/null +++ b/spec/apps/svelte-mini/src/main.js @@ -0,0 +1,18 @@ +import * as Sentry from "@sentry/svelte"; +import App from './App.svelte' + +Sentry.init({ + dsn: import.meta.env.SENTRY_DSN_JS, + debug: true, + integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()], + tracesSampleRate: 1.0, + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 1.0, + tracePropagationTargets: ["localhost"], +}); + +const app = new App({ + target: document.getElementById('app'), +}) + +export default app diff --git a/spec/apps/svelte-mini/vite.config.js b/spec/apps/svelte-mini/vite.config.js new file mode 100644 index 000000000..68077160f --- /dev/null +++ b/spec/apps/svelte-mini/vite.config.js @@ -0,0 +1,33 @@ +import { defineConfig } from 'vite' +import { svelte } from '@sveltejs/vite-plugin-svelte' + +export default defineConfig({ + plugins: [ + svelte(), + { + name: 'health-check', + configureServer(server) { + server.middlewares.use('/health', (req, res, next) => { + if (req.method === 'GET') { + res.setHeader('Content-Type', 'application/json') + res.setHeader('Access-Control-Allow-Origin', '*') + res.end(JSON.stringify({ + status: 'ok', + timestamp: new Date().toISOString(), + service: 'svelte-mini' + })) + } else { + next() + } + }) + } + } + ], + server: { + port: parseInt(process.env.SENTRY_E2E_SVELTE_APP_PORT || '4001'), + host: '0.0.0.0', + }, + define: { + __RAILS_API_URL__: JSON.stringify(process.env.SENTRY_E2E_RAILS_APP_URL || 'http://localhost:4000') + } +}) diff --git a/spec/features/tracing_spec.rb b/spec/features/tracing_spec.rb new file mode 100644 index 000000000..231727b80 --- /dev/null +++ b/spec/features/tracing_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +RSpec.describe "Tracing", type: :feature do + it "works" do + visit "/error" + + expect(page).to have_content("Svelte Mini App") + expect(page).to have_button("Trigger Error") + + click_button "trigger-error-btn" + + expect(page).to have_content("Error:") + + expect(logged_events[:event_count]).to be > 0 + + error_events = logged_events[:events].select { |event| event["exception"] } + expect(error_events).not_to be_empty + + error_event = error_events.first + exception_values = error_event.dig("exception", "values") + expect(exception_values).not_to be_empty + expect(exception_values.first["type"]).to eq("ZeroDivisionError") + + transaction_events = logged_events[:events].select { |event| event["type"] == "transaction" } + + expect(error_event.dig("contexts", "trace")).not_to be_nil + error_trace_id = error_event.dig("contexts", "trace", "trace_id") + expect(error_trace_id).to_not be(nil) + + if transaction_events.any? + transaction_event = transaction_events.first + trace_context = transaction_event.dig("contexts", "trace") + + expect(trace_context).not_to be_nil + + transaction_trace_id = trace_context["trace_id"] + + expect(transaction_trace_id).to_not be(nil) + expect(error_trace_id).to eq(transaction_trace_id) + + if transaction_event["_meta"] && transaction_event["_meta"]["dsc"] + dsc = transaction_event["_meta"]["dsc"] + expect(dsc).to include("sample_rand") + + sample_rand = dsc["sample_rand"] + expect(sample_rand).to_not be(nil) + end + end + + logged_events[:envelopes].each do |envelope| + envelope["items"].each do |item| + if item["payload"] && item["payload"]["_meta"] && item["payload"]["_meta"]["dsc"] + dsc = item["payload"]["_meta"]["dsc"] + + if dsc["sample_rand"] + expect(dsc["sample_rand"]).to_not be(nil) + end + end + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 000000000..ba555a9fe --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "sentry-ruby" +require "sentry/test_helper" + +require "selenium-webdriver" + +require "capybara" +require "capybara/rspec" + +require_relative "support/test_helper" + +Capybara.configure do |config| + config.default_driver = :selenium_headless_chrome + config.javascript_driver = :selenium_headless_chrome + config.app_host = ENV.fetch("SENTRY_E2E_SVELTE_APP_URL", "http://localhost:4001") +end + +Capybara.register_driver :selenium_headless_chrome do |app| + options = Selenium::WebDriver::Chrome::Options.new + + options.add_argument("--headless") + options.add_argument("--disable-dev-shm-usage") + options.add_argument("--no-sandbox") + options.add_argument("--disable-gpu") + options.add_argument("--temp-profile") + options.binary = "/usr/bin/chromium" if File.exist?("/usr/bin/chromium") + + Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) +end + +RSpec.configure do |config| + config.include(Capybara::DSL, type: :e2e) + + config.include(Test::Helper) + + config.before(:suite) do + Test::Helper.perform_basic_setup do |config| + config.transport.transport_class = Sentry::DebugTransport + end + end +end diff --git a/spec/support/test_helper.rb b/spec/support/test_helper.rb new file mode 100644 index 000000000..3a5349f3b --- /dev/null +++ b/spec/support/test_helper.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Test + module Helper + module_function + + def logged_events + @logged_events ||= begin + extracted_events = [] + envelopes = [] + + logged_envelopes.each do |event_data| + envelopes << { + "headers" => event_data["envelope_headers"], + "items" => event_data["items"] + } + + event_data["items"].each do |item| + if item["headers"]["type"] == "event" + extracted_events << item["payload"] + end + end + end + + { + events: extracted_events, + envelopes: envelopes, + event_count: extracted_events.length, + envelope_count: envelopes.length + } + end + end + + def logged_envelopes + Sentry.get_current_client.transport.logged_envelopes + end + + # TODO: move this to a shared helper for all gems + def perform_basic_setup + Sentry.init do |config| + config.sdk_logger = Logger.new(nil) + config.dsn = Sentry::TestHelper::DUMMY_DSN + config.transport.transport_class = Sentry::DummyTransport + # so the events will be sent synchronously for testing + config.background_worker_threads = 0 + yield(config) if block_given? + end + end + end +end