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}
+
+ {/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