diff --git a/.devcontainer/.env.example b/.devcontainer/.env.example index 24a575b5e..5ebb7ea84 100644 --- a/.devcontainer/.env.example +++ b/.devcontainer/.env.example @@ -6,7 +6,8 @@ VERSION="3.4.5" # E2E testing SENTRY_DSN="http://user:pass@sentry.localhost/project/42" -SENTRY_DSN_JS="http://user:pass@sentry-js.localhost/project/43" +# SENTRY_DSN_JS="http://user:pass@sentry-js.localhost/project/43" +SENTRY_DSN_JS="" SENTRY_E2E_RAILS_APP_PORT=4000 SENTRY_E2E_SVELTE_APP_PORT=4001 diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 9871a2679..fb0cab68a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -20,8 +20,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libffi-dev \ libgdbm-dev \ sqlite3 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - + +RUN apt-get update && apt-get install -y --no-install-recommends \ nodejs \ - npm \ chromium \ chromium-driver \ && apt-get clean \ diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml index e5c7c1b6d..68a9680f1 100644 --- a/.github/workflows/e2e_tests.yml +++ b/.github/workflows/e2e_tests.yml @@ -27,7 +27,7 @@ jobs: env: DOCKER_IMAGE: "ghcr.io/getsentry/sentry-ruby-devcontainer-3.4" - DOCKER_TAG: "c7f73e278f0f8ad6035d578685e7bfb34be5eb4c" + DOCKER_TAG: "d54d0ea1ee3e0d49f2b86d2689278447ccbbe0f9" steps: - name: Checkout code @@ -50,9 +50,9 @@ jobs: 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') }} + key: ${{ runner.os }}-${{ runner.arch }}-node-modules-${{ hashFiles('spec/apps/svelte-mini/package-lock.json') }} restore-keys: | - ${{ runner.os }}-node-modules- + ${{ runner.os }}-${{ runner.arch }}-node-modules- - name: Set up test container run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 226075393..4c9f8d319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +### Feature + +- Propagated sampling rates as specified in [Traces](https://develop.sentry.dev/sdk/telemetry/traces/#propagated-random-value) docs ([#2671](https://github.com/getsentry/sentry-ruby/pull/2671)) + ### Internal - Factor out do_request in HTTP transport ([#2662](https://github.com/getsentry/sentry-ruby/pull/2662)) diff --git a/sentry-ruby/lib/sentry-ruby.rb b/sentry-ruby/lib/sentry-ruby.rb index a2a43b4e3..986e6d1a8 100644 --- a/sentry-ruby/lib/sentry-ruby.rb +++ b/sentry-ruby/lib/sentry-ruby.rb @@ -10,6 +10,7 @@ require "sentry/utils/argument_checking_helper" require "sentry/utils/encoding_helper" require "sentry/utils/logging_helper" +require "sentry/utils/sample_rand" require "sentry/configuration" require "sentry/structured_logger" require "sentry/event" diff --git a/sentry-ruby/lib/sentry/hub.rb b/sentry-ruby/lib/sentry/hub.rb index c930902e8..53250288d 100644 --- a/sentry-ruby/lib/sentry/hub.rb +++ b/sentry-ruby/lib/sentry/hub.rb @@ -120,7 +120,8 @@ def start_transaction(transaction: nil, custom_sampling_context: {}, instrumente sampling_context = { transaction_context: transaction.to_hash, - parent_sampled: transaction.parent_sampled + parent_sampled: transaction.parent_sampled, + parent_sample_rate: transaction.parent_sample_rate } sampling_context.merge!(custom_sampling_context) @@ -357,6 +358,7 @@ def continue_trace(env, **options) parent_span_id: propagation_context.parent_span_id, parent_sampled: propagation_context.parent_sampled, baggage: propagation_context.baggage, + sample_rand: propagation_context.sample_rand, **options ) end diff --git a/sentry-ruby/lib/sentry/propagation_context.rb b/sentry-ruby/lib/sentry/propagation_context.rb index 9fb847dbc..a748cab8a 100644 --- a/sentry-ruby/lib/sentry/propagation_context.rb +++ b/sentry-ruby/lib/sentry/propagation_context.rb @@ -3,15 +3,16 @@ require "securerandom" require "sentry/baggage" require "sentry/utils/uuid" +require "sentry/utils/sample_rand" module Sentry class PropagationContext SENTRY_TRACE_REGEXP = Regexp.new( - "^[ \t]*" + # whitespace + "\\A[ \\t]{0,10}" + # limited whitespace at start "([0-9a-f]{32})?" + # trace_id "-?([0-9a-f]{16})?" + # span_id "-?([01])?" + # sampled - "[ \t]*$" # whitespace + "[ \\t]{0,10}\\z" # limited whitespace at end ) # An uuid that can be used to identify a trace. @@ -33,6 +34,52 @@ class PropagationContext # Please use the #get_baggage method for interfacing outside this class. # @return [Baggage, nil] attr_reader :baggage + # The propagated random value used for sampling decisions. + # @return [Float, nil] + attr_reader :sample_rand + + # Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header. + # + # @param sentry_trace [String] the sentry-trace header value from the previous transaction. + # @return [Array, nil] + def self.extract_sentry_trace(sentry_trace) + return if sentry_trace.to_s.empty? + + match = SENTRY_TRACE_REGEXP.match(sentry_trace) + return if match.nil? + + trace_id, parent_span_id, sampled_flag = match[1..3] + parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0" + + [trace_id, parent_span_id, parent_sampled] + end + + def self.extract_sample_rand_from_baggage(baggage, trace_id = nil) + return unless baggage&.items + + sample_rand_str = baggage.items["sample_rand"] + return unless sample_rand_str + + generator = Utils::SampleRand.new(trace_id: trace_id) + generator.generate_from_value(sample_rand_str) + end + + def self.generate_sample_rand(baggage, trace_id, parent_sampled) + generator = Utils::SampleRand.new(trace_id: trace_id) + + if baggage&.items && !parent_sampled.nil? + sample_rate_str = baggage.items["sample_rate"] + sample_rate = sample_rate_str&.to_f + + if sample_rate && !parent_sampled.nil? + generator.generate_from_sampling_decision(parent_sampled, sample_rate) + else + generator.generate_from_trace_id + end + else + generator.generate_from_trace_id + end + end def initialize(scope, env = nil) @scope = scope @@ -40,6 +87,7 @@ def initialize(scope, env = nil) @parent_sampled = nil @baggage = nil @incoming_trace = false + @sample_rand = nil if env sentry_trace_header = env["HTTP_SENTRY_TRACE"] || env[SENTRY_TRACE_HEADER_NAME] @@ -61,6 +109,8 @@ def initialize(scope, env = nil) Baggage.new({}) end + @sample_rand = self.class.extract_sample_rand_from_baggage(@baggage, @trace_id) || + @baggage.freeze! @incoming_trace = true end @@ -69,20 +119,7 @@ def initialize(scope, env = nil) @trace_id ||= Utils.uuid @span_id = Utils.uuid.slice(0, 16) - end - - # Extract the trace_id, parent_span_id and parent_sampled values from a sentry-trace header. - # - # @param sentry_trace [String] the sentry-trace header value from the previous transaction. - # @return [Array, nil] - def self.extract_sentry_trace(sentry_trace) - match = SENTRY_TRACE_REGEXP.match(sentry_trace) - return nil if match.nil? - - trace_id, parent_span_id, sampled_flag = match[1..3] - parent_sampled = sampled_flag.nil? ? nil : sampled_flag != "0" - - [trace_id, parent_span_id, parent_sampled] + @sample_rand ||= self.class.generate_sample_rand(@baggage, @trace_id, @parent_sampled) end # Returns the trace context that can be used to embed in an Event. @@ -123,6 +160,7 @@ def populate_head_baggage items = { "trace_id" => trace_id, + "sample_rand" => Utils::SampleRand.format(@sample_rand), "environment" => configuration.environment, "release" => configuration.release, "public_key" => configuration.dsn&.public_key diff --git a/sentry-ruby/lib/sentry/transaction.rb b/sentry-ruby/lib/sentry/transaction.rb index 60e262cf0..91eb3effe 100644 --- a/sentry-ruby/lib/sentry/transaction.rb +++ b/sentry-ruby/lib/sentry/transaction.rb @@ -2,6 +2,7 @@ require "sentry/baggage" require "sentry/profiler" +require "sentry/utils/sample_rand" require "sentry/propagation_context" module Sentry @@ -57,12 +58,17 @@ class Transaction < Span # @return [Profiler] attr_reader :profiler + # Sample rand value generated from trace_id + # @return [String] + attr_reader :sample_rand + def initialize( hub:, name: nil, source: :custom, parent_sampled: nil, baggage: nil, + sample_rand: nil, **options ) super(transaction: self, **options) @@ -82,12 +88,18 @@ def initialize( @effective_sample_rate = nil @contexts = {} @measurements = {} + @sample_rand = sample_rand unless @hub.profiler_running? @profiler = @configuration.profiler_class.new(@configuration) end init_span_recorder + + unless @sample_rand + generator = Utils::SampleRand.new(trace_id: @trace_id) + @sample_rand = generator.generate_from_trace_id + end end # @deprecated use Sentry.continue_trace instead. @@ -123,12 +135,15 @@ def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_h baggage.freeze! + sample_rand = extract_sample_rand_from_baggage(baggage, trace_id, parent_sampled) + new( trace_id: trace_id, parent_span_id: parent_span_id, parent_sampled: parent_sampled, hub: hub, baggage: baggage, + sample_rand: sample_rand, **options ) end @@ -139,6 +154,11 @@ def self.extract_sentry_trace(sentry_trace) PropagationContext.extract_sentry_trace(sentry_trace) end + def self.extract_sample_rand_from_baggage(baggage, trace_id, parent_sampled) + PropagationContext.extract_sample_rand_from_baggage(baggage, trace_id) || + PropagationContext.generate_sample_rand(baggage, trace_id, parent_sampled) + end + # @return [Hash] def to_hash hash = super @@ -153,6 +173,13 @@ def to_hash hash end + def parent_sample_rate + return unless @baggage&.items + + sample_rate_str = @baggage.items["sample_rate"] + sample_rate_str&.to_f + end + # @return [Transaction] def deep_dup copy = super @@ -225,7 +252,7 @@ def set_initial_sample_decision(sampling_context:) @effective_sample_rate /= 2**factor end - @sampled = Random.rand < @effective_sample_rate + @sampled = @sample_rand < @effective_sample_rate end if @sampled @@ -331,6 +358,7 @@ def populate_head_baggage items = { "trace_id" => trace_id, "sample_rate" => effective_sample_rate&.to_s, + "sample_rand" => Utils::SampleRand.format(@sample_rand), "sampled" => sampled&.to_s, "environment" => @environment, "release" => @release, diff --git a/sentry-ruby/lib/sentry/utils/sample_rand.rb b/sentry-ruby/lib/sentry/utils/sample_rand.rb new file mode 100644 index 000000000..9bdc7a2a4 --- /dev/null +++ b/sentry-ruby/lib/sentry/utils/sample_rand.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +module Sentry + module Utils + class SampleRand + PRECISION = 1_000_000.0 + FORMAT_PRECISION = 6 + + attr_reader :trace_id + + def self.valid?(value) + return false unless value + value >= 0.0 && value < 1.0 + end + + def self.format(value) + return unless value + + truncated = (value * PRECISION).floor / PRECISION + "%.#{FORMAT_PRECISION}f" % truncated + end + + def initialize(trace_id: nil) + @trace_id = trace_id + end + + def generate_from_trace_id + (random_from_trace_id * PRECISION).floor / PRECISION + end + + def generate_from_sampling_decision(sampled, sample_rate) + if invalid_sample_rate?(sample_rate) + fallback_generation + else + generate_based_on_sampling(sampled, sample_rate) + end + end + + def generate_from_value(sample_rand_value) + parsed_value = parse_value(sample_rand_value) + + if self.class.valid?(parsed_value) + parsed_value + else + fallback_generation + end + end + + private + + def random_from_trace_id + if @trace_id + Random.new(@trace_id[0, 16].to_i(16)) + else + Random.new + end.rand(1.0) + end + + def invalid_sample_rate?(sample_rate) + sample_rate.nil? || sample_rate <= 0.0 || sample_rate > 1.0 + end + + def fallback_generation + if @trace_id + (random_from_trace_id * PRECISION).floor / PRECISION + else + format_random(Random.rand(1.0)) + end + end + + def generate_based_on_sampling(sampled, sample_rate) + random = random_from_trace_id + + result = if sampled + random * sample_rate + elsif sample_rate == 1.0 + random + else + sample_rate + random * (1.0 - sample_rate) + end + + format_random(result) + end + + def format_random(value) + truncated = (value * PRECISION).floor / PRECISION + ("%.#{FORMAT_PRECISION}f" % truncated).to_f + end + + def parse_value(sample_rand_value) + Float(sample_rand_value) + rescue ArgumentError + nil + end + end + end +end diff --git a/sentry-ruby/spec/sentry/client_spec.rb b/sentry-ruby/spec/sentry/client_spec.rb index f455f6ca6..55b9129ee 100644 --- a/sentry-ruby/spec/sentry/client_spec.rb +++ b/sentry-ruby/spec/sentry/client_spec.rb @@ -273,6 +273,7 @@ def sentry_context expect(event.dynamic_sampling_context).to eq({ "environment" => "development", "public_key" => "12345", + "sample_rand" => Sentry::Utils::SampleRand.format(transaction.sample_rand), "sample_rate" => "1.0", "sampled" => "true", "transaction" => "test transaction", diff --git a/sentry-ruby/spec/sentry/hub_spec.rb b/sentry-ruby/spec/sentry/hub_spec.rb index b6c5884e5..f2f549d0d 100644 --- a/sentry-ruby/spec/sentry/hub_spec.rb +++ b/sentry-ruby/spec/sentry/hub_spec.rb @@ -641,4 +641,111 @@ expect(transport.events.count).to eq(0) end end + + describe "#continue_trace" do + before do + configuration.traces_sample_rate = 1.0 + end + + context "without incoming sentry trace" do + let(:env) { { "HTTP_FOO" => "bar" } } + + it "returns nil" do + expect(subject.continue_trace(env)).to be_nil + end + + it "generates new propagation context on scope" do + subject.continue_trace(env) + + propagation_context = subject.current_scope.propagation_context + expect(propagation_context.incoming_trace).to be false + end + end + + context "with incoming sentry trace" do + context "with sample_rand in baggage" do + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=0.123456" + } + end + + it "creates Transaction with sample_rand from PropagationContext" do + transaction = subject.continue_trace(env, name: "test_transaction") + + expect(transaction).to be_a(Sentry::Transaction) + + propagation_context = subject.current_scope.propagation_context + + expect(propagation_context.sample_rand).to eq(0.123456) + expect(transaction.sample_rand).to eq(0.123456) + end + + it "creates Transaction with correct trace properties" do + transaction = subject.continue_trace(env, name: "test_transaction") + + expect(transaction.trace_id).to eq("771a43a4192642f0b136d5159a501700") + expect(transaction.parent_span_id).to eq("7c51afd529da4a2a") + expect(transaction.parent_sampled).to be true + end + + it "preserves baggage in Transaction" do + transaction = subject.continue_trace(env, name: "test_transaction") + + baggage = transaction.get_baggage + + expect(baggage.items["trace_id"]).to eq("771a43a4192642f0b136d5159a501700") + expect(baggage.items["sample_rand"]).to eq("0.123456") + end + end + + context "without sample_rand in baggage" do + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700" + } + end + + it "creates Transaction with deterministic sample_rand" do + transaction = subject.continue_trace(env, name: "test_transaction") + + propagation_context = subject.current_scope.propagation_context + + expect(transaction.sample_rand).to eq(propagation_context.sample_rand) + + generator = Sentry::Utils::SampleRand.new(trace_id: "771a43a4192642f0b136d5159a501700") + expected = generator.generate_from_trace_id + + expect(transaction.sample_rand).to eq(expected) + end + end + + context "with tracing disabled" do + before do + configuration.traces_sample_rate = nil + end + + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=0.123456" + } + end + + it "returns nil even with valid incoming trace" do + expect(subject.continue_trace(env)).to be_nil + end + + it "still generates propagation context on scope" do + subject.continue_trace(env) + + propagation_context = subject.current_scope.propagation_context + expect(propagation_context.incoming_trace).to be true + expect(propagation_context.sample_rand).to eq(0.123456) + end + end + end + end end diff --git a/sentry-ruby/spec/sentry/net/http_spec.rb b/sentry-ruby/spec/sentry/net/http_spec.rb index 642e90d90..afe53f519 100644 --- a/sentry-ruby/spec/sentry/net/http_spec.rb +++ b/sentry-ruby/spec/sentry/net/http_spec.rb @@ -153,6 +153,7 @@ expect(request["baggage"]).to eq( "sentry-trace_id=#{transaction.trace_id},"\ "sentry-sample_rate=1.0,"\ + "sentry-sample_rand=#{Sentry::Utils::SampleRand.format(transaction.sample_rand)},"\ "sentry-sampled=true,"\ "sentry-environment=development,"\ "sentry-public_key=foobarbaz" diff --git a/sentry-ruby/spec/sentry/propagation_context/sample_rand_spec.rb b/sentry-ruby/spec/sentry/propagation_context/sample_rand_spec.rb new file mode 100644 index 000000000..788e95e59 --- /dev/null +++ b/sentry-ruby/spec/sentry/propagation_context/sample_rand_spec.rb @@ -0,0 +1,164 @@ +# frozen_string_literal: true + +RSpec.describe Sentry::PropagationContext do + before do + perform_basic_setup + end + + let(:scope) { Sentry.get_current_scope } + + describe "sample_rand integration" do + describe "#initialize" do + it "generates sample_rand when no incoming trace" do + context = described_class.new(scope) + + expect(context.sample_rand).to be_a(Float) + expect(context.sample_rand).to be >= 0.0 + expect(context.sample_rand).to be < 1.0 + end + + it "generates deterministic sample_rand from trace_id" do + context1 = described_class.new(scope) + context2 = described_class.new(scope) + + expect(context1.sample_rand).not_to eq(context2.sample_rand) + + trace_id = context1.trace_id + allow(Sentry::Utils).to receive(:uuid).and_return(trace_id) + context3 = described_class.new(scope) + + expect(context3.sample_rand).to eq(context1.sample_rand) + end + + context "with incoming trace" do + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=0.123456" + } + end + + it "uses sample_rand from incoming baggage" do + context = described_class.new(scope, env) + + expect(context.sample_rand).to eq(0.123456) + expect(context.incoming_trace).to be true + end + end + + context "with incoming trace but no sample_rand in baggage" do + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rate=0.5" + } + end + + it "generates sample_rand based on sampling decision" do + context = described_class.new(scope, env) + + expect(context.sample_rand).to be_a(Float) + expect(context.sample_rand).to be >= 0.0 + expect(context.sample_rand).to be < 1.0 + expect(context.incoming_trace).to be true + + expect(context.sample_rand).to be < 0.5 + end + + it "is deterministic for same trace" do + context1 = described_class.new(scope, env) + context2 = described_class.new(scope, env) + + expect(context1.sample_rand).to eq(context2.sample_rand) + end + end + + context "with incoming trace and parent_sampled=false" do + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-0", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rate=0.5" + } + end + + it "generates sample_rand based on unsampled decision" do + context = described_class.new(scope, env) + + expect(context.sample_rand).to be_a(Float) + expect(context.sample_rand).to be >= 0.0 + expect(context.sample_rand).to be < 1.0 + expect(context.incoming_trace).to be true + expect(context.parent_sampled).to be false + + expect(context.sample_rand).to be >= 0.5 + end + + it "is deterministic for same trace" do + context1 = described_class.new(scope, env) + context2 = described_class.new(scope, env) + + expect(context1.sample_rand).to eq(context2.sample_rand) + end + + it "uses parent's explicit unsampled decision instead of falling back to trace_id generation" do + context = described_class.new(scope, env) + + generator1 = Sentry::Utils::SampleRand.new(trace_id: "771a43a4192642f0b136d5159a501700") + expected_from_decision = generator1.generate_from_sampling_decision(false, 0.5) + + generator2 = Sentry::Utils::SampleRand.new(trace_id: "771a43a4192642f0b136d5159a501700") + expected_from_trace_id = generator2.generate_from_trace_id + + expect(context.sample_rand).to eq(expected_from_decision) + expect(context.sample_rand).not_to eq(expected_from_trace_id) + end + end + + context "with incoming trace but no baggage" do + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1" + } + end + + it "generates deterministic sample_rand from trace_id" do + context = described_class.new(scope, env) + + expect(context.sample_rand).to be_a(Float) + expect(context.sample_rand).to be >= 0.0 + expect(context.sample_rand).to be < 1.0 + expect(context.incoming_trace).to be true + + generator = Sentry::Utils::SampleRand.new(trace_id: "771a43a4192642f0b136d5159a501700") + expected = generator.generate_from_trace_id + expect(context.sample_rand).to eq(expected) + end + end + end + + describe "#get_baggage" do + it "includes sample_rand in baggage" do + context = described_class.new(scope) + baggage = context.get_baggage + + expect(baggage.items["sample_rand"]).to eq(Sentry::Utils::SampleRand.format(context.sample_rand)) + end + + context "with incoming baggage containing sample_rand" do + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=0.654321" + } + end + + it "preserves incoming sample_rand in baggage" do + context = described_class.new(scope, env) + baggage = context.get_baggage + + expect(baggage.items["sample_rand"]).to eq("0.654321") + end + end + end + end +end diff --git a/sentry-ruby/spec/sentry/propagation_context_spec.rb b/sentry-ruby/spec/sentry/propagation_context_spec.rb index 2ea2960ff..f94d76ed9 100644 --- a/sentry-ruby/spec/sentry/propagation_context_spec.rb +++ b/sentry-ruby/spec/sentry/propagation_context_spec.rb @@ -119,6 +119,7 @@ expect(baggage.mutable).to eq(false) expect(baggage.items).to eq({ "trace_id" => subject.trace_id, + "sample_rand" => Sentry::Utils::SampleRand.format(subject.sample_rand), "environment" => "test", "release" => "foobar", "public_key" => Sentry.configuration.dsn.public_key @@ -131,4 +132,49 @@ expect(subject.get_dynamic_sampling_context).to eq(subject.get_baggage.dynamic_sampling_context) end end + + describe ".extract_sentry_trace" do + it "extracts valid sentry-trace without whitespace" do + sentry_trace = "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1" + result = described_class.extract_sentry_trace(sentry_trace) + + expect(result).to eq(["771a43a4192642f0b136d5159a501700", "7c51afd529da4a2a", true]) + end + + it "extracts valid sentry-trace with leading and trailing whitespace" do + sentry_trace = " \t771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1\t " + result = described_class.extract_sentry_trace(sentry_trace) + + expect(result).to eq(["771a43a4192642f0b136d5159a501700", "7c51afd529da4a2a", true]) + end + + it "extracts sentry-trace without sampled flag" do + sentry_trace = "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a" + result = described_class.extract_sentry_trace(sentry_trace) + + expect(result).to eq(["771a43a4192642f0b136d5159a501700", "7c51afd529da4a2a", nil]) + end + + it "returns nil for invalid sentry-trace" do + expect(described_class.extract_sentry_trace("invalid")).to be_nil + expect(described_class.extract_sentry_trace("000-000-0")).to be_nil + expect(described_class.extract_sentry_trace("")).to be_nil + end + + it "handles excessive whitespace safely (ReDoS protection)" do + excessive_tabs = "\t" * 1000 + sentry_trace = "#{excessive_tabs}771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1#{excessive_tabs}" + result = described_class.extract_sentry_trace(sentry_trace) + + expect(result).to be_nil + end + + it "allows reasonable amount of whitespace" do + reasonable_whitespace = " \t \t \t \t " + sentry_trace = "#{reasonable_whitespace}771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1#{reasonable_whitespace}" + result = described_class.extract_sentry_trace(sentry_trace) + + expect(result).to eq(["771a43a4192642f0b136d5159a501700", "7c51afd529da4a2a", true]) + end + end end diff --git a/sentry-ruby/spec/sentry/rack/capture_exceptions_spec.rb b/sentry-ruby/spec/sentry/rack/capture_exceptions_spec.rb index 7ff6f4e4a..10cfa197a 100644 --- a/sentry-ruby/spec/sentry/rack/capture_exceptions_spec.rb +++ b/sentry-ruby/spec/sentry/rack/capture_exceptions_spec.rb @@ -279,11 +279,11 @@ def verify_transaction_doesnt_inherit_external_transaction(transaction, external end def wont_be_sampled_by_sdk - allow(Random).to receive(:rand).and_return(1.0) + allow_any_instance_of(Sentry::Utils::SampleRand).to receive(:generate_from_trace_id).and_return(1.0) end def will_be_sampled_by_sdk - allow(Random).to receive(:rand).and_return(0.3) + allow_any_instance_of(Sentry::Utils::SampleRand).to receive(:generate_from_trace_id).and_return(0.3) end before do @@ -430,7 +430,7 @@ def will_be_sampled_by_sdk context "when the transaction is sampled" do before do - allow(Random).to receive(:rand).and_return(0.4) + allow_any_instance_of(Sentry::Utils::SampleRand).to receive(:generate_from_trace_id).and_return(0.4) end it "starts a transaction and finishes it" do @@ -488,7 +488,7 @@ def will_be_sampled_by_sdk context "when the transaction is not sampled" do before do - allow(Random).to receive(:rand).and_return(0.6) + allow_any_instance_of(Sentry::Utils::SampleRand).to receive(:generate_from_trace_id).and_return(1.0) end it "doesn't do anything" do @@ -506,7 +506,7 @@ def will_be_sampled_by_sdk context "when there's an exception" do before do - allow(Random).to receive(:rand).and_return(0.4) + allow_any_instance_of(Sentry::Utils::SampleRand).to receive(:generate_from_trace_id).and_return(0.4) end it "still finishes the transaction" do diff --git a/sentry-ruby/spec/sentry/transaction_spec.rb b/sentry-ruby/spec/sentry/transaction_spec.rb index 4ee843479..7adc67186 100644 --- a/sentry-ruby/spec/sentry/transaction_spec.rb +++ b/sentry-ruby/spec/sentry/transaction_spec.rb @@ -257,22 +257,24 @@ end it "uses traces_sample_rate for sampling (positive result)" do - allow(Random).to receive(:rand).and_return(0.4) + # Create transaction with sample_rand < sample_rate (0.5) to ensure sampled + transaction = described_class.new(op: "rack.request", hub: Sentry.get_current_hub, sample_rand: 0.4) - subject.set_initial_sample_decision(sampling_context: {}) - expect(subject.sampled).to eq(true) - expect(subject.effective_sample_rate).to eq(0.5) + transaction.set_initial_sample_decision(sampling_context: {}) + expect(transaction.sampled).to eq(true) + expect(transaction.effective_sample_rate).to eq(0.5) expect(string_io.string).to include( "[Tracing] Starting transaction" ) end it "uses traces_sample_rate for sampling (negative result)" do - allow(Random).to receive(:rand).and_return(0.6) + # Create transaction with sample_rand > sample_rate (0.5) to ensure not sampled + transaction = described_class.new(op: "rack.request", hub: Sentry.get_current_hub, sample_rand: 0.6) - subject.set_initial_sample_decision(sampling_context: {}) - expect(subject.sampled).to eq(false) - expect(subject.effective_sample_rate).to eq(0.5) + transaction.set_initial_sample_decision(sampling_context: {}) + expect(transaction.sampled).to eq(false) + expect(transaction.effective_sample_rate).to eq(0.5) expect(string_io.string).to include( "[Tracing] Discarding transaction because it's not included in the random sample (sampling rate = 0.5)" ) @@ -474,11 +476,21 @@ end it "submits the event with the transaction's hub by default" do - subject.instance_variable_set(:@hub, another_hub) + # Create transaction with the specific hub from the beginning + transaction = described_class.new( + op: "sql.query", + description: "SELECT * FROM users;", + status: "ok", + sampled: true, + parent_sampled: true, + name: "foo", + source: :view, + hub: another_hub + ) expect(another_hub).to receive(:capture_event) - subject.finish + transaction.finish end end @@ -511,9 +523,13 @@ it "records lost event with reason backpressure" do expect(Sentry.get_current_client.transport).to receive(:any_rate_limited?).and_return(true) Sentry.backpressure_monitor.run - allow(Random).to receive(:rand).and_return(0.6) - subject.finish + # Create transaction with sample_rand that will be rejected after backpressure downsampling + # With traces_sample_rate = 1.0 and downsample_factor = 1, effective rate becomes 0.5 + # So sample_rand = 0.6 > 0.5 will be rejected + transaction = described_class.new(hub: Sentry.get_current_hub, sample_rand: 0.6) + + transaction.finish expect(Sentry.get_current_client.transport).to have_recorded_lost_event(:backpressure, 'transaction') expect(Sentry.get_current_client.transport).to have_recorded_lost_event(:backpressure, 'span') end @@ -556,7 +572,8 @@ name: "foo", source: source, hub: Sentry.get_current_hub, - baggage: incoming_baggage + baggage: incoming_baggage, + sample_rand: 0.123456 # Use a known value for predictable testing ) transaction.set_initial_sample_decision(sampling_context: {}) @@ -574,6 +591,7 @@ expect(baggage.items).to eq({ "environment" => "development", "public_key" => "12345", + "sample_rand" => "0.123456", # Known value from transaction creation "trace_id" => subject.trace_id, "transaction"=>"foo", "sample_rate" => "1.0", @@ -630,6 +648,7 @@ expect(baggage.items).to eq({ "environment" => "development", "public_key" => "12345", + "sample_rand" => "0.123456", # Known value from transaction creation "trace_id" => subject.trace_id, "transaction"=>"foo", "sample_rate" => "1.0", @@ -639,6 +658,46 @@ end end + describe ".extract_sample_rand_from_baggage" do + let(:trace_id) { "771a43a4192642f0b136d5159a501700" } + + it "returns trace_id generation when baggage is nil" do + result = described_class.extract_sample_rand_from_baggage(nil, trace_id, true) + + generator = Sentry::Utils::SampleRand.new(trace_id: trace_id) + expected = generator.generate_from_trace_id + + expect(result).to eq(expected) + end + + it "returns trace_id generation when baggage has no items" do + baggage = double("baggage", items: nil) + result = described_class.extract_sample_rand_from_baggage(baggage, trace_id, true) + + generator = Sentry::Utils::SampleRand.new(trace_id: trace_id) + expected = generator.generate_from_trace_id + + expect(result).to eq(expected) + end + + it "returns trace_id generation when sample_rand is invalid" do + baggage = double("baggage", items: { "sample_rand" => "1.5" }) + result = described_class.extract_sample_rand_from_baggage(baggage, trace_id, true) + + generator = Sentry::Utils::SampleRand.new(trace_id: trace_id) + expected = generator.generate_from_trace_id + + expect(result).to eq(expected) + end + + it "returns valid sample_rand from baggage when present" do + baggage = double("baggage", items: { "sample_rand" => "0.5" }) + result = described_class.extract_sample_rand_from_baggage(baggage, trace_id, true) + + expect(result).to eq(0.5) + end + end + describe "#set_name" do it "sets name and source directly" do subject.set_name("bar", source: :url) diff --git a/sentry-ruby/spec/sentry/transactions/sample_rand_propagation_spec.rb b/sentry-ruby/spec/sentry/transactions/sample_rand_propagation_spec.rb new file mode 100644 index 000000000..e3b337d2d --- /dev/null +++ b/sentry-ruby/spec/sentry/transactions/sample_rand_propagation_spec.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +RSpec.describe "Transactions and sample rand propagation" do + before do + perform_basic_setup do |config| + config.traces_sample_rate = 1.0 + end + end + + describe "sample_rand propagation" do + it "uses value from the baggage" do + env = { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=0.123456" + } + + transaction = Sentry.continue_trace(env, name: "backend_request", op: "http.server") + propagation_context = Sentry.get_current_scope.propagation_context + + expect(propagation_context.sample_rand).to eq(0.123456) + expect(transaction.sample_rand).to eq(0.123456) + + expect(transaction.sample_rand).to eq(propagation_context.sample_rand) + end + + it "generates deterministic value from trace id if there's no value in the baggage" do + env = { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700" + } + + transaction = Sentry.continue_trace(env, name: "backend_request", op: "http.server") + propagation_context = Sentry.get_current_scope.propagation_context + + expect(transaction.sample_rand).to eq(propagation_context.sample_rand) + + generator = Sentry::Utils::SampleRand.new(trace_id: "771a43a4192642f0b136d5159a501700") + expected = generator.generate_from_trace_id + + expect(transaction.sample_rand).to eq(expected) + expect(propagation_context.sample_rand).to eq(expected) + end + + [ + { sample_rand: 0.1, sample_rate: 0.5, should_sample: true }, + { sample_rand: 0.7, sample_rate: 0.5, should_sample: false }, + { sample_rand: 0.5, sample_rate: 0.5, should_sample: false }, + { sample_rand: 0.499999, sample_rate: 0.5, should_sample: true } + ].each do |test_case| + it "with #{test_case.inspect} - properly handles sampling decisions" do + Sentry.configuration.traces_sample_rate = test_case[:sample_rate] + + env = { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=#{test_case[:sample_rand]}" + } + + transaction = Sentry.continue_trace(env, name: "test") + Sentry.start_transaction(transaction: transaction) + + expect(transaction.sampled).to eq(test_case[:should_sample]) + end + end + + it "ensures baggage propagation includes correct sample_rand" do + env = { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=0.654321" + } + + transaction = Sentry.continue_trace(env, name: "backend_request") + baggage = transaction.get_baggage + + expect(baggage.items["sample_rand"]).to eq("0.654321") + + Sentry.get_current_scope.set_span(transaction) + + headers = Sentry.get_trace_propagation_headers + + expect(headers["baggage"]).to include("sentry-sample_rand=0.654321") + end + + it "handles edge cases and invalid sample_rand values" do + env = { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=1.5" + } + + transaction = Sentry.continue_trace(env, name: "test") + propagation_context = Sentry.get_current_scope.propagation_context + + generator = Sentry::Utils::SampleRand.new(trace_id: "771a43a4192642f0b136d5159a501700") + expected = generator.generate_from_trace_id + + expect(transaction.sample_rand).to eq(expected) + expect(propagation_context.sample_rand).to eq(expected) + end + + it "works correctly with multiple sequential requests" do + requests = [ + { sample_rand: 0.111111, trace_id: "11111111111111111111111111111111" }, + { sample_rand: 0.222222, trace_id: "22222222222222222222222222222222" }, + { sample_rand: 0.333333, trace_id: "33333333333333333333333333333333" } + ] + + requests.each do |request| + env = { + "HTTP_SENTRY_TRACE" => "#{request[:trace_id]}-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=#{request[:trace_id]},sentry-sample_rand=#{request[:sample_rand]}" + } + + transaction = Sentry.continue_trace(env, name: "test") + + expect(transaction.sample_rand).to eq(request[:sample_rand]) + expect(transaction.trace_id).to eq(request[:trace_id]) + end + end + end +end diff --git a/sentry-ruby/spec/sentry/transactions/trace_propagation_spec.rb b/sentry-ruby/spec/sentry/transactions/trace_propagation_spec.rb new file mode 100644 index 000000000..63eb34713 --- /dev/null +++ b/sentry-ruby/spec/sentry/transactions/trace_propagation_spec.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +RSpec.describe "Trace propagation" do + before do + perform_basic_setup do |config| + config.traces_sample_rate = 0.5 + + config.traces_sampler = lambda do |sampling_context| + parent_sample_rate = sampling_context[:parent_sample_rate] + + if parent_sample_rate + parent_sample_rate + else + 0.5 + end + end + end + end + + describe "end-to-end propagated sampling" do + it "maintains consistent sampling across distributed trace" do + root_transaction = Sentry.start_transaction(name: "root", op: "http.server") + + Sentry.get_current_scope.set_span(root_transaction) + + headers = Sentry.get_trace_propagation_headers + + expect(headers).to include("sentry-trace", "baggage") + expect(headers["baggage"]).to include("sentry-sample_rand=") + + baggage = root_transaction.get_baggage + sample_rand_from_baggage = baggage.items["sample_rand"] + + expect(sample_rand_from_baggage).to match(/\A\d+\.\d{6}\z/) + + sentry_trace = headers["sentry-trace"] + baggage_header = headers["baggage"] + + child_transaction = Sentry::Transaction.from_sentry_trace( + sentry_trace, + baggage: baggage_header + ) + + expect(child_transaction).not_to be_nil + + Sentry.get_current_scope.set_span(child_transaction) + + started_child = Sentry.start_transaction(transaction: child_transaction) + + expect(started_child.sampled).to eq(root_transaction.sampled) + expect(started_child.effective_sample_rate).to eq(root_transaction.effective_sample_rate) + + child_headers = Sentry.get_trace_propagation_headers + + expect(child_headers["baggage"]).to include("sentry-sample_rand=") + + child_baggage = started_child.get_baggage + child_sample_rand = child_baggage.items["sample_rand"] + + expect(child_sample_rand).to eq(sample_rand_from_baggage) + end + + it "handles missing sample_rand gracefully" do + sentry_trace = "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a" + baggage_header = "sentry-trace_id=#{sentry_trace},sentry-sample_rate=0.25" + + expected_sample_rand = Sentry::Utils::SampleRand.new(trace_id: "771a43a4192642f0b136d5159a501700").generate_from_trace_id + transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, baggage: baggage_header) + + expect(transaction.sample_rand).to eql(expected_sample_rand) + expect(transaction.baggage.items["sample_rate"]).to eql("0.25") + end + + it "handles invalid sample_rand in baggage" do + sentry_trace = "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1" + baggage_header = "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=1.5" + + expected_sample_rand = Sentry::Utils::SampleRand.new(trace_id: "771a43a4192642f0b136d5159a501700").generate_from_trace_id + transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, baggage: baggage_header) + + expect(transaction.sample_rand).to eq(expected_sample_rand) + + baggage = transaction.get_baggage + + expect(baggage.items).to eq({ + "trace_id" => "771a43a4192642f0b136d5159a501700", + "sample_rand" => "1.5" + }) + end + + it "works with PropagationContext for tracing without performance" do + env = { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=0.123456" + } + + scope = Sentry.get_current_scope + propagation_context = Sentry::PropagationContext.new(scope, env) + + expect(propagation_context.sample_rand).to eq(0.123456) + + baggage = propagation_context.get_baggage + expect(baggage.items["sample_rand"]).to eq("0.123456") + end + + it "demonstrates deterministic sampling behavior" do + trace_id = "771a43a4192642f0b136d5159a501700" + + results = 5.times.map do + transaction = Sentry::Transaction.new(trace_id: trace_id, hub: Sentry.get_current_hub) + Sentry.start_transaction(transaction: transaction) + transaction.sampled + end + + expect(results.uniq.length).to eq(1) + + sample_rands = 5.times.map do + transaction = Sentry::Transaction.new(trace_id: trace_id, hub: Sentry.get_current_hub) + baggage = transaction.get_baggage + baggage.items["sample_rand"] + end + + expect(sample_rands.uniq.length).to eq(1) + + generator = Sentry::Utils::SampleRand.new(trace_id: trace_id) + value = generator.generate_from_trace_id + expected_sample_rand = Sentry::Utils::SampleRand.format(value) + expect(sample_rands.first).to eq(expected_sample_rand) + end + + it "works with custom traces_sampler" do + sampling_contexts = [] + + Sentry.configuration.traces_sampler = lambda do |context| + sampling_contexts << context + context[:parent_sample_rate] || 0.5 + end + + baggage = Sentry::Baggage.new({ "sample_rate" => "0.75" }) + + parent_transaction = Sentry::Transaction.new( + hub: Sentry.get_current_hub, + baggage: baggage, + sample_rand: 0.6 + ) + + Sentry.start_transaction(transaction: parent_transaction) + + expect(sampling_contexts.last[:parent_sample_rate]).to eq(0.75) + expect(parent_transaction.sampled).to be true + + transaction_baggage = parent_transaction.get_baggage + + expect(transaction_baggage.items["sample_rand"]).to eq("0.600000") + end + end +end diff --git a/sentry-ruby/spec/sentry/utils/sample_rand_spec.rb b/sentry-ruby/spec/sentry/utils/sample_rand_spec.rb new file mode 100644 index 000000000..4e92d2e85 --- /dev/null +++ b/sentry-ruby/spec/sentry/utils/sample_rand_spec.rb @@ -0,0 +1,263 @@ +# frozen_string_literal: true + +RSpec.describe Sentry::Utils::SampleRand do + describe "#generate_from_trace_id" do + it "generates a float in range [0, 1) with 6 decimal places" do + trace_id = "abcdef1234567890abcdef1234567890" + generator = described_class.new(trace_id: trace_id) + sample_rand = generator.generate_from_trace_id + + expect(sample_rand).to be_a(Float) + expect(sample_rand).to be >= 0.0 + expect(sample_rand).to be < 1.0 + expect(sample_rand.to_s.split('.')[1].length).to be <= 6 + end + + it "generates deterministic values for the same trace_id" do + trace_id = "abcdef1234567890abcdef1234567890" + + generator1 = described_class.new(trace_id: trace_id) + generator2 = described_class.new(trace_id: trace_id) + sample_rand1 = generator1.generate_from_trace_id + sample_rand2 = generator2.generate_from_trace_id + + expect(sample_rand1).to eq(sample_rand2) + end + + it "generates different values for different trace_ids" do + trace_id1 = "abcdef1234567890abcdef1234567890" + trace_id2 = "fedcba0987654321fedcba0987654321" + + generator1 = described_class.new(trace_id: trace_id1) + generator2 = described_class.new(trace_id: trace_id2) + sample_rand1 = generator1.generate_from_trace_id + sample_rand2 = generator2.generate_from_trace_id + + expect(sample_rand1).not_to eq(sample_rand2) + end + + it "handles short trace_ids" do + trace_id = "abc123" + generator = described_class.new(trace_id: trace_id) + sample_rand = generator.generate_from_trace_id + + expect(sample_rand).to be_a(Float) + expect(sample_rand).to be >= 0.0 + expect(sample_rand).to be < 1.0 + end + end + + describe "#generate_from_sampling_decision" do + let(:trace_id) { "abcdef1234567890abcdef1234567890" } + + context "with valid sample_rate and sampled=true" do + it "generates value in range [0, sample_rate)" do + sample_rate = 0.5 + generator = described_class.new(trace_id: trace_id) + sample_rand = generator.generate_from_sampling_decision(true, sample_rate) + + expect(sample_rand).to be >= 0.0 + expect(sample_rand).to be < sample_rate + end + + it "is deterministic with trace_id" do + sample_rate = 0.5 + + generator1 = described_class.new(trace_id: trace_id) + generator2 = described_class.new(trace_id: trace_id) + sample_rand1 = generator1.generate_from_sampling_decision(true, sample_rate) + sample_rand2 = generator2.generate_from_sampling_decision(true, sample_rate) + + expect(sample_rand1).to eq(sample_rand2) + end + + it "never generates invalid values even with sample_rate = 1.0" do + generator = described_class.new(trace_id: trace_id) + result = generator.generate_from_sampling_decision(true, 1.0) + + expect(result).to be >= 0.0 + expect(result).to be < 1.0 + expect(described_class.valid?(result)).to be true + end + end + + context "with valid sample_rate and sampled=false" do + it "generates value in range [sample_rate, 1)" do + sample_rate = 0.3 + generator = described_class.new(trace_id: trace_id) + sample_rand = generator.generate_from_sampling_decision(false, sample_rate) + + expect(sample_rand).to be >= sample_rate + expect(sample_rand).to be < 1.0 + end + + it "is deterministic with trace_id" do + sample_rate = 0.3 + + generator1 = described_class.new(trace_id: trace_id) + generator2 = described_class.new(trace_id: trace_id) + sample_rand1 = generator1.generate_from_sampling_decision(false, sample_rate) + sample_rand2 = generator2.generate_from_sampling_decision(false, sample_rate) + + expect(sample_rand1).to eq(sample_rand2) + end + end + + context "with invalid sample_rate" do + it "falls back to trace_id generation when sample_rate is nil" do + generator1 = described_class.new(trace_id: trace_id) + generator2 = described_class.new(trace_id: trace_id) + expected = generator1.generate_from_trace_id + actual = generator2.generate_from_sampling_decision(true, nil) + + expect(actual).to eq(expected) + end + + it "falls back to trace_id generation when sample_rate is 0" do + generator1 = described_class.new(trace_id: trace_id) + generator2 = described_class.new(trace_id: trace_id) + expected = generator1.generate_from_trace_id + actual = generator2.generate_from_sampling_decision(true, 0.0) + + expect(actual).to eq(expected) + end + + it "falls back to trace_id generation when sample_rate > 1" do + generator1 = described_class.new(trace_id: trace_id) + generator2 = described_class.new(trace_id: trace_id) + expected = generator1.generate_from_trace_id + actual = generator2.generate_from_sampling_decision(true, 1.5) + + expect(actual).to eq(expected) + end + + it "uses Random.rand when no trace_id provided" do + generator = described_class.new + result = generator.generate_from_sampling_decision(true, nil) + + expect(result).to be_a(Float) + expect(result).to be >= 0.0 + expect(result).to be < 1.0 + expect(result.to_s.split('.')[1].length).to be <= 6 + end + + it "never generates values >= 1.0 even with edge case rounding" do + 1000.times do + generator = described_class.new + result = generator.generate_from_sampling_decision(true, nil) + expect(result).to be < 1.0 + end + end + + it "handles edge case where sampled is false and sample_rate is 1.0" do + generator = described_class.new(trace_id: "abcdef1234567890abcdef1234567890") + result = generator.generate_from_sampling_decision(false, 1.0) + + expect(result).to be_a(Float) + expect(result).to be >= 0.0 + expect(result).to be < 1.0 + expect(described_class.valid?(result)).to be true + end + end + end + + describe "#generate_from_value" do + it "accepts valid float values" do + generator = described_class.new + result = generator.generate_from_value(0.5) + expect(described_class.valid?(result)).to be true + expect(result).to eq(0.5) + end + + it "accepts valid string values" do + generator = described_class.new + result = generator.generate_from_value("0.5") + expect(described_class.valid?(result)).to be true + expect(result).to eq(0.5) + end + + it "falls back for invalid values" do + generator = described_class.new(trace_id: "abcdef1234567890abcdef1234567890") + result = generator.generate_from_value(1.5) + expect(described_class.valid?(result)).to be true + expect(result).to be >= 0.0 + expect(result).to be < 1.0 + end + end + + describe ".valid?" do + it "returns true for valid values" do + expect(described_class.valid?(0.5)).to be true + end + + it "returns false for invalid values" do + expect(described_class.valid?(1.5)).to be false + end + end + + describe "#generate_from_value with invalid string inputs" do + it "rejects non-numeric strings that convert to 0.0" do + generator = described_class.new(trace_id: "abcdef1234567890abcdef1234567890") + + invalid_strings = ["invalid", "abc", "not_a_number", ""] + + invalid_strings.each do |invalid_string| + result = generator.generate_from_value(invalid_string) + + expect(result).not_to eq(0.0) + expect(described_class.valid?(result)).to be true + expect(result).to be >= 0.0 + expect(result).to be < 1.0 + end + end + + it "accepts valid numeric strings" do + generator = described_class.new + + valid_strings = ["0.5", "0.0", "0.999999", "0", "0.000000"] + + valid_strings.each do |valid_string| + result = generator.generate_from_value(valid_string) + expect(result).to eq(valid_string.to_f) + expect(described_class.valid?(result)).to be true + end + end + + it "rejects numeric strings that are out of valid range" do + generator = described_class.new(trace_id: "abcdef1234567890abcdef1234567890") + + invalid_range_strings = ["1.0", "1.5", "-0.1", "-1.0"] + + invalid_range_strings.each do |invalid_string| + result = generator.generate_from_value(invalid_string) + + expect(result).not_to eq(invalid_string.to_f) + expect(described_class.valid?(result)).to be true + expect(result).to be >= 0.0 + expect(result).to be < 1.0 + end + end + + ["0.5abc", "abc0.5", "0..5", "0.5.0", "0.5e2", ".", "-"].each do |value| + it "rejects #{value.inspect} and generates from trace_id" do + generator = described_class.new(trace_id: "abcdef1234567890abcdef1234567890") + + result = generator.generate_from_value(value) + + expect(result).not_to eq(value.to_f) + expect(described_class.valid?(result)).to be true + expect(result).to be >= 0.0 + expect(result).to be < 1.0 + end + end + end + + describe ".format" do + it "formats float to 6 decimal places" do + expect(described_class.format(0.123456789)).to eq("0.123456") + expect(described_class.format(0.9999999)).to eq("0.999999") + expect(described_class.format(0.1)).to eq("0.100000") + expect(described_class.format(0.0)).to eq("0.000000") + end + end +end diff --git a/sentry-ruby/spec/sentry_spec.rb b/sentry-ruby/spec/sentry_spec.rb index 5f0e2c97f..a636872d8 100644 --- a/sentry-ruby/spec/sentry_spec.rb +++ b/sentry-ruby/spec/sentry_spec.rb @@ -457,8 +457,14 @@ expect(transaction.sampled).to eq(false) - allow(Random).to receive(:rand).and_return(0.4) - transaction = Sentry.continue_trace({ "sentry-trace" => not_sampled_trace }, op: "rack.request", name: "/payment") + transaction = Sentry.continue_trace( + { + "sentry-trace" => not_sampled_trace, + "baggage" => "sentry-sample_rand=0.4" + }, + op: "rack.request", + name: "/payment" + ) described_class.start_transaction(transaction: transaction) expect(transaction.sampled).to eq(true) @@ -470,42 +476,50 @@ end it "gives /payment 0.5 of rate" do - allow(Random).to receive(:rand).and_return(0.4) - transaction = described_class.start_transaction(op: "rack.request", name: "/payment") + transaction = Sentry::Transaction.new(hub: Sentry.get_current_hub, op: "rack.request", name: "/payment", sample_rand: 0.4) + + described_class.start_transaction(transaction: transaction) expect(transaction.sampled).to eq(true) - allow(Random).to receive(:rand).and_return(0.6) - transaction = described_class.start_transaction(op: "rack.request", name: "/payment") + transaction = Sentry::Transaction.new(hub: Sentry.get_current_hub, op: "rack.request", name: "/payment", sample_rand: 0.6) + + described_class.start_transaction(transaction: transaction) expect(transaction.sampled).to eq(false) end it "gives /api 0.2 of rate" do - allow(Random).to receive(:rand).and_return(0.1) - transaction = described_class.start_transaction(op: "rack.request", name: "/api") + transaction = Sentry::Transaction.new(hub: Sentry.get_current_hub, op: "rack.request", name: "/api", sample_rand: 0.1) + + described_class.start_transaction(transaction: transaction) expect(transaction.sampled).to eq(true) - allow(Random).to receive(:rand).and_return(0.3) - transaction = described_class.start_transaction(op: "rack.request", name: "/api") + transaction = Sentry::Transaction.new(hub: Sentry.get_current_hub, op: "rack.request", name: "/api", sample_rand: 0.3) + + described_class.start_transaction(transaction: transaction) expect(transaction.sampled).to eq(false) end it "gives other paths 0.1 of rate" do - allow(Random).to receive(:rand).and_return(0.05) - transaction = described_class.start_transaction(op: "rack.request", name: "/orders") + transaction = Sentry::Transaction.new(hub: Sentry.get_current_hub, op: "rack.request", name: "/orders", sample_rand: 0.05) + + described_class.start_transaction(transaction: transaction) expect(transaction.sampled).to eq(true) - allow(Random).to receive(:rand).and_return(0.2) - transaction = described_class.start_transaction(op: "rack.request", name: "/orders") + transaction = Sentry::Transaction.new(hub: Sentry.get_current_hub, op: "rack.request", name: "/orders", sample_rand: 0.2) + + described_class.start_transaction(transaction: transaction) expect(transaction.sampled).to eq(false) end it "gives sidekiq ops 0.01 of rate" do - allow(Random).to receive(:rand).and_return(0.005) - transaction = described_class.start_transaction(op: "sidekiq") + transaction = Sentry::Transaction.new(hub: Sentry.get_current_hub, op: "sidekiq", sample_rand: 0.005) + + described_class.start_transaction(transaction: transaction) expect(transaction.sampled).to eq(true) - allow(Random).to receive(:rand).and_return(0.02) - transaction = described_class.start_transaction(op: "sidekiq") + transaction = Sentry::Transaction.new(hub: Sentry.get_current_hub, op: "sidekiq", sample_rand: 0.02) + + described_class.start_transaction(transaction: transaction) expect(transaction.sampled).to eq(false) end end @@ -860,7 +874,7 @@ baggage = described_class.get_baggage propagation_context = described_class.get_current_scope.propagation_context - expect(baggage).to eq("sentry-trace_id=#{propagation_context.trace_id},sentry-environment=development,sentry-public_key=12345") + expect(baggage).to eq("sentry-trace_id=#{propagation_context.trace_id},sentry-sample_rand=#{Sentry::Utils::SampleRand.format(propagation_context.sample_rand)},sentry-environment=development,sentry-public_key=12345") end it "returns a valid baggage header from scope current span" do @@ -870,7 +884,7 @@ baggage = described_class.get_baggage - expect(baggage).to eq("sentry-trace_id=#{span.trace_id},sentry-sampled=true,sentry-environment=development,sentry-public_key=12345") + expect(baggage).to eq("sentry-trace_id=#{span.trace_id},sentry-sample_rand=#{Sentry::Utils::SampleRand.format(transaction.sample_rand)},sentry-sampled=true,sentry-environment=development,sentry-public_key=12345") end end @@ -953,6 +967,133 @@ expect(transaction.baggage.items).to eq(incoming_prop_context.get_baggage.items) expect(transaction.baggage.mutable).to eq(false) end + + describe "sample_rand propagation" do + before do + Sentry.configuration.traces_sample_rate = 1.0 + end + + context "with sample_rand in incoming baggage" do + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=0.123456" + } + end + + it "propagates sample_rand from PropagationContext to Transaction" do + # This is the critical test that would have caught the bug + transaction = described_class.continue_trace(env, name: "test") + + # Get the PropagationContext that was created + propagation_context = Sentry.get_current_scope.propagation_context + + # Verify PropagationContext extracted sample_rand from baggage + expect(propagation_context.sample_rand).to eq(0.123456) + + # Verify Transaction received the same sample_rand (this was the bug) + expect(transaction.sample_rand).to eq(0.123456) + end + + it "uses propagated sample_rand for sampling decision" do + # Test with sample_rand that should result in sampling + transaction = described_class.continue_trace(env, name: "test") + described_class.start_transaction(transaction: transaction) + + # With sample_rand=0.123456 and traces_sample_rate=1.0, should be sampled + expect(transaction.sampled).to be true + end + + it "maintains sample_rand consistency across the trace" do + transaction = described_class.continue_trace(env, name: "test") + + # The sample_rand should be included in outgoing baggage + baggage = transaction.get_baggage + expect(baggage.items["sample_rand"]).to eq("0.123456") + end + end + + context "with different sample_rand values" do + [0.000001, 0.123456, 0.500000, 0.999999].each do |sample_rand| + it "correctly propagates sentry-sample_rand=#{sample_rand}" do + env = { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=#{sample_rand}" + } + + transaction = described_class.continue_trace(env, name: "test") + expect(transaction.sample_rand).to eq(sample_rand) + end + end + end + + context "without sample_rand in incoming baggage" do + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700" + } + end + + it "propagates deterministic sample_rand from PropagationContext to Transaction" do + transaction = described_class.continue_trace(env, name: "test") + + # Get the PropagationContext that was created + propagation_context = Sentry.get_current_scope.propagation_context + + # Both should have the same deterministic sample_rand + expect(transaction.sample_rand).to eq(propagation_context.sample_rand) + + # Should be deterministic based on trace_id + generator = Sentry::Utils::SampleRand.new(trace_id: "771a43a4192642f0b136d5159a501700") + expected = generator.generate_from_trace_id + expect(transaction.sample_rand).to eq(expected) + end + end + + context "with invalid sample_rand in baggage" do + let(:env) do + { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-1", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=1.5" + } + end + + it "falls back to deterministic sample_rand generation" do + transaction = described_class.continue_trace(env, name: "test") + + # Should fall back to deterministic generation + generator = Sentry::Utils::SampleRand.new(trace_id: "771a43a4192642f0b136d5159a501700") + expected = generator.generate_from_trace_id + expect(transaction.sample_rand).to eq(expected) + end + end + + context "sampling decision consistency" do + it "makes consistent sampling decisions based on sample_rand" do + # Test case where sample_rand < sample_rate (should be sampled) + env_sampled = { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=0.3" + } + + Sentry.configuration.traces_sample_rate = 0.5 + transaction_sampled = described_class.continue_trace(env_sampled, name: "test") + described_class.start_transaction(transaction: transaction_sampled) + expect(transaction_sampled.sampled).to be true + + # Test case where sample_rand >= sample_rate (should not be sampled) + env_not_sampled = { + "HTTP_SENTRY_TRACE" => "771a43a4192642f0b136d5159a501700-7c51afd529da4a2a-", + "HTTP_BAGGAGE" => "sentry-trace_id=771a43a4192642f0b136d5159a501700,sentry-sample_rand=0.7" + } + + transaction_not_sampled = described_class.continue_trace(env_not_sampled, name: "test") + described_class.start_transaction(transaction: transaction_not_sampled) + expect(transaction_not_sampled.sampled).to be false + end + end + end end end diff --git a/spec/apps/rails-mini/app.rb b/spec/apps/rails-mini/app.rb index 2c31fffca..0750c387b 100644 --- a/spec/apps/rails-mini/app.rb +++ b/spec/apps/rails-mini/app.rb @@ -17,6 +17,14 @@ class RailsMiniApp < Rails::Application config.api_only = true config.force_ssl = false + def debug_log_path + @log_path ||= begin + path = Pathname(__dir__).join("../../../log") + FileUtils.mkdir_p(path) unless path.exist? + path.realpath + end + end + initializer :configure_sentry do Sentry.init do |config| config.dsn = ENV["SENTRY_DSN"] @@ -28,9 +36,8 @@ class RailsMiniApp < Rails::Application 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.sdk_debug_transport_log_file = debug_log_path.join("sentry_debug_events.log") config.background_worker_threads = 0 end end @@ -57,11 +64,22 @@ class EventsController < ActionController::Base before_action :set_cors_headers def health + sentry_initialized = Sentry.initialized? + sentry_dsn = ENV["SENTRY_DSN"] + render json: { status: "ok", timestamp: Time.now.utc.iso8601, - sentry_initialized: Sentry.initialized?, - log_file_writable: check_log_file_writable + sentry_initialized: sentry_initialized, + sentry_dsn_configured: !sentry_dsn.nil? && !sentry_dsn.empty?, + sentry_dsn: sentry_dsn, + sentry_environment: sentry_initialized ? Sentry.configuration.environment : nil, + debug_info: { + sentry_loaded: defined?(Sentry), + configuration_present: Sentry.respond_to?(:configuration), + dsn_configured: Sentry.respond_to?(:configuration) && Sentry.configuration&.dsn.present?, + env_dsn_value: sentry_dsn + } } end @@ -72,14 +90,6 @@ def trace_headers 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' @@ -94,7 +104,6 @@ def set_cors_headers 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' => '*', diff --git a/spec/apps/svelte-mini/package-lock.json b/spec/apps/svelte-mini/package-lock.json index 5959a82b8..46ea6a5e3 100644 --- a/spec/apps/svelte-mini/package-lock.json +++ b/spec/apps/svelte-mini/package-lock.json @@ -8,12 +8,15 @@ "name": "svelte-mini", "version": "1.0.0", "dependencies": { - "@sentry/svelte": "^7.0.0" + "@sentry/svelte": "~10.5" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0", - "vite": "^5.0.0" + "@sveltejs/vite-plugin-svelte": "^6.1.2", + "svelte": "~5.3.0", + "vite": "^7.1.3" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "~4.46" } }, "node_modules/@ampproject/remapping": { @@ -29,9 +32,9 @@ } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], @@ -41,13 +44,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], @@ -57,13 +60,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], @@ -73,13 +76,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], @@ -89,13 +92,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], @@ -105,13 +108,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], @@ -121,13 +124,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], @@ -137,13 +140,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], @@ -153,13 +156,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], @@ -169,13 +172,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], @@ -185,13 +188,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], @@ -201,13 +204,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], @@ -217,13 +220,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], @@ -233,13 +236,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], @@ -249,13 +252,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], @@ -265,13 +268,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], @@ -281,13 +284,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], @@ -297,13 +300,29 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], @@ -313,13 +332,29 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], @@ -329,13 +364,29 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], @@ -345,13 +396,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], @@ -361,13 +412,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], @@ -377,13 +428,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], @@ -393,13 +444,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -414,23 +465,23 @@ } }, "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==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "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==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.3.tgz", + "integrity": "sha512-UmTdvXnLlqQNOCJnyksjPs1G4GqXNGW1LrzCe8+8QoaLhhDeTXYBgJ3k6x61WIhlHX2U+VzEJ55TtIjR/HTySA==", "cpu": [ "arm" ], @@ -441,9 +492,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.3.tgz", + "integrity": "sha512-8NoxqLpXm7VyeI0ocidh335D6OKT0UJ6fHdnIxf3+6oOerZZc+O7r+UhvROji6OspyPm+rrIdb1gTXtVIqn+Sg==", "cpu": [ "arm64" ], @@ -454,9 +505,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.3.tgz", + "integrity": "sha512-csnNavqZVs1+7/hUKtgjMECsNG2cdB8F7XBHP6FfQjqhjF8rzMzb3SLyy/1BG7YSfQ+bG75Ph7DyedbUqwq1rA==", "cpu": [ "arm64" ], @@ -467,9 +518,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.3.tgz", + "integrity": "sha512-r2MXNjbuYabSIX5yQqnT8SGSQ26XQc8fmp6UhlYJd95PZJkQD1u82fWP7HqvGUf33IsOC6qsiV+vcuD4SDP6iw==", "cpu": [ "x64" ], @@ -480,9 +531,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.3.tgz", + "integrity": "sha512-uluObTmgPJDuJh9xqxyr7MV61Imq+0IvVsAlWyvxAaBSNzCcmZlhfYcRhCdMaCsy46ccZa7vtDDripgs9Jkqsw==", "cpu": [ "arm64" ], @@ -493,9 +544,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.3.tgz", + "integrity": "sha512-AVJXEq9RVHQnejdbFvh1eWEoobohUYN3nqJIPI4mNTMpsyYN01VvcAClxflyk2HIxvLpRcRggpX1m9hkXkpC/A==", "cpu": [ "x64" ], @@ -506,9 +557,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.3.tgz", + "integrity": "sha512-byyflM+huiwHlKi7VHLAYTKr67X199+V+mt1iRgJenAI594vcmGGddWlu6eHujmcdl6TqSNnvqaXJqZdnEWRGA==", "cpu": [ "arm" ], @@ -519,9 +570,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.3.tgz", + "integrity": "sha512-aLm3NMIjr4Y9LklrH5cu7yybBqoVCdr4Nvnm8WB7PKCn34fMCGypVNpGK0JQWdPAzR/FnoEoFtlRqZbBBLhVoQ==", "cpu": [ "arm" ], @@ -532,9 +583,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.3.tgz", + "integrity": "sha512-VtilE6eznJRDIoFOzaagQodUksTEfLIsvXymS+UdJiSXrPW7Ai+WG4uapAc3F7Hgs791TwdGh4xyOzbuzIZrnw==", "cpu": [ "arm64" ], @@ -545,9 +596,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.3.tgz", + "integrity": "sha512-dG3JuS6+cRAL0GQ925Vppafi0qwZnkHdPeuZIxIPXqkCLP02l7ka+OCyBoDEv8S+nKHxfjvjW4OZ7hTdHkx8/w==", "cpu": [ "arm64" ], @@ -558,9 +609,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.3.tgz", + "integrity": "sha512-iU8DxnxEKJptf8Vcx4XvAUdpkZfaz0KWfRrnIRrOndL0SvzEte+MTM7nDH4A2Now4FvTZ01yFAgj6TX/mZl8hQ==", "cpu": [ "loong64" ], @@ -570,10 +621,10 @@ "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==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.3.tgz", + "integrity": "sha512-VrQZp9tkk0yozJoQvQcqlWiqaPnLM6uY1qPYXvukKePb0fqaiQtOdMJSxNFUZFsGw5oA5vvVokjHrx8a9Qsz2A==", "cpu": [ "ppc64" ], @@ -584,9 +635,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.3.tgz", + "integrity": "sha512-uf2eucWSUb+M7b0poZ/08LsbcRgaDYL8NCGjUeFMwCWFwOuFcZ8D9ayPl25P3pl+D2FH45EbHdfyUesQ2Lt9wA==", "cpu": [ "riscv64" ], @@ -597,9 +648,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.3.tgz", + "integrity": "sha512-7tnUcDvN8DHm/9ra+/nF7lLzYHDeODKKKrh6JmZejbh1FnCNZS8zMkZY5J4sEipy2OW1d1Ncc4gNHUd0DLqkSg==", "cpu": [ "riscv64" ], @@ -610,9 +661,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.3.tgz", + "integrity": "sha512-MUpAOallJim8CsJK+4Lc9tQzlfPbHxWDrGXZm2z6biaadNpvh3a5ewcdat478W+tXDoUiHwErX/dOql7ETcLqg==", "cpu": [ "s390x" ], @@ -623,22 +674,21 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.3.tgz", + "integrity": "sha512-F42IgZI4JicE2vM2PWCe0N5mR5vR0gIdORPqhGQ32/u1S1v3kLtbZ0C/mi9FFk7C5T0PgdeyWEPajPjaUpyoKg==", "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.3.tgz", + "integrity": "sha512-oLc+JrwwvbimJUInzx56Q3ujL3Kkhxehg7O1gWAYzm8hImCd5ld1F2Gry5YDjR21MNb5WCKhC9hXgU7rRlyegQ==", "cpu": [ "x64" ], @@ -649,9 +699,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.3.tgz", + "integrity": "sha512-lOrQ+BVRstruD1fkWg9yjmumhowR0oLAAzavB7yFSaGltY8klttmZtCLvOXCmGE9mLIn8IBV/IFrQOWz5xbFPg==", "cpu": [ "arm64" ], @@ -662,9 +712,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.3.tgz", + "integrity": "sha512-vvrVKPRS4GduGR7VMH8EylCBqsDcw6U+/0nPDuIjXQRbHJc6xOBj+frx8ksfZAh6+Fptw5wHrN7etlMmQnPQVg==", "cpu": [ "ia32" ], @@ -675,9 +725,9 @@ ] }, "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==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.3.tgz", + "integrity": "sha512-fi3cPxCnu3ZeM3EwKZPgXbWoGzm2XHgB/WShKI81uj8wG0+laobmqy5wbgEwzstlbLu4MyO8C19FyhhWseYKNQ==", "cpu": [ "x64" ], @@ -687,178 +737,127 @@ "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==", + "node_modules/@sentry-internal/browser-utils": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.5.0.tgz", + "integrity": "sha512-4KIJdEj/8Ip9yqJleVSFe68r/U5bn5o/lYUwnFNEnDNxmpUbOlr7x3DXYuRFi1sfoMUxK9K1DrjnBkR7YYF00g==", "dependencies": { - "@sentry/core": "7.120.3", - "@sentry/types": "7.120.3", - "@sentry/utils": "7.120.3" + "@sentry/core": "10.5.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, - "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==", + "node_modules/@sentry-internal/feedback": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.5.0.tgz", + "integrity": "sha512-x79P4VZwUxb1EGZb9OQ5EEgrDWFCUlrbzHBwV/oocQA5Ss1SFz5u6cP5Ak7yJtILiJtdGzAyAoQOy4GKD13D4Q==", "dependencies": { - "@sentry/core": "7.120.3", - "@sentry/types": "7.120.3", - "@sentry/utils": "7.120.3" + "@sentry/core": "10.5.0" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "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==", + "node_modules/@sentry-internal/replay": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.5.0.tgz", + "integrity": "sha512-Dp4coE/nPzhFrYH3iVrpVKmhNJ1m/jGXMEDBCNg3wJZRszI41Hrj0jCAM0Y2S3Q4IxYOmFYaFbGtVpAznRyOHg==", "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" + "@sentry-internal/browser-utils": "10.5.0", + "@sentry/core": "10.5.0" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@sentry/core": { - "version": "7.120.3", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.3.tgz", - "integrity": "sha512-vyy11fCGpkGK3qI5DSXOjgIboBZTriw0YDx/0KyX5CjIjDDNgp5AGgpgFkfZyiYiaU2Ww3iFuKo4wHmBusz1uA==", + "node_modules/@sentry-internal/replay-canvas": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.5.0.tgz", + "integrity": "sha512-5nrRKd5swefd9+sFXFZ/NeL3bz/VxBls3ubAQ3afak15FikkSyHq3oKRKpMOtDsiYKXE3Bc0y3rF5A+y3OXjIA==", "dependencies": { - "@sentry/types": "7.120.3", - "@sentry/utils": "7.120.3" + "@sentry-internal/replay": "10.5.0", + "@sentry/core": "10.5.0" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "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==", + "node_modules/@sentry/browser": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.5.0.tgz", + "integrity": "sha512-o5pEJeZ/iZ7Fmaz2sIirThfnmSVNiP5ZYhacvcDi0qc288TmBbikCX3fXxq3xiSkhXfe1o5QIbNyovzfutyuVw==", "dependencies": { - "@sentry/core": "7.120.3", - "@sentry/types": "7.120.3", - "@sentry/utils": "7.120.3", - "localforage": "^1.8.1" + "@sentry-internal/browser-utils": "10.5.0", + "@sentry-internal/feedback": "10.5.0", + "@sentry-internal/replay": "10.5.0", + "@sentry-internal/replay-canvas": "10.5.0", + "@sentry/core": "10.5.0" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "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" - }, + "node_modules/@sentry/core": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.5.0.tgz", + "integrity": "sha512-jTJ8NhZSKB2yj3QTVRXfCCngQzAOLThQUxCl9A7Mv+XF10tP7xbH/88MVQ5WiOr2IzcmrB9r2nmUe36BnMlLjA==", "engines": { - "node": ">=12" + "node": ">=18" } }, "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==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@sentry/svelte/-/svelte-10.5.0.tgz", + "integrity": "sha512-lw50mOZne0t1qiU2rhoJ5d0YVDlpLiLxE40GH/gn2e2chM7DVZWkC85EerVrhfqMuwBDA2n8tXTm+WNfY7IThw==", "dependencies": { - "@sentry/browser": "7.120.3", - "@sentry/core": "7.120.3", - "@sentry/types": "7.120.3", - "@sentry/utils": "7.120.3", + "@sentry/browser": "10.5.0", + "@sentry/core": "10.5.0", "magic-string": "^0.30.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "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" + "svelte": "3.x || 4.x || 5.x" } }, "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==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.1.2.tgz", + "integrity": "sha512-7v+7OkUYelC2dhhYDAgX1qO2LcGscZ18Hi5kKzJQq7tQeXpH215dd0+J/HnX2zM5B3QKcIrTVqCGkZXAy5awYw==", "dev": true, "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", - "debug": "^4.3.4", + "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", + "debug": "^4.4.1", "deepmerge": "^4.3.1", "kleur": "^4.1.5", - "magic-string": "^0.30.10", - "svelte-hmr": "^0.16.0", - "vitefu": "^0.2.5" + "magic-string": "^0.30.17", + "vitefu": "^1.1.1" }, "engines": { - "node": "^18.0.0 || >=20" + "node": "^20.19 || ^22.12 || >=24" }, "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" + "svelte": "^5.0.0", + "vite": "^6.3.0 || ^7.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==", + "node_modules/@sveltejs/vite-plugin-svelte/node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.1.tgz", + "integrity": "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==", "dev": true, "dependencies": { - "debug": "^4.3.4" + "debug": "^4.4.1" }, "engines": { - "node": "^18.0.0 || >=20" + "node": "^20.19 || ^22.12 || >=24" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" + "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", + "svelte": "^5.0.0", + "vite": "^6.3.0 || ^7.0.0" } }, "node_modules/@types/estree": { @@ -877,6 +876,14 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-typescript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz", + "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==", + "peerDependencies": { + "acorn": ">=8.9.0" + } + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -893,30 +900,6 @@ "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", @@ -944,49 +927,74 @@ } }, "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==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "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" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==" + }, + "node_modules/esrap": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.9.tgz", + "integrity": "sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" } }, - "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/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, "node_modules/fsevents": { @@ -1003,11 +1011,6 @@ "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", @@ -1025,22 +1028,6 @@ "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", @@ -1054,11 +1041,6 @@ "@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", @@ -1083,22 +1065,24 @@ "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/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -1128,9 +1112,9 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.46.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.3.tgz", + "integrity": "sha512-RZn2XTjXb8t5g13f5YclGoilU/kwT696DIkY3sywjdZidNSi3+vseaQov7D7BZXVJCPv3pDWUN69C78GGbXsKw==", "dev": true, "dependencies": { "@types/estree": "1.0.8" @@ -1143,26 +1127,26 @@ "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", + "@rollup/rollup-android-arm-eabi": "4.46.3", + "@rollup/rollup-android-arm64": "4.46.3", + "@rollup/rollup-darwin-arm64": "4.46.3", + "@rollup/rollup-darwin-x64": "4.46.3", + "@rollup/rollup-freebsd-arm64": "4.46.3", + "@rollup/rollup-freebsd-x64": "4.46.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.3", + "@rollup/rollup-linux-arm-musleabihf": "4.46.3", + "@rollup/rollup-linux-arm64-gnu": "4.46.3", + "@rollup/rollup-linux-arm64-musl": "4.46.3", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.3", + "@rollup/rollup-linux-ppc64-gnu": "4.46.3", + "@rollup/rollup-linux-riscv64-gnu": "4.46.3", + "@rollup/rollup-linux-riscv64-musl": "4.46.3", + "@rollup/rollup-linux-s390x-gnu": "4.46.3", + "@rollup/rollup-linux-x64-gnu": "4.46.3", + "@rollup/rollup-linux-x64-musl": "4.46.3", + "@rollup/rollup-win32-arm64-msvc": "4.46.3", + "@rollup/rollup-win32-ia32-msvc": "4.46.3", + "@rollup/rollup-win32-x64-msvc": "4.46.3", "fsevents": "~2.3.2" } }, @@ -1170,61 +1154,68 @@ "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==", + "dev": true, "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==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.3.2.tgz", + "integrity": "sha512-a0vmqDZt91jRocPB4U+/o03w5p7ujknsSNvImeNRpfI06Rc/V8ObHmud9VomVlk2k2XnPTZcomtxb2H0kp5E6Q==", "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", + "@ampproject/remapping": "^2.3.0", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "acorn-typescript": "^1.4.13", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "esm-env": "^1.2.1", + "esrap": "^1.2.3", + "is-reference": "^3.0.3", "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" }, "engines": { - "node": ">=16" + "node": ">=18" } }, - "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==", + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", "dev": true, + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, "engines": { - "node": "^12.20 || ^14.13.1 || >= 16" + "node": ">=12.0.0" }, - "peerDependencies": { - "svelte": "^3.19.0 || ^4.0.0" + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz", + "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==", "dev": true, "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -1233,19 +1224,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -1266,22 +1263,33 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "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==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", + "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", "dev": true, "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "peerDependenciesMeta": { "vite": { "optional": true } } + }, + "node_modules/zimmerframe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", + "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==" } } } diff --git a/spec/apps/svelte-mini/package.json b/spec/apps/svelte-mini/package.json index ad0e356d6..820e4cecb 100644 --- a/spec/apps/svelte-mini/package.json +++ b/spec/apps/svelte-mini/package.json @@ -8,11 +8,14 @@ "preview": "vite preview" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0", - "vite": "^5.0.0" + "@sveltejs/vite-plugin-svelte": "^6.1.2", + "svelte": "~5.3.0", + "vite": "^7.1.3" }, "dependencies": { - "@sentry/svelte": "^7.0.0" + "@sentry/svelte": "~10.5" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "~4.46" } } diff --git a/spec/apps/svelte-mini/src/App.svelte b/spec/apps/svelte-mini/src/App.svelte index cf4cbcbcd..f7a93b0a6 100644 --- a/spec/apps/svelte-mini/src/App.svelte +++ b/spec/apps/svelte-mini/src/App.svelte @@ -1,14 +1,14 @@