From ebe8d7a537113acc024fa09ae15511af6743aab4 Mon Sep 17 00:00:00 2001 From: Justin Bowen Date: Sun, 29 Mar 2026 10:03:13 -0700 Subject: [PATCH 1/3] feat: add telemetry engine for distributed tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a comprehensive telemetry system for collecting and reporting agent traces to hosted or self-hosted observability endpoints. Features: - Trace collection with spans for generation lifecycle - Token usage tracking (input, output, thinking) - Tool call tracing with timing - Error recording with backtraces - Async HTTP reporting with batching - Configurable sampling rate - Auto-instrumentation via railtie Configuration via YAML: telemetry: enabled: true endpoint: https://api.activeagents.ai/v1/traces api_key: your-api-key sample_rate: 1.0 Or programmatically: ActiveAgent::Telemetry.configure do |config| config.enabled = true config.endpoint = "https://..." config.api_key = "..." end 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/.vitepress/config.mts | 1 + docs/framework/telemetry.md | 380 ++++++++++++++++++ lib/active_agent.rb | 1 + lib/active_agent/railtie.rb | 30 ++ lib/active_agent/telemetry.rb | 162 ++++++++ lib/active_agent/telemetry/configuration.rb | 184 +++++++++ lib/active_agent/telemetry/instrumentation.rb | 149 +++++++ lib/active_agent/telemetry/reporter.rb | 147 +++++++ lib/active_agent/telemetry/span.rb | 267 ++++++++++++ lib/active_agent/telemetry/tracer.rb | 184 +++++++++ 10 files changed, 1505 insertions(+) create mode 100644 docs/framework/telemetry.md create mode 100644 lib/active_agent/telemetry.rb create mode 100644 lib/active_agent/telemetry/configuration.rb create mode 100644 lib/active_agent/telemetry/instrumentation.rb create mode 100644 lib/active_agent/telemetry/reporter.rb create mode 100644 lib/active_agent/telemetry/span.rb create mode 100644 lib/active_agent/telemetry/tracer.rb diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 02262704..cf41e49a 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -99,6 +99,7 @@ export default defineConfig({ { text: 'Providers', link: '/providers' }, { text: 'Configuration', link: '/framework/configuration' }, { text: 'Instrumentation', link: '/framework/instrumentation' }, + { text: 'Telemetry', link: '/framework/telemetry' }, { text: 'Retries', link: '/framework/retries' }, { text: 'Rails Integration', link: '/framework/rails' }, { text: 'Testing', link: '/framework/testing' }, diff --git a/docs/framework/telemetry.md b/docs/framework/telemetry.md new file mode 100644 index 00000000..296ab674 --- /dev/null +++ b/docs/framework/telemetry.md @@ -0,0 +1,380 @@ +--- +title: Telemetry & Observability +description: Collect and report agent traces to monitor AI operations, track costs, and debug generation flows with hosted or self-hosted observability. +--- + +# {{ $frontmatter.title }} + +ActiveAgent includes built-in telemetry for collecting and reporting agent traces. Monitor your AI operations, track token usage and costs, and debug generation flows with comprehensive observability. + +## Overview + +The telemetry system captures: +- **Generation Traces**: Full lifecycle of agent generations +- **Token Usage**: Input, output, and thinking tokens per request +- **Tool Calls**: Invocations with timing and results +- **Errors**: Exceptions with backtraces for debugging +- **Performance Metrics**: Response times and latencies + +## Quick Start + +### Hosted Service (ActiveAgents.ai) + +The fastest way to get started is with the hosted observability service: + +```yaml +# config/active_agent.yml +development: + openai: + service: "OpenAI" + access_token: <%= Rails.application.credentials.dig(:openai, :access_token) %> + + telemetry: + enabled: true + endpoint: https://api.activeagents.ai/v1/traces + api_key: <%= Rails.application.credentials.dig(:activeagents, :api_key) %> +``` + +### Self-Hosted + +For complete control, run your own telemetry endpoint: + +```yaml +# config/active_agent.yml +production: + telemetry: + enabled: true + endpoint: https://observability.mycompany.com/v1/traces + api_key: <%= ENV["TELEMETRY_API_KEY"] %> + service_name: my-rails-app +``` + +## Configuration + +### YAML Configuration + +Configure telemetry in your `config/active_agent.yml`: + +```yaml +telemetry: + enabled: true + endpoint: https://api.activeagents.ai/v1/traces + api_key: <%= Rails.application.credentials.dig(:activeagents, :api_key) %> + sample_rate: 1.0 # 1.0 = 100%, 0.5 = 50% + batch_size: 100 # Traces per batch + flush_interval: 5 # Seconds between flushes + service_name: my-app # Override app name + capture_bodies: false # Include request/response bodies + resource_attributes: # Custom attributes for all traces + deployment: production + team: ai-platform +``` + +### Programmatic Configuration + +Configure in an initializer for dynamic settings: + +```ruby +# config/initializers/active_agent.rb +ActiveAgent::Telemetry.configure do |config| + config.enabled = Rails.env.production? + config.endpoint = ENV.fetch("TELEMETRY_ENDPOINT", "https://api.activeagents.ai/v1/traces") + config.api_key = Rails.application.credentials.dig(:activeagents, :api_key) + config.sample_rate = 1.0 + config.service_name = Rails.application.class.module_parent_name.underscore +end +``` + +### Rails Configuration + +You can also configure via Rails config: + +```ruby +# config/application.rb +config.active_agent.telemetry = { + enabled: true, + endpoint: "https://api.activeagents.ai/v1/traces", + api_key: Rails.application.credentials.dig(:activeagents, :api_key) +} +``` + +## Configuration Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `enabled` | Boolean | `false` | Enable telemetry collection | +| `endpoint` | String | `https://api.activeagents.ai/v1/traces` | Telemetry receiver URL | +| `api_key` | String | `nil` | Authentication token | +| `sample_rate` | Float | `1.0` | Sampling rate (0.0 - 1.0) | +| `batch_size` | Integer | `100` | Traces per batch before flush | +| `flush_interval` | Integer | `5` | Seconds between auto-flushes | +| `timeout` | Integer | `10` | HTTP request timeout | +| `capture_bodies` | Boolean | `false` | Include message bodies | +| `service_name` | String | App name | Service identifier | +| `environment` | String | `Rails.env` | Environment name | +| `resource_attributes` | Hash | `{}` | Custom trace attributes | +| `redact_attributes` | Array | `["password", "secret", ...]` | Keys to redact | + +## Trace Structure + +Each trace captures the complete generation lifecycle: + +``` +Trace: WeatherAgent.forecast +├── Span: agent.prompt (prompt preparation) +├── Span: llm.generate (API call) +│ ├── tokens: { input: 150, output: 75, total: 225 } +│ └── model: "gpt-4o" +└── Span: tool.get_weather (tool invocation) + └── duration: 234ms +``` + +### Span Types + +| Type | Description | +|------|-------------| +| `root` | Root span for the entire generation | +| `prompt` | Prompt preparation and rendering | +| `llm` | LLM API call | +| `tool` | Tool/function invocation | +| `thinking` | Extended thinking (Anthropic) | +| `embedding` | Embedding generation | +| `error` | Error handling | + +## Manual Tracing + +Add custom spans to your traces: + +```ruby +class WeatherAgent < ApplicationAgent + def forecast(location:) + @location = location + + # Add custom span + if ActiveAgent::Telemetry.enabled? + span = ActiveAgent::Telemetry.span("geocode.lookup") + span.set_attribute("location", location) + coordinates = geocode_location(location) + span.finish + end + + prompt + end +end +``` + +### Trace Block + +Use the trace block for automatic timing and error handling: + +```ruby +ActiveAgent::Telemetry.trace("custom.operation") do |span| + span.set_attribute("user_id", current_user.id) + span.set_attribute("operation", "data_enrichment") + + result = perform_operation + + span.set_tokens(input: 100, output: 50) + result +end +``` + +## Sampling + +Control trace volume with sampling: + +```ruby +ActiveAgent::Telemetry.configure do |config| + # Sample 10% of production traffic + config.sample_rate = Rails.env.production? ? 0.1 : 1.0 +end +``` + +Sampling is deterministic per-trace, so all spans within a trace are included or excluded together. + +## Flushing & Shutdown + +Traces are batched and sent asynchronously. Force flush when needed: + +```ruby +# Flush buffered traces immediately +ActiveAgent::Telemetry.flush + +# Graceful shutdown (flush and wait) +ActiveAgent::Telemetry.shutdown +``` + +### Rails Integration + +Telemetry automatically flushes on Rails shutdown: + +```ruby +# config/initializers/active_agent.rb +at_exit { ActiveAgent::Telemetry.shutdown } +``` + +## Self-Hosting + +### Endpoint Requirements + +Your telemetry endpoint must accept POST requests with: + +**Headers:** +- `Content-Type: application/json` +- `Authorization: Bearer ` +- `X-Service-Name: ` +- `X-Environment: ` + +**Payload:** +```json +{ + "traces": [ + { + "trace_id": "abc123...", + "service_name": "my-app", + "environment": "production", + "timestamp": "2024-01-15T10:30:00.123456Z", + "resource_attributes": {}, + "spans": [ + { + "span_id": "def456", + "trace_id": "abc123...", + "parent_span_id": null, + "name": "WeatherAgent.forecast", + "type": "root", + "start_time": "2024-01-15T10:30:00.123456Z", + "end_time": "2024-01-15T10:30:01.234567Z", + "duration_ms": 1111.11, + "status": "OK", + "attributes": { + "agent.class": "WeatherAgent", + "agent.action": "forecast" + }, + "tokens": { + "input": 150, + "output": 75, + "total": 225 + }, + "events": [] + } + ] + } + ], + "sdk": { + "name": "activeagent", + "version": "0.5.0", + "language": "ruby", + "runtime_version": "3.3.0" + } +} +``` + +### Example Rails Endpoint + +```ruby +# app/controllers/api/traces_controller.rb +class Api::TracesController < ApplicationController + skip_before_action :verify_authenticity_token + before_action :authenticate_api_key! + + def create + traces = params[:traces] + + traces.each do |trace| + Trace.create!( + trace_id: trace[:trace_id], + service_name: trace[:service_name], + environment: trace[:environment], + spans: trace[:spans], + timestamp: trace[:timestamp] + ) + end + + head :accepted + end + + private + + def authenticate_api_key! + api_key = request.headers["Authorization"]&.gsub(/^Bearer /, "") + head :unauthorized unless ApiKey.exists?(key: api_key) + end +end +``` + +## Comparison with Instrumentation + +ActiveAgent provides two complementary observability systems: + +| Feature | Instrumentation | Telemetry | +|---------|-----------------|-----------| +| **Purpose** | Local logging & metrics | Distributed tracing | +| **Transport** | ActiveSupport::Notifications | HTTP POST | +| **Destination** | Rails logs, local metrics | Remote endpoint | +| **Use Case** | Debugging, local monitoring | Production observability | +| **Overhead** | Minimal | Async, batched | + +Use **Instrumentation** for local development and debugging. Use **Telemetry** for production observability and analytics. + +## Security Considerations + +### Sensitive Data + +By default, telemetry redacts common sensitive attributes: +- `password`, `secret`, `token`, `key`, `credential`, `api_key` + +Add custom redactions: + +```ruby +ActiveAgent::Telemetry.configure do |config| + config.redact_attributes += ["ssn", "credit_card"] +end +``` + +### Message Bodies + +Message bodies are **not captured by default**. Enable with caution: + +```ruby +ActiveAgent::Telemetry.configure do |config| + config.capture_bodies = true # Only in controlled environments +end +``` + +## Troubleshooting + +### Traces Not Appearing + +1. **Check enabled status:** + ```ruby + puts ActiveAgent::Telemetry.enabled? # Should be true + ``` + +2. **Verify configuration:** + ```ruby + puts ActiveAgent::Telemetry.configuration.to_h + ``` + +3. **Check logs for errors:** + ```ruby + ActiveAgent::Telemetry.configure do |config| + config.logger = Rails.logger + end + ``` + +### High Memory Usage + +Reduce batch size or increase flush frequency: + +```ruby +ActiveAgent::Telemetry.configure do |config| + config.batch_size = 25 + config.flush_interval = 2 +end +``` + +## Related Documentation + +- **[Instrumentation](/framework/instrumentation)** - Local logging with ActiveSupport::Notifications +- **[Usage Statistics](/actions/usage)** - Token usage and cost tracking +- **[Configuration](/framework/configuration)** - General framework configuration diff --git a/lib/active_agent.rb b/lib/active_agent.rb index f39a0164..8603c735 100644 --- a/lib/active_agent.rb +++ b/lib/active_agent.rb @@ -106,6 +106,7 @@ module ActiveAgent autoload :Rescue, "active_agent/concerns/rescue" autoload :Tooling, "active_agent/concerns/tooling" autoload :View, "active_agent/concerns/view" + autoload :Telemetry class << self # Eagerly loads all ActiveAgent components and descendant agent classes. diff --git a/lib/active_agent/railtie.rb b/lib/active_agent/railtie.rb index 76477662..11392c9b 100644 --- a/lib/active_agent/railtie.rb +++ b/lib/active_agent/railtie.rb @@ -10,6 +10,7 @@ module ActiveAgent class Railtie < Rails::Railtie # :nodoc: config.active_agent = ActiveSupport::OrderedOptions.new config.active_agent.preview_paths = [] + config.active_agent.telemetry = ActiveSupport::OrderedOptions.new config.eager_load_namespaces << ActiveAgent initializer "active_agent.deprecator", before: :load_environment_config do |app| @@ -40,6 +41,35 @@ class Railtie < Rails::Railtie # :nodoc: ActiveAgent.configuration_load(Rails.root.join("config", "active_agent.yml")) # endregion configuration_load + # region telemetry_configuration + # Load telemetry configuration from activeagent.yml or Rails config + telemetry_config = ActiveAgent.configuration[:telemetry] + if telemetry_config.is_a?(Hash) + ActiveAgent::Telemetry.configure do |config| + config.load_from_hash(telemetry_config) + end + end + + # Also support Rails config.active_agent.telemetry + if options.telemetry.present? + ActiveAgent::Telemetry.configure do |config| + config.enabled = options.telemetry[:enabled] if options.telemetry.key?(:enabled) + config.endpoint = options.telemetry[:endpoint] if options.telemetry.key?(:endpoint) + config.api_key = options.telemetry[:api_key] if options.telemetry.key?(:api_key) + config.sample_rate = options.telemetry[:sample_rate] if options.telemetry.key?(:sample_rate) + config.service_name = options.telemetry[:service_name] if options.telemetry.key?(:service_name) + end + end + + # Apply instrumentation to ActiveAgent::Base when telemetry is enabled + if ActiveAgent::Telemetry.enabled? + ActiveSupport.on_load(:active_agent) do + include ActiveAgent::Telemetry::Instrumentation + instrument_telemetry! + end + end + # endregion telemetry_configuration + ActiveSupport.on_load(:active_agent) do include AbstractController::UrlFor extend ::AbstractController::Railties::RoutesHelpers.with(app.routes, false) diff --git a/lib/active_agent/telemetry.rb b/lib/active_agent/telemetry.rb new file mode 100644 index 00000000..51a0d170 --- /dev/null +++ b/lib/active_agent/telemetry.rb @@ -0,0 +1,162 @@ +# frozen_string_literal: true + +require "net/http" +require "json" +require "securerandom" + +module ActiveAgent + # Telemetry module for collecting and reporting agent traces. + # + # Provides optional observability by capturing agent generation traces, + # tool calls, token usage, and errors. Reports to a configured endpoint + # (self-hosted or ActiveAgents.ai hosted service). + # + # = Features + # + # * **Trace Collection**: Captures full generation lifecycle with spans + # * **Token Tracking**: Records input/output/thinking tokens per generation + # * **Tool Call Tracing**: Captures tool invocations with arguments and results + # * **Error Tracking**: Records errors with backtraces + # * **Async Reporting**: Non-blocking HTTP reporting with background thread + # + # = Configuration + # + # Configure in your Rails initializer or activeagent.yml: + # + # @example Basic configuration + # ActiveAgent::Telemetry.configure do |config| + # config.enabled = true + # config.endpoint = "https://api.activeagents.ai/v1/traces" + # config.api_key = Rails.application.credentials.dig(:activeagents, :api_key) + # end + # + # @example YAML configuration (config/activeagent.yml) + # telemetry: + # enabled: true + # endpoint: https://api.activeagents.ai/v1/traces + # api_key: <%= Rails.application.credentials.dig(:activeagents, :api_key) %> + # sample_rate: 1.0 + # batch_size: 100 + # flush_interval: 5 + # + # @example Self-hosted endpoint + # ActiveAgent::Telemetry.configure do |config| + # config.endpoint = "https://observability.mycompany.com/v1/traces" + # config.api_key = ENV["TELEMETRY_API_KEY"] + # end + # + # @see ActiveAgent::Telemetry::Configuration + # @see ActiveAgent::Telemetry::Tracer + module Telemetry + extend ActiveSupport::Autoload + + autoload :Configuration + autoload :Tracer + autoload :Span + autoload :Reporter + autoload :Instrumentation + + class << self + # Returns the telemetry configuration instance. + # + # @return [Configuration] The configuration instance + def configuration + @configuration ||= Configuration.new + end + + # Configures telemetry with a block. + # + # @yield [config] Yields the configuration instance + # @yieldparam config [Configuration] The configuration to modify + # @return [Configuration] The modified configuration + # + # @example + # ActiveAgent::Telemetry.configure do |config| + # config.enabled = true + # config.endpoint = "https://api.activeagents.ai/v1/traces" + # config.api_key = "your-api-key" + # end + def configure + yield configuration if block_given? + configuration + end + + # Resets the configuration to defaults. + # + # @return [Configuration] New default configuration + def reset_configuration! + @configuration = Configuration.new + end + + # Returns whether telemetry is enabled and configured. + # + # @return [Boolean] True if telemetry should collect and report + def enabled? + configuration.enabled? && configuration.configured? + end + + # Returns the global tracer instance. + # + # @return [Tracer] The tracer instance + def tracer + @tracer ||= Tracer.new(configuration) + end + + # Starts a new trace for an agent generation. + # + # @param name [String] Name of the trace (typically agent.action) + # @param attributes [Hash] Additional trace attributes + # @yield [trace] Yields the trace for adding spans + # @return [Span] The root span of the trace + # + # @example + # ActiveAgent::Telemetry.trace("WeatherAgent.forecast") do |trace| + # trace.add_span("llm.generate", provider: "anthropic") + # trace.set_tokens(input: 100, output: 50) + # end + def trace(name, **attributes, &block) + return yield(NullSpan.new) unless enabled? + + tracer.trace(name, **attributes, &block) + end + + # Records a standalone span (outside of a trace context). + # + # @param name [String] Span name + # @param attributes [Hash] Span attributes + # @return [Span] The created span + def span(name, **attributes) + return NullSpan.new unless enabled? + + tracer.span(name, **attributes) + end + + # Flushes any buffered traces immediately. + # + # @return [void] + def flush + tracer.flush if enabled? + end + + # Shuts down telemetry, flushing remaining traces. + # + # @return [void] + def shutdown + tracer.shutdown if @tracer + end + end + + # Null span implementation for when telemetry is disabled. + # + # Provides no-op methods that match Span interface to avoid + # nil checks throughout the codebase. + class NullSpan + def add_span(name, **attributes); self; end + def set_attribute(key, value); self; end + def set_tokens(input: 0, output: 0, thinking: 0); self; end + def set_status(status, message = nil); self; end + def record_error(error); self; end + def finish; self; end + end + end +end diff --git a/lib/active_agent/telemetry/configuration.rb b/lib/active_agent/telemetry/configuration.rb new file mode 100644 index 00000000..611924b8 --- /dev/null +++ b/lib/active_agent/telemetry/configuration.rb @@ -0,0 +1,184 @@ +# frozen_string_literal: true + +module ActiveAgent + module Telemetry + # Configuration for telemetry collection and reporting. + # + # Stores settings for endpoint, authentication, sampling, and batching. + # Configuration can be set programmatically or loaded from YAML. + # + # @example Programmatic configuration + # ActiveAgent::Telemetry.configure do |config| + # config.enabled = true + # config.endpoint = "https://api.activeagents.ai/v1/traces" + # config.api_key = "your-api-key" + # config.sample_rate = 1.0 + # end + # + # @example YAML configuration (config/activeagent.yml) + # telemetry: + # enabled: true + # endpoint: https://api.activeagents.ai/v1/traces + # api_key: <%= ENV["ACTIVEAGENTS_API_KEY"] %> + # sample_rate: 1.0 + # batch_size: 100 + # flush_interval: 5 + # + class Configuration + # @return [Boolean] Whether telemetry is enabled (default: false) + attr_accessor :enabled + + # @return [String] The endpoint URL for sending traces + attr_accessor :endpoint + + # @return [String] API key for authentication + attr_accessor :api_key + + # @return [Float] Sampling rate from 0.0 to 1.0 (default: 1.0) + attr_accessor :sample_rate + + # @return [Integer] Number of traces to batch before sending (default: 100) + attr_accessor :batch_size + + # @return [Integer] Seconds between automatic flushes (default: 5) + attr_accessor :flush_interval + + # @return [Integer] HTTP timeout in seconds (default: 10) + attr_accessor :timeout + + # @return [Boolean] Whether to capture request/response bodies (default: false) + attr_accessor :capture_bodies + + # @return [Array] Attributes to redact from traces + attr_accessor :redact_attributes + + # @return [String] Service name for trace attribution + attr_accessor :service_name + + # @return [String] Environment name (development, staging, production) + attr_accessor :environment + + # @return [Hash] Additional resource attributes to include in all traces + attr_accessor :resource_attributes + + # @return [Logger] Logger for telemetry operations + attr_accessor :logger + + # Default ActiveAgents.ai endpoint for hosted observability. + DEFAULT_ENDPOINT = "https://api.activeagents.ai/v1/traces" + + def initialize + @enabled = false + @endpoint = DEFAULT_ENDPOINT + @api_key = nil + @sample_rate = 1.0 + @batch_size = 100 + @flush_interval = 5 + @timeout = 10 + @capture_bodies = false + @redact_attributes = %w[password secret token key credential api_key] + @service_name = nil + @environment = Rails.env if defined?(Rails) + @resource_attributes = {} + @logger = nil + end + + # Returns whether telemetry collection is enabled. + # + # @return [Boolean] + def enabled? + @enabled == true + end + + # Returns whether telemetry is properly configured. + # + # Checks that endpoint and api_key are present. + # + # @return [Boolean] + def configured? + endpoint.present? && api_key.present? + end + + # Returns whether a trace should be sampled. + # + # Uses sample_rate to determine if trace should be collected. + # + # @return [Boolean] + def should_sample? + return true if sample_rate >= 1.0 + return false if sample_rate <= 0.0 + + rand < sample_rate + end + + # Resolves the service name for traces. + # + # Falls back to Rails application name or "activeagent". + # + # @return [String] + def resolved_service_name + @service_name || rails_app_name || "activeagent" + end + + # Returns the logger for telemetry operations. + # + # Falls back to Rails.logger or a null logger. + # + # @return [Logger] + def resolved_logger + @logger || (defined?(Rails) && Rails.logger) || Logger.new(File::NULL) + end + + # Loads configuration from a hash (typically from YAML). + # + # @param hash [Hash] Configuration hash + # @return [self] + def load_from_hash(hash) + hash = hash.with_indifferent_access if hash.respond_to?(:with_indifferent_access) + + @enabled = hash[:enabled] if hash.key?(:enabled) + @endpoint = hash[:endpoint] if hash.key?(:endpoint) + @api_key = hash[:api_key] if hash.key?(:api_key) + @sample_rate = hash[:sample_rate].to_f if hash.key?(:sample_rate) + @batch_size = hash[:batch_size].to_i if hash.key?(:batch_size) + @flush_interval = hash[:flush_interval].to_i if hash.key?(:flush_interval) + @timeout = hash[:timeout].to_i if hash.key?(:timeout) + @capture_bodies = hash[:capture_bodies] if hash.key?(:capture_bodies) + @redact_attributes = hash[:redact_attributes] if hash.key?(:redact_attributes) + @service_name = hash[:service_name] if hash.key?(:service_name) + @environment = hash[:environment] if hash.key?(:environment) + @resource_attributes = hash[:resource_attributes] if hash.key?(:resource_attributes) + + self + end + + # Returns configuration as a hash for serialization. + # + # @return [Hash] + def to_h + { + enabled: enabled, + endpoint: endpoint, + api_key: api_key ? "[REDACTED]" : nil, + sample_rate: sample_rate, + batch_size: batch_size, + flush_interval: flush_interval, + timeout: timeout, + capture_bodies: capture_bodies, + service_name: resolved_service_name, + environment: environment + } + end + + private + + def rails_app_name + return nil unless defined?(Rails) && Rails.application + + Rails.application.class.module_parent_name.underscore + rescue StandardError + nil + end + end + end +end diff --git a/lib/active_agent/telemetry/instrumentation.rb b/lib/active_agent/telemetry/instrumentation.rb new file mode 100644 index 00000000..d5117fdb --- /dev/null +++ b/lib/active_agent/telemetry/instrumentation.rb @@ -0,0 +1,149 @@ +# frozen_string_literal: true + +module ActiveAgent + module Telemetry + # Auto-instrumentation for ActiveAgent generation lifecycle. + # + # When included in ActiveAgent::Base, automatically traces: + # - Agent generation (prompt_now, generate_now) + # - Tool calls + # - Streaming events + # - Errors + # + # @example Enabling instrumentation + # # In config/initializers/activeagent.rb + # ActiveAgent::Telemetry.configure do |config| + # config.enabled = true + # config.endpoint = "https://api.activeagents.ai/v1/traces" + # config.api_key = Rails.application.credentials.activeagents_api_key + # end + # + # # Instrumentation is automatically applied when telemetry is enabled + # + module Instrumentation + extend ActiveSupport::Concern + + included do + # Hook into generation lifecycle + around_generate :trace_generation if respond_to?(:around_generate) + end + + class_methods do + # Installs instrumentation on the agent class. + # + # Called automatically when telemetry is enabled. + def instrument_telemetry! + return if @telemetry_instrumented + + prepend GenerationInstrumentation + @telemetry_instrumented = true + end + end + + # Module prepended to intercept generation methods. + module GenerationInstrumentation + # Wraps process_prompt with telemetry tracing. + def process_prompt + return super unless Telemetry.enabled? + + Telemetry.trace("#{self.class.name}.#{action_name}", span_type: :root) do |span| + span.set_attribute("agent.class", self.class.name) + span.set_attribute("agent.action", action_name.to_s) + span.set_attribute("agent.provider", provider_name) if respond_to?(:provider_name) + span.set_attribute("agent.model", model_name) if respond_to?(:model_name) + + # Add prompt span + prompt_span = span.add_span("agent.prompt", span_type: :prompt) + prompt_span.set_attribute("messages.count", messages.size) if respond_to?(:messages) + prompt_span.finish + + # Execute generation with LLM span + llm_span = span.add_span("llm.generate", span_type: :llm) + llm_span.set_attribute("llm.provider", provider_name) if respond_to?(:provider_name) + llm_span.set_attribute("llm.model", model_name) if respond_to?(:model_name) + + begin + result = super + + # Record token usage from response + if result.respond_to?(:usage) + usage = result.usage + llm_span.set_tokens( + input: usage[:input_tokens] || usage["input_tokens"] || 0, + output: usage[:output_tokens] || usage["output_tokens"] || 0, + thinking: usage[:thinking_tokens] || usage["thinking_tokens"] || 0 + ) + span.set_tokens( + input: usage[:input_tokens] || usage["input_tokens"] || 0, + output: usage[:output_tokens] || usage["output_tokens"] || 0, + thinking: usage[:thinking_tokens] || usage["thinking_tokens"] || 0 + ) + end + + # Record tool calls if present + if result.respond_to?(:tool_calls) && result.tool_calls.present? + result.tool_calls.each do |tool_call| + tool_span = span.add_span("tool.#{tool_call[:name]}", span_type: :tool) + tool_span.set_attribute("tool.name", tool_call[:name]) + tool_span.set_attribute("tool.id", tool_call[:id]) if tool_call[:id] + tool_span.finish + end + end + + llm_span.set_status(:ok) + llm_span.finish + span.set_status(:ok) + + result + rescue StandardError => e + llm_span.record_error(e) + llm_span.finish + span.record_error(e) + raise + end + end + end + + # Wraps process_embed with telemetry tracing. + def process_embed + return super unless Telemetry.enabled? + + Telemetry.trace("#{self.class.name}.embed", span_type: :embedding) do |span| + span.set_attribute("agent.class", self.class.name) + span.set_attribute("agent.action", "embed") + span.set_attribute("agent.provider", provider_name) if respond_to?(:provider_name) + + begin + result = super + + if result.respond_to?(:usage) + usage = result.usage + span.set_tokens(input: usage[:input_tokens] || usage["input_tokens"] || 0) + end + + span.set_status(:ok) + result + rescue StandardError => e + span.record_error(e) + raise + end + end + end + + private + + def provider_name + self.class.generation_provider&.to_s || "unknown" + rescue StandardError + "unknown" + end + + def model_name + prompt_options[:model] || "unknown" + rescue StandardError + "unknown" + end + end + end + end +end diff --git a/lib/active_agent/telemetry/reporter.rb b/lib/active_agent/telemetry/reporter.rb new file mode 100644 index 00000000..7e691216 --- /dev/null +++ b/lib/active_agent/telemetry/reporter.rb @@ -0,0 +1,147 @@ +# frozen_string_literal: true + +require "net/http" +require "json" +require "uri" + +module ActiveAgent + module Telemetry + # Asynchronously reports traces to the telemetry endpoint. + # + # Buffers traces and sends them in batches to reduce network overhead. + # Uses a background thread for non-blocking transmission. + # + # @example + # reporter = Reporter.new(configuration) + # reporter.report(trace_payload) + # reporter.flush # Send immediately + # reporter.shutdown # Clean shutdown + # + class Reporter + # @return [Configuration] Telemetry configuration + attr_reader :configuration + + def initialize(configuration) + @configuration = configuration + @buffer = [] + @mutex = Mutex.new + @running = false + @thread = nil + @shutdown = false + + start_flush_thread if configuration.enabled? + end + + # Adds a trace to the buffer for transmission. + # + # @param trace [Hash] Trace payload + # @return [void] + def report(trace) + return unless configuration.enabled? + + @mutex.synchronize do + @buffer << trace + + # Flush immediately if buffer is full + if @buffer.size >= configuration.batch_size + flush_buffer + end + end + end + + # Flushes all buffered traces immediately. + # + # @return [void] + def flush + @mutex.synchronize do + flush_buffer + end + end + + # Shuts down the reporter, flushing remaining traces. + # + # @return [void] + def shutdown + @shutdown = true + flush + @thread&.join(5) # Wait up to 5 seconds for thread to finish + end + + private + + # Starts the background flush thread. + def start_flush_thread + @running = true + @thread = Thread.new do + Thread.current.name = "activeagent-telemetry-reporter" + + while @running && !@shutdown + sleep(configuration.flush_interval) + + @mutex.synchronize do + flush_buffer if @buffer.any? + end + end + end + end + + # Flushes the buffer by sending traces to the endpoint. + # + # Must be called within @mutex synchronization. + def flush_buffer + return if @buffer.empty? + + traces = @buffer.dup + @buffer.clear + + Thread.new { send_traces(traces) } + end + + # Sends traces to the configured endpoint. + # + # @param traces [Array] Traces to send + def send_traces(traces) + uri = URI.parse(configuration.endpoint) + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = uri.scheme == "https" + http.open_timeout = configuration.timeout + http.read_timeout = configuration.timeout + + request = Net::HTTP::Post.new(uri.request_uri) + request["Content-Type"] = "application/json" + request["Authorization"] = "Bearer #{configuration.api_key}" + request["User-Agent"] = "ActiveAgent/#{ActiveAgent::VERSION} Ruby/#{RUBY_VERSION}" + request["X-Service-Name"] = configuration.resolved_service_name + request["X-Environment"] = configuration.environment + + payload = { + traces: traces, + sdk: { + name: "activeagent", + version: ActiveAgent::VERSION, + language: "ruby", + runtime_version: RUBY_VERSION + } + } + + request.body = JSON.generate(payload) + + response = http.request(request) + + unless response.is_a?(Net::HTTPSuccess) + log_error("Failed to send traces: #{response.code} #{response.message}") + end + rescue StandardError => e + log_error("Error sending traces: #{e.class} - #{e.message}") + end + + # Logs an error message. + # + # @param message [String] Error message + def log_error(message) + configuration.resolved_logger.error("[ActiveAgent::Telemetry] #{message}") + end + end + end +end diff --git a/lib/active_agent/telemetry/span.rb b/lib/active_agent/telemetry/span.rb new file mode 100644 index 00000000..f8b8d282 --- /dev/null +++ b/lib/active_agent/telemetry/span.rb @@ -0,0 +1,267 @@ +# frozen_string_literal: true + +module ActiveAgent + module Telemetry + # Represents a single span in a trace. + # + # Spans capture discrete operations within a trace, such as LLM calls, + # tool invocations, or prompt rendering. Each span has timing, attributes, + # and can have child spans. + # + # @example Creating a span + # span = Span.new("llm.generate", trace_id: trace.trace_id) + # span.set_attribute("provider", "anthropic") + # span.set_attribute("model", "claude-3-5-sonnet") + # span.set_tokens(input: 100, output: 50) + # span.finish + # + class Span + # Span types for categorization + TYPES = { + root: "root", # Root span for entire generation + prompt: "prompt", # Prompt preparation/rendering + llm: "llm", # LLM API call + tool: "tool", # Tool invocation + thinking: "thinking", # Extended thinking (Anthropic) + embedding: "embedding", # Embedding generation + error: "error" # Error handling + }.freeze + + # Span status codes + STATUS = { + unset: "UNSET", + ok: "OK", + error: "ERROR" + }.freeze + + # @return [String] Unique identifier for this span + attr_reader :span_id + + # @return [String] Trace ID this span belongs to + attr_reader :trace_id + + # @return [String, nil] Parent span ID + attr_reader :parent_span_id + + # @return [String] Span name (e.g., "llm.generate", "tool.get_weather") + attr_reader :name + + # @return [String] Span type from TYPES + attr_reader :span_type + + # @return [Time] When the span started + attr_reader :start_time + + # @return [Time, nil] When the span ended + attr_reader :end_time + + # @return [Hash] Span attributes + attr_reader :attributes + + # @return [Array] Child spans + attr_reader :children + + # @return [String] Status code from STATUS + attr_reader :status + + # @return [String, nil] Status message + attr_reader :status_message + + # @return [Array] Events recorded during the span + attr_reader :events + + # Creates a new span. + # + # @param name [String] Span name + # @param trace_id [String] Parent trace ID + # @param parent_span_id [String, nil] Parent span ID + # @param span_type [Symbol] Type of span + # @param attributes [Hash] Initial attributes + def initialize(name, trace_id:, parent_span_id: nil, span_type: :root, **attributes) + @span_id = SecureRandom.hex(8) + @trace_id = trace_id + @parent_span_id = parent_span_id + @name = name + @span_type = TYPES[span_type] || span_type.to_s + @start_time = Time.current + @end_time = nil + @attributes = attributes.transform_keys(&:to_s) + @children = [] + @status = STATUS[:unset] + @status_message = nil + @events = [] + @tokens = { input: 0, output: 0, thinking: 0, total: 0 } + end + + # Creates a child span. + # + # @param name [String] Child span name + # @param span_type [Symbol] Type of span + # @param attributes [Hash] Span attributes + # @return [Span] The child span + def add_span(name, span_type: :root, **attributes) + child = Span.new( + name, + trace_id: trace_id, + parent_span_id: span_id, + span_type: span_type, + **attributes + ) + @children << child + child + end + + # Sets a single attribute. + # + # @param key [String, Symbol] Attribute key + # @param value [Object] Attribute value + # @return [self] + def set_attribute(key, value) + @attributes[key.to_s] = value + self + end + + # Sets multiple attributes at once. + # + # @param attrs [Hash] Attributes to set + # @return [self] + def set_attributes(attrs) + attrs.each { |k, v| set_attribute(k, v) } + self + end + + # Sets token usage for LLM spans. + # + # @param input [Integer] Input token count + # @param output [Integer] Output token count + # @param thinking [Integer] Thinking token count (Anthropic extended thinking) + # @return [self] + def set_tokens(input: 0, output: 0, thinking: 0) + @tokens = { + input: input, + output: output, + thinking: thinking, + total: input + output + thinking + } + set_attribute("tokens.input", input) + set_attribute("tokens.output", output) + set_attribute("tokens.thinking", thinking) if thinking > 0 + set_attribute("tokens.total", @tokens[:total]) + self + end + + # Returns token usage. + # + # @return [Hash] Token counts + def tokens + @tokens.dup + end + + # Sets the span status. + # + # @param code [Symbol] Status code (:ok, :error, :unset) + # @param message [String, nil] Optional status message + # @return [self] + def set_status(code, message = nil) + @status = STATUS[code] || STATUS[:unset] + @status_message = message + self + end + + # Records an error on the span. + # + # @param error [Exception] The error to record + # @return [self] + def record_error(error) + set_status(:error, error.message) + set_attribute("error.type", error.class.name) + set_attribute("error.message", error.message) + set_attribute("error.backtrace", error.backtrace&.first(10)&.join("\n")) + + add_event("exception", { + "exception.type" => error.class.name, + "exception.message" => error.message, + "exception.stacktrace" => error.backtrace&.join("\n") + }) + + self + end + + # Adds an event to the span. + # + # @param name [String] Event name + # @param attributes [Hash] Event attributes + # @return [self] + def add_event(name, attributes = {}) + @events << { + name: name, + timestamp: Time.current.iso8601(6), + attributes: attributes.transform_keys(&:to_s) + } + self + end + + # Marks the span as finished. + # + # @return [self] + def finish + @end_time = Time.current + set_status(:ok) if @status == STATUS[:unset] + self + end + + # Returns whether the span is finished. + # + # @return [Boolean] + def finished? + !@end_time.nil? + end + + # Returns the duration in milliseconds. + # + # @return [Float, nil] Duration or nil if not finished + def duration_ms + return nil unless finished? + + ((@end_time - @start_time) * 1000).round(2) + end + + # Serializes the span for transmission. + # + # @return [Hash] Serialized span data + def to_h + { + span_id: span_id, + trace_id: trace_id, + parent_span_id: parent_span_id, + name: name, + type: span_type, + start_time: start_time.iso8601(6), + end_time: end_time&.iso8601(6), + duration_ms: duration_ms, + status: status, + status_message: status_message, + attributes: attributes, + tokens: tokens, + events: events, + children: children.map(&:to_h) + } + end + + # Executes a block and records timing/errors. + # + # @yield Block to execute within the span + # @return [Object] Result of the block + def measure + result = yield + set_status(:ok) + result + rescue StandardError => e + record_error(e) + raise + ensure + finish + end + end + end +end diff --git a/lib/active_agent/telemetry/tracer.rb b/lib/active_agent/telemetry/tracer.rb new file mode 100644 index 00000000..efc27fa5 --- /dev/null +++ b/lib/active_agent/telemetry/tracer.rb @@ -0,0 +1,184 @@ +# frozen_string_literal: true + +module ActiveAgent + module Telemetry + # Manages trace creation and lifecycle. + # + # The Tracer creates traces, manages the current trace context, + # and coordinates with the Reporter for async transmission. + # + # @example Basic usage + # tracer = Tracer.new(configuration) + # tracer.trace("MyAgent.greet") do |span| + # span.set_attribute("user_id", 123) + # span.add_span("llm.generate", span_type: :llm) + # end + # + class Tracer + # @return [Configuration] Telemetry configuration + attr_reader :configuration + + # @return [Reporter] The reporter for sending traces + attr_reader :reporter + + # Thread-local storage for current trace context + CURRENT_SPAN_KEY = :active_agent_telemetry_current_span + + def initialize(configuration) + @configuration = configuration + @reporter = Reporter.new(configuration) + @mutex = Mutex.new + end + + # Creates and executes a new trace. + # + # @param name [String] Trace name (typically "AgentClass.action") + # @param attributes [Hash] Root span attributes + # @yield [span] Yields the root span for adding child spans + # @return [Object] Result of the block + # + # @example + # tracer.trace("WeatherAgent.forecast") do |span| + # span.set_attribute("location", "Seattle") + # result = do_llm_call + # span.set_tokens(input: 100, output: 50) + # result + # end + def trace(name, **attributes, &block) + return yield(Telemetry::NullSpan.new) unless should_trace? + + trace_id = generate_trace_id + root_span = Span.new( + name, + trace_id: trace_id, + span_type: :root, + **default_attributes.merge(attributes) + ) + + with_span(root_span) do + result = yield(root_span) + root_span.finish + report_trace(root_span) + result + end + rescue StandardError => e + root_span&.record_error(e) + root_span&.finish + report_trace(root_span) if root_span + raise + end + + # Creates a standalone span (not within a trace block). + # + # @param name [String] Span name + # @param attributes [Hash] Span attributes + # @return [Span] The created span + def span(name, **attributes) + return Telemetry::NullSpan.new unless should_trace? + + current = current_span + if current + current.add_span(name, **attributes) + else + Span.new(name, trace_id: generate_trace_id, **default_attributes.merge(attributes)) + end + end + + # Returns the current span from thread-local storage. + # + # @return [Span, nil] Current span or nil + def current_span + Thread.current[CURRENT_SPAN_KEY] + end + + # Flushes buffered traces immediately. + # + # @return [void] + def flush + reporter.flush + end + + # Shuts down the tracer and reporter. + # + # @return [void] + def shutdown + reporter.shutdown + end + + private + + # Executes block with span as current context. + # + # @param span [Span] Span to set as current + # @yield Block to execute + # @return [Object] Result of block + def with_span(span) + previous = Thread.current[CURRENT_SPAN_KEY] + Thread.current[CURRENT_SPAN_KEY] = span + yield + ensure + Thread.current[CURRENT_SPAN_KEY] = previous + end + + # Reports a completed trace to the reporter. + # + # @param span [Span] Root span of the trace + def report_trace(span) + reporter.report(build_trace_payload(span)) + end + + # Builds the trace payload for transmission. + # + # @param root_span [Span] Root span + # @return [Hash] Trace payload + def build_trace_payload(root_span) + { + trace_id: root_span.trace_id, + service_name: configuration.resolved_service_name, + environment: configuration.environment, + timestamp: Time.current.iso8601(6), + resource_attributes: configuration.resource_attributes, + spans: flatten_spans(root_span) + } + end + + # Flattens span hierarchy into array. + # + # @param span [Span] Root span + # @return [Array] Flattened span data + def flatten_spans(span) + result = [span.to_h.except(:children)] + span.children.each do |child| + result.concat(flatten_spans(child)) + end + result + end + + # Returns whether this trace should be sampled. + # + # @return [Boolean] + def should_trace? + configuration.enabled? && configuration.configured? && configuration.should_sample? + end + + # Generates a unique trace ID. + # + # @return [String] 32-character hex trace ID + def generate_trace_id + SecureRandom.hex(16) + end + + # Returns default attributes for all spans. + # + # @return [Hash] Default attributes + def default_attributes + { + "service.name" => configuration.resolved_service_name, + "service.environment" => configuration.environment, + "telemetry.sdk.name" => "activeagent", + "telemetry.sdk.version" => ActiveAgent::VERSION + } + end + end + end +end From ffde32636eb7f40ea212f793d541c010701c142e Mon Sep 17 00:00:00 2001 From: Justin Bowen Date: Sun, 29 Mar 2026 16:09:59 -0700 Subject: [PATCH 2/3] feat: add dashboard engine for telemetry visualization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a Rails Engine to the ActiveAgent gem that provides: - TelemetryTrace model for storing traces - TracesController for viewing traces in a dashboard - API::TracesController for ingesting traces - ProcessTelemetryTracesJob for async trace processing - ERB views with Tailwind CSS for the dashboard UI - Install generator for migrations and configuration The dashboard supports two modes: - Local mode: Single-tenant, no auth, synchronous processing - Multi-tenant mode: Account association, API key auth, async processing Configuration options: - multi_tenant: Enable multi-tenant mode - account_class: Class name for Account model - current_account_method: Controller method to get current account - trace_model_class: Custom trace model class - use_inertia: Enable React/Inertia frontend - authentication_method: Custom auth callback 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- lib/active_agent.rb | 1 + lib/active_agent/dashboard.rb | 95 +++++++++ .../dashboard/api/traces_controller.rb | 117 +++++++++++ .../dashboard/application_controller.rb | 31 +++ .../dashboard/traces_controller.rb | 103 +++++++++ .../process_telemetry_traces_job.rb | 77 +++++++ .../models/active_agent/telemetry_trace.rb | 198 ++++++++++++++++++ .../dashboard/traces/_trace_detail.html.erb | 105 ++++++++++ .../dashboard/traces/index.html.erb | 135 ++++++++++++ .../dashboard/traces/metrics.html.erb | 143 +++++++++++++ .../dashboard/traces/show.html.erb | 36 ++++ .../dashboard/application.html.erb | 94 +++++++++ lib/active_agent/dashboard/config/routes.rb | 17 ++ lib/active_agent/dashboard/engine.rb | 71 +++++++ lib/active_agent/railtie.rb | 3 +- lib/active_agent/telemetry/configuration.rb | 35 +++- lib/active_agent/telemetry/instrumentation.rb | 24 ++- lib/active_agent/telemetry/reporter.rb | 29 +++ .../dashboard/install_generator.rb | 64 ++++++ .../templates/active_agent_dashboard.rb.erb | 30 +++ ...reate_active_agent_telemetry_traces.rb.erb | 30 +++ 21 files changed, 1425 insertions(+), 13 deletions(-) create mode 100644 lib/active_agent/dashboard.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb create mode 100644 lib/active_agent/dashboard/app/controllers/active_agent/dashboard/traces_controller.rb create mode 100644 lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb create mode 100644 lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb create mode 100644 lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb create mode 100644 lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb create mode 100644 lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb create mode 100644 lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb create mode 100644 lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb create mode 100644 lib/active_agent/dashboard/config/routes.rb create mode 100644 lib/active_agent/dashboard/engine.rb create mode 100644 lib/generators/active_agent/dashboard/install_generator.rb create mode 100644 lib/generators/active_agent/dashboard/templates/active_agent_dashboard.rb.erb create mode 100644 lib/generators/active_agent/dashboard/templates/create_active_agent_telemetry_traces.rb.erb diff --git a/lib/active_agent.rb b/lib/active_agent.rb index 8603c735..adc56b5d 100644 --- a/lib/active_agent.rb +++ b/lib/active_agent.rb @@ -107,6 +107,7 @@ module ActiveAgent autoload :Tooling, "active_agent/concerns/tooling" autoload :View, "active_agent/concerns/view" autoload :Telemetry + autoload :Dashboard class << self # Eagerly loads all ActiveAgent components and descendant agent classes. diff --git a/lib/active_agent/dashboard.rb b/lib/active_agent/dashboard.rb new file mode 100644 index 00000000..d67a2866 --- /dev/null +++ b/lib/active_agent/dashboard.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +require "active_agent/dashboard/engine" + +module ActiveAgent + # Dashboard engine for visualizing telemetry data. + # + # Mount the engine in your routes to access the telemetry dashboard: + # + # # config/routes.rb + # mount ActiveAgent::Dashboard::Engine => "/active_agent" + # + # The dashboard provides: + # - Traces view: See all agent invocations with spans, timing, and token usage + # - Metrics view: Aggregate statistics and charts + # - Local storage mode: Store traces in your own database + # - Multi-tenant mode: For platforms with multiple accounts + # + # @example Local mode configuration (default) + # ActiveAgent::Dashboard.configure do |config| + # config.authentication_method = ->(controller) { controller.authenticate_admin! } + # end + # + # @example Multi-tenant mode configuration (for activeagents.ai) + # ActiveAgent::Dashboard.configure do |config| + # config.multi_tenant = true + # config.account_class = "Account" + # config.current_account_method = :current_account + # config.authentication_method = ->(controller) { controller.authenticate_user! } + # end + # + module Dashboard + class << self + # Authentication method to call on controllers + attr_accessor :authentication_method + + # Enable multi-tenant mode (requires account association) + attr_accessor :multi_tenant + + # Class name for the Account model (multi-tenant mode) + attr_accessor :account_class + + # Method to call on controller to get current account (multi-tenant mode) + attr_accessor :current_account_method + + # Custom trace model class (for host app overrides) + attr_accessor :trace_model_class + + # Enable React/Inertia frontend instead of ERB + attr_accessor :use_inertia + + # Custom layout for the dashboard + attr_accessor :layout + + # Returns whether multi-tenant mode is enabled. + # + # @return [Boolean] + def multi_tenant? + @multi_tenant == true + end + + # Returns the trace model class to use. + # + # @return [Class] The trace model class + def trace_model + if trace_model_class + trace_model_class.constantize + else + ActiveAgent::TelemetryTrace + end + end + + # Configures the dashboard. + # + # @yield [config] Configuration block + def configure + yield self + end + + # Reset configuration to defaults + def reset! + @authentication_method = nil + @multi_tenant = false + @account_class = nil + @current_account_method = nil + @trace_model_class = nil + @use_inertia = false + @layout = nil + end + end + + # Set defaults + reset! + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb new file mode 100644 index 00000000..f6889140 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + module Api + # Telemetry ingestion endpoint. + # + # Receives traces from ActiveAgent::Telemetry::Reporter and stores them + # for analysis and visualization in the dashboard. + # + # Supports two modes: + # - Local mode: No authentication, synchronous processing + # - Multi-tenant mode: Bearer token auth, async processing via job + # + # @example Local mode request + # POST /active_agent/api/traces + # Content-Type: application/json + # + # { + # "traces": [...], + # "sdk": { "name": "activeagent", "version": "0.5.0" } + # } + # + # @example Multi-tenant mode request + # POST /active_agent/api/traces + # Authorization: Bearer + # Content-Type: application/json + # + # { + # "traces": [...], + # "sdk": { "name": "activeagent", "version": "0.5.0" } + # } + # + class TracesController < ActionController::API + before_action :authenticate_api_key!, if: -> { ActiveAgent::Dashboard.multi_tenant? } + + # POST /active_agent/api/traces + def create + traces = params[:traces] || [] + sdk_info = params[:sdk] || {} + + return head :accepted if traces.empty? + + if ActiveAgent::Dashboard.multi_tenant? + # Multi-tenant mode: process in background + ActiveAgent::ProcessTelemetryTracesJob.perform_later( + account_id: @account&.id, + traces: traces.as_json, + sdk_info: sdk_info.as_json, + received_at: Time.current.iso8601(6) + ) + else + # Local mode: process synchronously + process_traces_synchronously(traces, sdk_info) + end + + head :accepted + rescue ActionController::ParameterMissing => e + render json: { error: e.message }, status: :bad_request + rescue StandardError => e + Rails.logger.error("[ActiveAgent::Dashboard] Trace ingestion error: #{e.message}") + render json: { error: "Internal server error" }, status: :internal_server_error + end + + private + + # Authenticates the request using Bearer token from Authorization header. + # Only used in multi-tenant mode. + def authenticate_api_key! + token = extract_bearer_token + + if token.blank? + render json: { error: "Missing Authorization header" }, status: :unauthorized + return + end + + account_class = ActiveAgent::Dashboard.account_class.constantize + @account = account_class.find_by(telemetry_api_key: token) + + if @account.nil? + render json: { error: "Invalid API key" }, status: :unauthorized + return + end + + # Track usage for rate limiting (if the account responds to it) + @account.increment_telemetry_usage! if @account.respond_to?(:increment_telemetry_usage!) + end + + # Extracts Bearer token from Authorization header. + def extract_bearer_token + auth_header = request.headers["Authorization"] + return nil if auth_header.blank? + + match = auth_header.match(/^Bearer\s+(.+)$/i) + match[1] if match + end + + # Process traces synchronously for local development. + def process_traces_synchronously(traces, sdk_info) + model = ActiveAgent::Dashboard.trace_model + + traces.each do |trace| + # Skip if trace already exists (idempotency) + next if model.exists?(trace_id: trace["trace_id"]) + + model.create_from_payload(trace, sdk_info) + rescue StandardError => e + Rails.logger.error( + "[ActiveAgent::Dashboard] Failed to process trace #{trace['trace_id']}: " \ + "#{e.class} - #{e.message}" + ) + end + end + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb new file mode 100644 index 00000000..b46af241 --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Base controller for the ActiveAgent Dashboard. + # + # Handles authentication via configurable authentication_method. + class ApplicationController < ActionController::Base + protect_from_forgery with: :exception + + before_action :authenticate_dashboard! + + # Add the engine's view path + prepend_view_path ActiveAgent::Dashboard::Engine.dashboard_root.join("app", "views").to_s + + layout "active_agent/dashboard/application" + + private + + def authenticate_dashboard! + return if ActiveAgent::Dashboard.authentication_method.nil? + + result = ActiveAgent::Dashboard.authentication_method.call(self) + head :unauthorized unless result + rescue StandardError => e + Rails.logger.error("[ActiveAgent::Dashboard] Authentication error: #{e.message}") + head :unauthorized + end + end + end +end diff --git a/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/traces_controller.rb b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/traces_controller.rb new file mode 100644 index 00000000..f72cc9be --- /dev/null +++ b/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/traces_controller.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Controller for viewing telemetry traces. + # + # Provides: + # - List view with filtering and pagination + # - Detail view with span timeline + # - Metrics overview + # - Live updates via Turbo Streams + class TracesController < ApplicationController + def index + @traces = fetch_traces + @metrics = calculate_metrics + + respond_to do |format| + format.html + format.turbo_stream + end + end + + def show + @trace = ActiveAgent::TelemetryTrace.find(params[:id]) + end + + def metrics + @metrics = calculate_metrics + @agent_stats = agent_statistics + @time_series = time_series_data + + respond_to do |format| + format.html + format.turbo_stream + end + end + + private + + def fetch_traces + traces = ActiveAgent::TelemetryTrace.recent + + traces = traces.for_agent(params[:agent]) if params[:agent].present? + traces = traces.with_errors if params[:status] == "error" + traces = traces.for_service(params[:service]) if params[:service].present? + + if params[:start_date].present? && params[:end_date].present? + traces = traces.for_date_range( + Time.parse(params[:start_date]), + Time.parse(params[:end_date]) + ) + end + + traces.limit(params[:limit] || 50) + end + + def calculate_metrics + traces = ActiveAgent::TelemetryTrace.where( + "created_at > ?", 24.hours.ago + ) + + { + total_traces: traces.count, + total_tokens: traces.sum(:total_input_tokens) + traces.sum(:total_output_tokens), + avg_duration_ms: traces.average(:total_duration_ms)&.round(2) || 0, + error_rate: calculate_error_rate(traces), + unique_agents: traces.distinct.count(:agent_class) + } + end + + def calculate_error_rate(traces) + total = traces.count + return 0.0 if total.zero? + + errors = traces.with_errors.count + ((errors.to_f / total) * 100).round(2) + end + + def agent_statistics + ActiveAgent::TelemetryTrace + .where("created_at > ?", 24.hours.ago) + .group(:agent_class) + .select( + "agent_class", + "COUNT(*) as trace_count", + "SUM(total_input_tokens + total_output_tokens) as total_tokens", + "AVG(total_duration_ms) as avg_duration", + "SUM(CASE WHEN status = 'ERROR' THEN 1 ELSE 0 END) as error_count" + ) + end + + def time_series_data + ActiveAgent::TelemetryTrace + .where("created_at > ?", 1.hour.ago) + .group_by_minute(:created_at) + .count + rescue NoMethodError + # Fallback if groupdate gem not available + {} + end + end + end +end diff --git a/lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb b/lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb new file mode 100644 index 00000000..0456e8dc --- /dev/null +++ b/lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module ActiveAgent + # Processes telemetry traces received from ActiveAgent clients. + # + # This job handles the asynchronous processing of trace data to avoid + # blocking the ingestion endpoint. It: + # - Creates TelemetryTrace records for each trace + # - Updates aggregate statistics + # - Handles any errors gracefully + # + # @example Local mode + # ActiveAgent::ProcessTelemetryTracesJob.perform_later( + # traces: [...], + # sdk_info: { name: "activeagent", version: "0.5.0" }, + # received_at: "2024-01-15T10:30:00Z" + # ) + # + # @example Multi-tenant mode + # ActiveAgent::ProcessTelemetryTracesJob.perform_later( + # account_id: 1, + # traces: [...], + # sdk_info: { name: "activeagent", version: "0.5.0" }, + # received_at: "2024-01-15T10:30:00Z" + # ) + # + class ProcessTelemetryTracesJob < ::ActiveJob::Base + queue_as :default + + # Maximum traces to process in a single job to avoid memory issues + MAX_TRACES_PER_JOB = 100 + + def perform(account_id: nil, traces:, sdk_info:, received_at:) + account = resolve_account(account_id) + + # In multi-tenant mode, require an account + if ActiveAgent::Dashboard.multi_tenant? && account.nil? + Rails.logger.warn("[ProcessTelemetryTracesJob] Skipping traces - no valid account") + return + end + + traces = traces.take(MAX_TRACES_PER_JOB) + + traces.each do |trace| + process_trace(trace, sdk_info, account) + rescue StandardError => e + Rails.logger.error( + "[ProcessTelemetryTracesJob] Failed to process trace #{trace['trace_id']}: " \ + "#{e.class} - #{e.message}" + ) + end + end + + private + + def resolve_account(account_id) + return nil unless ActiveAgent::Dashboard.multi_tenant? + return nil unless account_id + + account_class = ActiveAgent::Dashboard.account_class.constantize + account_class.find_by(id: account_id) + end + + def process_trace(trace, sdk_info, account) + model = ActiveAgent::Dashboard.trace_model + + # Build uniqueness scope + scope = model.where(trace_id: trace["trace_id"]) + scope = scope.where(account: account) if ActiveAgent::Dashboard.multi_tenant? && account + + # Skip if trace already exists (idempotency) + return if scope.exists? + + model.create_from_payload(trace, sdk_info, account: account) + end + end +end diff --git a/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb b/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb new file mode 100644 index 00000000..b178e724 --- /dev/null +++ b/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb @@ -0,0 +1,198 @@ +# frozen_string_literal: true + +module ActiveAgent + # Stores telemetry traces from ActiveAgent clients. + # + # Each trace represents a complete generation lifecycle, including prompt + # preparation, LLM calls, tool invocations, and error handling. + # + # This model supports two modes: + # - Local mode: No account association (single-tenant, self-hosted) + # - Multi-tenant mode: With account association (for activeagents.ai platform) + # + # @example Creating a trace from ingested data (local mode) + # ActiveAgent::TelemetryTrace.create_from_payload(trace_payload, sdk_info) + # + # @example Creating a trace with account (multi-tenant mode) + # ActiveAgent::TelemetryTrace.create_from_payload(trace_payload, sdk_info, account: account) + # + class TelemetryTrace < ::ActiveRecord::Base + self.table_name = "active_agent_telemetry_traces" + + # Optional account association for multi-tenant mode + # The host app can add: belongs_to :account if needed + if ActiveAgent::Dashboard.multi_tenant? + belongs_to :account, class_name: ActiveAgent::Dashboard.account_class + end + + # Status values for traces + STATUS_OK = "OK" + STATUS_ERROR = "ERROR" + STATUS_UNSET = "UNSET" + + validates :trace_id, presence: true + + # Scopes + scope :recent, -> { order(timestamp: :desc) } + scope :with_errors, -> { where(status: STATUS_ERROR) } + scope :for_service, ->(name) { where(service_name: name) } + scope :for_environment, ->(env) { where(environment: env) } + scope :for_agent, ->(agent_class) { where(agent_class: agent_class) } + scope :for_date_range, ->(start_date, end_date) { where(timestamp: start_date..end_date) } + scope :for_account, ->(account) { where(account: account) if ActiveAgent::Dashboard.multi_tenant? } + + # Creates a TelemetryTrace from an ingested trace payload. + # + # Extracts relevant data from the trace payload and stores it in a + # normalized format for querying and analysis. + # + # @param trace [Hash] The trace payload from ActiveAgent::Telemetry + # @param sdk_info [Hash] SDK metadata + # @param account [Object, nil] Optional account for multi-tenant mode + # @return [TelemetryTrace] The created trace + def self.create_from_payload(trace, sdk_info = {}, account: nil) + spans = trace["spans"] || [] + root_span = spans.find { |s| s["parent_span_id"].nil? } || spans.first || {} + + # Calculate totals from all spans + total_duration = root_span["duration_ms"] + total_input = 0 + total_output = 0 + total_thinking = 0 + + spans.each do |span| + tokens = span["tokens"] || {} + total_input += (tokens["input"] || 0) + total_output += (tokens["output"] || 0) + total_thinking += (tokens["thinking"] || 0) + end + + # Extract agent info from root span attributes + attributes = root_span["attributes"] || {} + agent_class = attributes["agent.class"] + agent_action = attributes["agent.action"] + + # Find any error message + error_span = spans.find { |s| s["status"] == STATUS_ERROR } + error_message = error_span&.dig("attributes", "error.message") + + attrs = { + trace_id: trace["trace_id"], + service_name: trace["service_name"], + environment: trace["environment"], + timestamp: Time.parse(trace["timestamp"]), + spans: spans, + resource_attributes: trace["resource_attributes"], + sdk_info: sdk_info, + total_duration_ms: total_duration, + total_input_tokens: total_input, + total_output_tokens: total_output, + total_thinking_tokens: total_thinking, + status: root_span["status"] || STATUS_UNSET, + agent_class: agent_class, + agent_action: agent_action, + error_message: error_message + } + + # Add account if in multi-tenant mode + attrs[:account] = account if ActiveAgent::Dashboard.multi_tenant? && account + + create!(attrs) + end + + # Returns the root span of this trace. + # + # @return [Hash, nil] The root span or nil + def root_span + spans&.find { |s| s["parent_span_id"].nil? } + end + + # Returns all LLM spans in this trace. + # + # @return [Array] LLM spans + def llm_spans + spans&.select { |s| s["type"] == "llm" } || [] + end + + # Returns all tool call spans in this trace. + # + # @return [Array] Tool spans + def tool_spans + spans&.select { |s| s["type"] == "tool" } || [] + end + + # Returns total token count. + # + # @return [Integer] Total tokens used + def total_tokens + (total_input_tokens || 0) + (total_output_tokens || 0) + (total_thinking_tokens || 0) + end + + # Returns whether this trace had an error. + # + # @return [Boolean] + def error? + status == STATUS_ERROR + end + + # Returns display name for the trace. + # + # @return [String] Display name (e.g., "WeatherAgent.forecast") + def display_name + if agent_class && agent_action + "#{agent_class}.#{agent_action}" + elsif agent_class + agent_class + else + trace_id&.first(8) + end + end + + # Returns formatted duration. + # + # @return [String] Duration in ms or s + def formatted_duration + return "—" unless total_duration_ms + + if total_duration_ms >= 1000 + "#{(total_duration_ms / 1000.0).round(2)}s" + else + "#{total_duration_ms.round(0)}ms" + end + end + + # Returns formatted token count. + # + # @return [String] Token count with K suffix for large numbers + def formatted_tokens + count = total_tokens + return "0" if count.zero? + + if count >= 1000 + "#{(count / 1000.0).round(1)}K" + else + count.to_s + end + end + + # Returns the provider used (from LLM spans). + # + # @return [String, nil] Provider name + def provider + llm_span = llm_spans.first + return nil unless llm_span + + llm_span.dig("attributes", "llm.provider") + end + + # Returns the model used (from LLM spans). + # + # @return [String, nil] Model name + def model + llm_span = llm_spans.first + return nil unless llm_span + + llm_span.dig("attributes", "llm.model") + end + end +end diff --git a/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb b/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb new file mode 100644 index 00000000..68787a2b --- /dev/null +++ b/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb @@ -0,0 +1,105 @@ +
+ +
+
+ Trace ID +
<%= trace.trace_id %>
+
+
+ Service +
<%= trace.service_name || "—" %>
+
+
+ Provider +
<%= trace.provider || "—" %>
+
+
+ Model +
<%= trace.model || "—" %>
+
+
+ + <% if trace.error? && trace.error_message.present? %> +
+
+ + + +
+

Error

+

<%= trace.error_message %>

+
+
+
+ <% end %> + + +
+
+
+ Input: + <%= number_with_delimiter(trace.total_input_tokens || 0) %> +
+
+
+ Output: + <%= number_with_delimiter(trace.total_output_tokens || 0) %> +
+ <% if trace.total_thinking_tokens.to_i > 0 %> +
+
+ Thinking: + <%= number_with_delimiter(trace.total_thinking_tokens) %> +
+ <% end %> +
+ + + <% if trace.spans.present? %> +
+

Span Timeline

+
+ <% total_duration = trace.total_duration_ms || 1 %> + <% trace.spans.each_with_index do |span, index| %> + <% + start_pct = ((span["start_time_relative_ms"] || 0) / total_duration * 100).clamp(0, 100) + width_pct = ((span["duration_ms"] || 0) / total_duration * 100).clamp(0.5, 100 - start_pct) + span_type = span["type"] || "unknown" + colors = { + "root" => "bg-gray-400", + "prompt" => "bg-blue-400", + "llm" => "bg-indigo-500", + "generate" => "bg-indigo-400", + "tool" => "bg-amber-500", + "thinking" => "bg-purple-500", + "response" => "bg-green-400" + } + bg_color = colors[span_type] || "bg-gray-400" + %> +
+
"> + <%= span["name"]&.split(".")&.last || span_type %> +
+
+
" + style="left: <%= start_pct %>%; width: <%= width_pct %>%;" + title="<%= span["name"] %>: <%= span["duration_ms"]&.round(1) %>ms"> +
+
+
+ <%= span["duration_ms"] ? "#{span["duration_ms"].round(1)}ms" : "—" %> +
+
+ <% end %> +
+
+ <% end %> + + +
+ + View raw trace data + +
<%= JSON.pretty_generate(trace.as_json(except: [:id, :created_at, :updated_at])) %>
+
+
diff --git a/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb b/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb new file mode 100644 index 00000000..1b68ba4c --- /dev/null +++ b/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb @@ -0,0 +1,135 @@ +
+ +
+
+
Total Traces
+
+ <%= number_with_delimiter(@metrics[:total_traces]) %> +
+
Last 24h
+
+ +
+
Total Tokens
+
+ <%= number_to_human(@metrics[:total_tokens], precision: 2) %> +
+
Last 24h
+
+ +
+
Avg Duration
+
+ <%= @metrics[:avg_duration_ms] >= 1000 ? "#{(@metrics[:avg_duration_ms] / 1000.0).round(2)}s" : "#{@metrics[:avg_duration_ms].round(0)}ms" %> +
+
Last 24h
+
+ +
+
Error Rate
+
+ <%= @metrics[:error_rate] %>% +
+
Last 24h
+
+ +
+
Active Agents
+
+ <%= @metrics[:unique_agents] %> +
+
Last 24h
+
+
+ + +
+
+

Recent Traces

+
+ <%= link_to "All", traces_path, class: "px-3 py-1 text-sm rounded #{params[:status].blank? ? 'bg-indigo-100 text-indigo-700' : 'text-gray-600 hover:bg-gray-100'}" %> + <%= link_to "Errors", traces_path(status: "error"), class: "px-3 py-1 text-sm rounded #{params[:status] == 'error' ? 'bg-red-100 text-red-700' : 'text-gray-600 hover:bg-gray-100'}" %> +
+
+ + <% if @traces.any? %> + + + + + + + + + + + + <% @traces.each do |trace| %> + + + + + + + + + + + + + + <% end %> + +
AgentStatusDurationTokensTime
+
+ + <%= trace.agent_class || "Unknown" %> + + <% if trace.agent_action %> + + .<%= trace.agent_action %> + + <% end %> +
+
<%= trace.trace_id&.first(8) %>
+
+ <% if trace.error? %> + + Error + + <% else %> + + OK + + <% end %> + + <%= trace.formatted_duration %> + +
+ <%= trace.formatted_tokens %> + <% if trace.total_thinking_tokens.to_i > 0 %> + + +<%= number_to_human(trace.total_thinking_tokens, precision: 1) %> thinking + + <% end %> +
+
+ <%= time_ago_in_words(trace.timestamp || trace.created_at) %> ago +
+ <% else %> +
+ + + +

No traces yet

+

+ Traces will appear here when your agents generate responses. +

+
+ <% end %> +
+
diff --git a/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb b/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb new file mode 100644 index 00000000..9b05bf0f --- /dev/null +++ b/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb @@ -0,0 +1,143 @@ +
+

Metrics Overview

+ + +
+
+
+
+ + + +
+
+

Total Traces

+

+ <%= number_with_delimiter(@metrics[:total_traces]) %> +

+
+
+
+ +
+
+
+ + + +
+
+

Total Tokens

+

+ <%= number_to_human(@metrics[:total_tokens], precision: 2) %> +

+
+
+
+ +
+
+
+ + + +
+
+

Avg Duration

+

+ <%= @metrics[:avg_duration_ms] >= 1000 ? "#{(@metrics[:avg_duration_ms] / 1000.0).round(2)}s" : "#{@metrics[:avg_duration_ms].round(0)}ms" %> +

+
+
+
+ +
+
+
+ + + +
+
+

Error Rate

+

+ <%= @metrics[:error_rate] %>% +

+
+
+
+ +
+
+
+ + + +
+
+

Active Agents

+

+ <%= @metrics[:unique_agents] %> +

+
+
+
+
+ + +
+
+

Agent Statistics

+

Performance breakdown by agent class (last 24h)

+
+ + <% if @agent_stats.any? %> + + + + + + + + + + + + <% @agent_stats.each do |stat| %> + + + + + + + + <% end %> + +
AgentTracesTokensAvg DurationErrors
+ + <%= stat.agent_class || "Unknown" %> + + + <%= number_with_delimiter(stat.trace_count) %> + + <%= number_to_human(stat.total_tokens || 0, precision: 1) %> + + <% avg = stat.avg_duration.to_f %> + <%= avg >= 1000 ? "#{(avg / 1000.0).round(2)}s" : "#{avg.round(0)}ms" %> + + <% error_count = stat.error_count.to_i %> + <% if error_count > 0 %> + + <%= error_count %> + + <% else %> + 0 + <% end %> +
+ <% else %> +
+ No agent data available yet. +
+ <% end %> +
+
diff --git a/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb b/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb new file mode 100644 index 00000000..434730a7 --- /dev/null +++ b/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb @@ -0,0 +1,36 @@ +
+ <%= link_to "← Back to traces", traces_path, class: "text-indigo-600 hover:text-indigo-800 text-sm" %> +
+ +
+
+
+
+

+ <%= @trace.display_name %> +

+

+ <%= @trace.trace_id %> +

+
+
+ <% if @trace.error? %> + + Error + + <% else %> + + Success + + <% end %> + + <%= @trace.timestamp&.strftime("%Y-%m-%d %H:%M:%S") %> + +
+
+
+ +
+ <%= render partial: "trace_detail", locals: { trace: @trace } %> +
+
diff --git a/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb b/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb new file mode 100644 index 00000000..5c15d9e1 --- /dev/null +++ b/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb @@ -0,0 +1,94 @@ + + + + + + ActiveAgent Dashboard + + + + + + + +
+ + + + +
+
+ <%= yield %> +
+
+
+ + diff --git a/lib/active_agent/dashboard/config/routes.rb b/lib/active_agent/dashboard/config/routes.rb new file mode 100644 index 00000000..c070b50a --- /dev/null +++ b/lib/active_agent/dashboard/config/routes.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +ActiveAgent::Dashboard::Engine.routes.draw do + # Dashboard routes + root to: "traces#index" + + resources :traces, only: [:index, :show] do + collection do + get :metrics + end + end + + # API routes for local telemetry ingestion + namespace :api do + resources :traces, only: [:create] + end +end diff --git a/lib/active_agent/dashboard/engine.rb b/lib/active_agent/dashboard/engine.rb new file mode 100644 index 00000000..f7abda26 --- /dev/null +++ b/lib/active_agent/dashboard/engine.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module ActiveAgent + module Dashboard + # Rails Engine for the ActiveAgent telemetry dashboard. + # + # Mount in your routes: + # mount ActiveAgent::Dashboard::Engine => "/active_agent" + # + class Engine < ::Rails::Engine + isolate_namespace ActiveAgent::Dashboard + + # Use a unique engine name to avoid conflicts + engine_name "active_agent_dashboard" + + # Engine root for locating assets + def self.dashboard_root + @dashboard_root ||= Pathname.new(File.expand_path("..", __FILE__)) + end + + # Draw the engine routes + def self.draw_routes + routes.draw do + root to: "traces#index" + + resources :traces, only: [:index, :show] do + collection do + get :metrics + end + end + + namespace :api do + resources :traces, only: [:create] + end + end + end + + # Require engine controllers, models, and jobs + def self.load_engine_files + dashboard_root = self.dashboard_root + + # Load models + require dashboard_root.join("app", "models", "active_agent", "telemetry_trace").to_s + + # Load jobs + require dashboard_root.join("app", "jobs", "active_agent", "process_telemetry_traces_job").to_s + + # Load controllers + require dashboard_root.join("app", "controllers", "active_agent", "dashboard", "application_controller").to_s + require dashboard_root.join("app", "controllers", "active_agent", "dashboard", "traces_controller").to_s + require dashboard_root.join("app", "controllers", "active_agent", "dashboard", "api", "traces_controller").to_s + end + + config.active_agent_dashboard = ActiveSupport::OrderedOptions.new + + initializer "active_agent.dashboard.append_view_paths" do |app| + dashboard_root = ActiveAgent::Dashboard::Engine.dashboard_root + ActionController::Base.prepend_view_path(dashboard_root.join("app", "views").to_s) + end + + # Use to_prepare to load files before each request in development + # and once in production + config.to_prepare do + ActiveAgent::Dashboard::Engine.load_engine_files + end + end + + # Draw routes when the engine is loaded + Engine.draw_routes + end +end diff --git a/lib/active_agent/railtie.rb b/lib/active_agent/railtie.rb index 11392c9b..baba8f9e 100644 --- a/lib/active_agent/railtie.rb +++ b/lib/active_agent/railtie.rb @@ -85,7 +85,8 @@ class Railtie < Rails::Railtie # :nodoc: self.generation_job = generation_job.constantize end - options.each { |k, v| send(:"#{k}=", v) } + # Skip telemetry config - it's handled separately above + options.except(:telemetry).each { |k, v| send(:"#{k}=", v) } end ActiveSupport.on_load(:action_dispatch_integration_test) do diff --git a/lib/active_agent/telemetry/configuration.rb b/lib/active_agent/telemetry/configuration.rb index 611924b8..89e7b032 100644 --- a/lib/active_agent/telemetry/configuration.rb +++ b/lib/active_agent/telemetry/configuration.rb @@ -64,9 +64,15 @@ class Configuration # @return [Logger] Logger for telemetry operations attr_accessor :logger + # @return [Boolean] Whether to store traces locally in the app's database + attr_accessor :local_storage + # Default ActiveAgents.ai endpoint for hosted observability. DEFAULT_ENDPOINT = "https://api.activeagents.ai/v1/traces" + # Local dashboard endpoint path (relative to app root) + LOCAL_ENDPOINT_PATH = "/active_agent/api/traces" + def initialize @enabled = false @endpoint = DEFAULT_ENDPOINT @@ -81,6 +87,7 @@ def initialize @environment = Rails.env if defined?(Rails) @resource_attributes = {} @logger = nil + @local_storage = false end # Returns whether telemetry collection is enabled. @@ -92,11 +99,31 @@ def enabled? # Returns whether telemetry is properly configured. # - # Checks that endpoint and api_key are present. + # Checks that endpoint and api_key are present, or local_storage is enabled. # # @return [Boolean] def configured? - endpoint.present? && api_key.present? + local_storage? || (endpoint.present? && api_key.present?) + end + + # Returns whether local storage mode is enabled. + # + # @return [Boolean] + def local_storage? + @local_storage == true + end + + # Returns the resolved endpoint for trace reporting. + # + # Uses local endpoint when local_storage is enabled. + # + # @return [String] + def resolved_endpoint + if local_storage? + LOCAL_ENDPOINT_PATH + else + endpoint + end end # Returns whether a trace should be sampled. @@ -148,6 +175,7 @@ def load_from_hash(hash) @service_name = hash[:service_name] if hash.key?(:service_name) @environment = hash[:environment] if hash.key?(:environment) @resource_attributes = hash[:resource_attributes] if hash.key?(:resource_attributes) + @local_storage = hash[:local_storage] if hash.key?(:local_storage) self end @@ -166,7 +194,8 @@ def to_h timeout: timeout, capture_bodies: capture_bodies, service_name: resolved_service_name, - environment: environment + environment: environment, + local_storage: local_storage } end diff --git a/lib/active_agent/telemetry/instrumentation.rb b/lib/active_agent/telemetry/instrumentation.rb index d5117fdb..f9ddb53a 100644 --- a/lib/active_agent/telemetry/instrumentation.rb +++ b/lib/active_agent/telemetry/instrumentation.rb @@ -66,17 +66,22 @@ def process_prompt result = super # Record token usage from response - if result.respond_to?(:usage) + if result.respond_to?(:usage) && result.usage.present? usage = result.usage + # Usage model uses methods, not hash access + input_tokens = (usage.input_tokens rescue 0) || 0 + output_tokens = (usage.output_tokens rescue 0) || 0 + reasoning_tokens = (usage.reasoning_tokens rescue 0) || 0 + llm_span.set_tokens( - input: usage[:input_tokens] || usage["input_tokens"] || 0, - output: usage[:output_tokens] || usage["output_tokens"] || 0, - thinking: usage[:thinking_tokens] || usage["thinking_tokens"] || 0 + input: input_tokens.to_i, + output: output_tokens.to_i, + thinking: reasoning_tokens.to_i ) span.set_tokens( - input: usage[:input_tokens] || usage["input_tokens"] || 0, - output: usage[:output_tokens] || usage["output_tokens"] || 0, - thinking: usage[:thinking_tokens] || usage["thinking_tokens"] || 0 + input: input_tokens.to_i, + output: output_tokens.to_i, + thinking: reasoning_tokens.to_i ) end @@ -116,9 +121,10 @@ def process_embed begin result = super - if result.respond_to?(:usage) + if result.respond_to?(:usage) && result.usage.present? usage = result.usage - span.set_tokens(input: usage[:input_tokens] || usage["input_tokens"] || 0) + input_tokens = (usage.input_tokens rescue 0) || 0 + span.set_tokens(input: input_tokens.to_i) end span.set_status(:ok) diff --git a/lib/active_agent/telemetry/reporter.rb b/lib/active_agent/telemetry/reporter.rb index 7e691216..11679f84 100644 --- a/lib/active_agent/telemetry/reporter.rb +++ b/lib/active_agent/telemetry/reporter.rb @@ -101,6 +101,12 @@ def flush_buffer # # @param traces [Array] Traces to send def send_traces(traces) + # Use direct database storage for local mode + if configuration.local_storage? + store_traces_locally(traces) + return + end + uri = URI.parse(configuration.endpoint) http = Net::HTTP.new(uri.host, uri.port) @@ -136,6 +142,29 @@ def send_traces(traces) log_error("Error sending traces: #{e.class} - #{e.message}") end + # Stores traces directly in the local database. + # + # @param traces [Array] Traces to store + def store_traces_locally(traces) + sdk_info = { + name: "activeagent", + version: ActiveAgent::VERSION, + language: "ruby", + runtime_version: RUBY_VERSION + } + + traces.each do |trace| + # Skip if trace already exists (idempotency) + next if ActiveAgent::TelemetryTrace.exists?(trace_id: trace["trace_id"]) + + ActiveAgent::TelemetryTrace.create_from_payload(trace, sdk_info) + rescue StandardError => e + log_error("Failed to store trace locally: #{e.class} - #{e.message}") + end + rescue StandardError => e + log_error("Error storing traces locally: #{e.class} - #{e.message}") + end + # Logs an error message. # # @param message [String] Error message diff --git a/lib/generators/active_agent/dashboard/install_generator.rb b/lib/generators/active_agent/dashboard/install_generator.rb new file mode 100644 index 00000000..97dd8bbf --- /dev/null +++ b/lib/generators/active_agent/dashboard/install_generator.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require "rails/generators" +require "rails/generators/active_record" + +module ActiveAgent + module Dashboard + # Generator for installing the ActiveAgent Dashboard. + # + # @example Run the generator + # rails generate active_agent:dashboard:install + # + # This will: + # - Create the telemetry_traces table migration + # - Add mount directive to routes + # - Create initializer for dashboard configuration + # + class InstallGenerator < Rails::Generators::Base + include ActiveRecord::Generators::Migration + + source_root File.expand_path("templates", __dir__) + + desc "Installs the ActiveAgent Dashboard with telemetry storage" + + def create_migration + migration_template( + "create_active_agent_telemetry_traces.rb.erb", + "db/migrate/create_active_agent_telemetry_traces.rb" + ) + end + + def add_route + route 'mount ActiveAgent::Dashboard::Engine => "/active_agent"' + end + + def create_initializer + template( + "active_agent_dashboard.rb.erb", + "config/initializers/active_agent_dashboard.rb" + ) + end + + def show_readme + say "\n" + say "ActiveAgent Dashboard installed successfully!", :green + say "\n" + say "Next steps:" + say " 1. Run migrations: rails db:migrate" + say " 2. Configure telemetry in config/active_agent.yml:" + say " telemetry:" + say " enabled: true" + say " local_storage: true" + say " 3. Visit /active_agent to view the dashboard" + say "\n" + end + + private + + def migration_version + "[#{ActiveRecord::Migration.current_version}]" + end + end + end +end diff --git a/lib/generators/active_agent/dashboard/templates/active_agent_dashboard.rb.erb b/lib/generators/active_agent/dashboard/templates/active_agent_dashboard.rb.erb new file mode 100644 index 00000000..02a41897 --- /dev/null +++ b/lib/generators/active_agent/dashboard/templates/active_agent_dashboard.rb.erb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# ActiveAgent Dashboard Configuration +# +# This file configures the ActiveAgent telemetry dashboard. +# The dashboard is mounted at /active_agent by default. + +ActiveAgent::Dashboard.configure do |config| + # Authentication method - provide a lambda that receives the controller + # and performs authentication. Return false or raise to deny access. + # + # Examples: + # + # Basic auth: + # config.authentication_method = ->(controller) { + # controller.authenticate_or_request_with_http_basic do |username, password| + # username == "admin" && password == Rails.application.credentials.dashboard_password + # end + # } + # + # Devise: + # config.authentication_method = ->(controller) { + # controller.authenticate_admin! + # } + # + # No authentication (development only!): + # config.authentication_method = nil + # + config.authentication_method = nil +end diff --git a/lib/generators/active_agent/dashboard/templates/create_active_agent_telemetry_traces.rb.erb b/lib/generators/active_agent/dashboard/templates/create_active_agent_telemetry_traces.rb.erb new file mode 100644 index 00000000..803e4df7 --- /dev/null +++ b/lib/generators/active_agent/dashboard/templates/create_active_agent_telemetry_traces.rb.erb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class CreateActiveAgentTelemetryTraces < ActiveRecord::Migration<%= migration_version %> + def change + create_table :active_agent_telemetry_traces do |t| + t.string :trace_id, null: false, index: true + t.string :service_name + t.string :environment + t.datetime :timestamp, index: true + t.jsonb :spans, default: [] + t.jsonb :resource_attributes, default: {} + t.jsonb :sdk_info, default: {} + t.decimal :total_duration_ms, precision: 12, scale: 3 + t.integer :total_input_tokens, default: 0 + t.integer :total_output_tokens, default: 0 + t.integer :total_thinking_tokens, default: 0 + t.string :status + t.string :agent_class, index: true + t.string :agent_action + t.text :error_message + + t.timestamps + end + + add_index :active_agent_telemetry_traces, :status + add_index :active_agent_telemetry_traces, [:agent_class, :agent_action] + add_index :active_agent_telemetry_traces, [:service_name, :environment] + add_index :active_agent_telemetry_traces, :created_at + end +end From d79ded8080acbbcc7573ce3061dd1fd3afc23e7d Mon Sep 17 00:00:00 2001 From: Justin Bowen Date: Mon, 30 Mar 2026 09:31:28 -0700 Subject: [PATCH 3/3] fix: rename create_migration to avoid Rails conflict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The method name 'create_migration' conflicts with ActiveRecord::Generators::Migration module internals. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .tool-versions | 1 + docs/.vitepress/theme/style.css | 2 +- .../dashboard/install_generator.rb | 2 +- yarn.lock | 1842 +++++++++-------- 4 files changed, 949 insertions(+), 898 deletions(-) diff --git a/.tool-versions b/.tool-versions index 9e57e968..489e0527 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1,2 @@ nodejs 24.7.0 +ruby 3.4.8 diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css index 887caed4..364b8824 100644 --- a/docs/.vitepress/theme/style.css +++ b/docs/.vitepress/theme/style.css @@ -102,7 +102,7 @@ rgb(255, 100, 100) 20%, rgb(250, 52, 59) 40%, rgb(255, 100, 100) 60%, - rgb(250, 52, 59) 100% + rgb(250, 52, 59) 80% ); --vp-home-hero-image-background-image: radial-gradient( diff --git a/lib/generators/active_agent/dashboard/install_generator.rb b/lib/generators/active_agent/dashboard/install_generator.rb index 97dd8bbf..db34f809 100644 --- a/lib/generators/active_agent/dashboard/install_generator.rb +++ b/lib/generators/active_agent/dashboard/install_generator.rb @@ -22,7 +22,7 @@ class InstallGenerator < Rails::Generators::Base desc "Installs the ActiveAgent Dashboard with telemetry storage" - def create_migration + def copy_migrations migration_template( "create_active_agent_telemetry_traces.rb.erb", "db/migrate/create_active_agent_telemetry_traces.rb" diff --git a/yarn.lock b/yarn.lock index b7b33199..0e41ccc6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,628 +2,644 @@ # yarn lockfile v1 +"@algolia/abtesting@1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.2.0.tgz" + integrity sha512-Z6Liq7US5CpdHExZLfPMBPxQHHUObV587kGvCLniLr1UTx0fGFIeGNWd005WIqQXqEda9GyAi7T2e7DUupVv0g== + dependencies: + "@algolia/client-common" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + "@algolia/autocomplete-core@1.17.7": - "integrity" "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==" - "resolved" "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz" - "version" "1.17.7" + version "1.17.7" + resolved "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz" + integrity sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q== dependencies: "@algolia/autocomplete-plugin-algolia-insights" "1.17.7" "@algolia/autocomplete-shared" "1.17.7" "@algolia/autocomplete-plugin-algolia-insights@1.17.7": - "integrity" "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==" - "resolved" "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz" - "version" "1.17.7" + version "1.17.7" + resolved "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz" + integrity sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A== dependencies: "@algolia/autocomplete-shared" "1.17.7" "@algolia/autocomplete-preset-algolia@1.17.7": - "integrity" "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==" - "resolved" "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz" - "version" "1.17.7" + version "1.17.7" + resolved "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz" + integrity sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA== dependencies: "@algolia/autocomplete-shared" "1.17.7" "@algolia/autocomplete-shared@1.17.7": - "integrity" "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==" - "resolved" "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz" - "version" "1.17.7" - -"@algolia/client-abtesting@5.27.0": - "integrity" "sha512-SITU5umoknxETtw67TxJu9njyMkWiH8pM+Bvw4dzfuIrIAT6Y1rmwV4y0A0didWoT+6xVuammIykbtBMolBcmg==" - "resolved" "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - "@algolia/requester-browser-xhr" "5.27.0" - "@algolia/requester-fetch" "5.27.0" - "@algolia/requester-node-http" "5.27.0" - -"@algolia/client-analytics@5.27.0": - "integrity" "sha512-go1b9qIZK5vYEQ7jD2bsfhhhVsoh9cFxQ5xF8TzTsg2WOCZR3O92oXCkq15SOK0ngJfqDU6a/k0oZ4KuEnih1Q==" - "resolved" "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - "@algolia/requester-browser-xhr" "5.27.0" - "@algolia/requester-fetch" "5.27.0" - "@algolia/requester-node-http" "5.27.0" - -"@algolia/client-common@5.27.0": - "integrity" "sha512-tnFOzdNuMzsz93kOClj3fKfuYoF3oYaEB5bggULSj075GJ7HUNedBEm7a6ScrjtnOaOtipbnT7veUpHA4o4wEQ==" - "resolved" "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.27.0.tgz" - "version" "5.27.0" - -"@algolia/client-insights@5.27.0": - "integrity" "sha512-y1qgw39qZijjQBXrqZTiwK1cWgWGRiLpJNWBv9w36nVMKfl9kInrfsYmdBAfmlhVgF/+Woe0y1jQ7pa4HyShAw==" - "resolved" "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - "@algolia/requester-browser-xhr" "5.27.0" - "@algolia/requester-fetch" "5.27.0" - "@algolia/requester-node-http" "5.27.0" - -"@algolia/client-personalization@5.27.0": - "integrity" "sha512-XluG9qPZKEbiLoIfXTKbABsWDNOMPx0t6T2ImJTTeuX+U/zBdmfcqqgcgkqXp+vbXof/XX/4of9Eqo1JaqEmKw==" - "resolved" "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - "@algolia/requester-browser-xhr" "5.27.0" - "@algolia/requester-fetch" "5.27.0" - "@algolia/requester-node-http" "5.27.0" - -"@algolia/client-query-suggestions@5.27.0": - "integrity" "sha512-V8/To+SsAl2sdw2AAjeLJuCW1L+xpz+LAGerJK7HKqHzE5yQhWmIWZTzqYQcojkii4iBMYn0y3+uReWqT8XVSQ==" - "resolved" "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - "@algolia/requester-browser-xhr" "5.27.0" - "@algolia/requester-fetch" "5.27.0" - "@algolia/requester-node-http" "5.27.0" - -"@algolia/client-search@>= 4.9.1 < 6", "@algolia/client-search@5.27.0": - "integrity" "sha512-EJJ7WmvmUXZdchueKFCK8UZFyLqy4Hz64snNp0cTc7c0MKaSeDGYEDxVsIJKp15r7ORaoGxSyS4y6BGZMXYuCg==" - "resolved" "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - "@algolia/requester-browser-xhr" "5.27.0" - "@algolia/requester-fetch" "5.27.0" - "@algolia/requester-node-http" "5.27.0" - -"@algolia/ingestion@1.27.0": - "integrity" "sha512-xNCyWeqpmEo4EdmpG57Fs1fJIQcPwt5NnJ6MBdXnUdMVXF4f5PHgza+HQWQQcYpCsune96jfmR0v7us6gRIlCw==" - "resolved" "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.27.0.tgz" - "version" "1.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - "@algolia/requester-browser-xhr" "5.27.0" - "@algolia/requester-fetch" "5.27.0" - "@algolia/requester-node-http" "5.27.0" - -"@algolia/monitoring@1.27.0": - "integrity" "sha512-P0NDiEFyt9UYQLBI0IQocIT7xHpjMpoFN3UDeerbztlkH9HdqT0GGh1SHYmNWpbMWIGWhSJTtz6kSIWvFu4+pw==" - "resolved" "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.27.0.tgz" - "version" "1.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - "@algolia/requester-browser-xhr" "5.27.0" - "@algolia/requester-fetch" "5.27.0" - "@algolia/requester-node-http" "5.27.0" - -"@algolia/recommend@5.27.0": - "integrity" "sha512-cqfTMF1d1cc7hg0vITNAFxJZas7MJ4Obc36WwkKpY23NOtGb+4tH9X7UKlQa2PmTgbXIANoJ/DAQTeiVlD2I4Q==" - "resolved" "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - "@algolia/requester-browser-xhr" "5.27.0" - "@algolia/requester-fetch" "5.27.0" - "@algolia/requester-node-http" "5.27.0" - -"@algolia/requester-browser-xhr@5.27.0": - "integrity" "sha512-ErenYTcXl16wYXtf0pxLl9KLVxIztuehqXHfW9nNsD8mz9OX42HbXuPzT7y6JcPiWJpc/UU/LY5wBTB65vsEUg==" - "resolved" "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - -"@algolia/requester-fetch@5.27.0": - "integrity" "sha512-CNOvmXsVi+IvT7z1d+6X7FveVkgEQwTNgipjQCHTIbF9KSMfZR7tUsJC+NpELrm10ALdOMauah84ybs9rw1cKQ==" - "resolved" "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - -"@algolia/requester-node-http@5.27.0": - "integrity" "sha512-Nx9EdLYZDsaYFTthqmc0XcVvsx6jqeEX8fNiYOB5i2HboQwl8pJPj1jFhGqoGd0KG7KFR+sdPO5/e0EDDAru2Q==" - "resolved" "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-common" "5.27.0" - -"@antfu/install-pkg@^1.0.0": - "integrity" "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==" - "resolved" "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz" - "version" "1.1.0" - dependencies: - "package-manager-detector" "^1.3.0" - "tinyexec" "^1.0.1" - -"@antfu/utils@^8.1.0": - "integrity" "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==" - "resolved" "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz" - "version" "8.1.1" + version "1.17.7" + resolved "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz" + integrity sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg== + +"@algolia/client-abtesting@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.36.0.tgz" + integrity sha512-uGr57O1UqDDeZHYXr1VnUomtdgQMxb6fS8yC/LXCMOn5ucN4k6FlcCRqXQnUyiiFZNG/rVK3zpRiyomq4JWXdQ== + dependencies: + "@algolia/client-common" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + +"@algolia/client-analytics@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.36.0.tgz" + integrity sha512-/zrf0NMxcvBBQ4r9lIqM7rMt7oI7gY7bZ+bNcgpZAQMvzXbKJVla3MqKGuPC/bfOthKvAcAr0mCZ8/7GwBmkVw== + dependencies: + "@algolia/client-common" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + +"@algolia/client-common@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.36.0.tgz" + integrity sha512-fDsg9w6xXWQyNkm/VfiWF2D9wnpTPv0fRVei7lWtz7cXJewhOmP1kKE2GaDTI4QDxVxgDkoPJ1+3UVMIzTcjjQ== + +"@algolia/client-insights@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.36.0.tgz" + integrity sha512-x6ZICyIN3BZjja47lqlMLG+AZwfx9wrYWttd6Daxp+wX/fFGxha6gdqxeoi5J44BmFqK8CUU4u8vpwHqGOCl4g== + dependencies: + "@algolia/client-common" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + +"@algolia/client-personalization@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.36.0.tgz" + integrity sha512-gnH9VHrC+/9OuaumbgxNXzzEq1AY2j3tm00ymNXNz35T7RQ2AK/x4T5b2UnjOUJejuXaSJ88gFyPk3nM5OhJZQ== + dependencies: + "@algolia/client-common" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + +"@algolia/client-query-suggestions@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.36.0.tgz" + integrity sha512-GkWIS+cAMoxsNPHEp3j7iywO9JJMVHVCWHzPPHFXIe0iNIOfsnZy5MqC1T9sifjqoU9b0GGbzzdxB3TEdwfiFA== + dependencies: + "@algolia/client-common" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + +"@algolia/client-search@>= 4.9.1 < 6", "@algolia/client-search@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.36.0.tgz" + integrity sha512-MLx32nSeDSNxfx28IfvwfHEfeo3AYe9JgEj0rLeYtJGmt0W30K6tCNokxhWGUUKrggQTH6H1lnohWsoj2OC2bw== + dependencies: + "@algolia/client-common" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + +"@algolia/ingestion@1.36.0": + version "1.36.0" + resolved "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.36.0.tgz" + integrity sha512-6zmlPLCsyzShOsfs1G1uqxwLTojte3NLyukwyUmJFfa46DSq3wkIOE9hFtqAoV951dXp4sZd2KCFYJmgRjcYbA== + dependencies: + "@algolia/client-common" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + +"@algolia/monitoring@1.36.0": + version "1.36.0" + resolved "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.36.0.tgz" + integrity sha512-SjJeDqlzAKJiWhquqfDWLEu5X/PIM+5KvUH65c4LBvt8T+USOVJbijtzA9UHZ1eUIfFSDBmbzEH0YvlS6Di2mg== + dependencies: + "@algolia/client-common" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + +"@algolia/recommend@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.36.0.tgz" + integrity sha512-FalJm3h9fwoZZpkkMpA0r4Grcvjk32FzmC4CXvlpyF/gBvu6pXE01yygjJBU20zGVLGsXU+Ad8nYPf+oGD7Zkg== + dependencies: + "@algolia/client-common" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + +"@algolia/requester-browser-xhr@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.36.0.tgz" + integrity sha512-weE9SImWIDmQrfGLb1pSPEfP3mioKQ84GaQRpUmjFxlxG/4nW2bSsmkV+kNp1s+iomL2gnxFknSmcQuuAy+kPA== + dependencies: + "@algolia/client-common" "5.36.0" + +"@algolia/requester-fetch@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.36.0.tgz" + integrity sha512-zGPI2sgzvOwCHTVMmDvc301iirOKCtJ+Egh+HQB/+DG0zTGUT1DpdwQVT25A7Yin/twnO8CkFpI/S+74FVYNjg== + dependencies: + "@algolia/client-common" "5.36.0" + +"@algolia/requester-node-http@5.36.0": + version "5.36.0" + resolved "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.36.0.tgz" + integrity sha512-dNbBGE/O6VG/6vFhv3CFm5za4rubAVrhQf/ef0YWiDqPMmalPxGEzIijw4xV1mU1JmX2ffyp/x8Kdtz24sDkOQ== + dependencies: + "@algolia/client-common" "5.36.0" + +"@antfu/install-pkg@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz" + integrity sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ== + dependencies: + package-manager-detector "^1.3.0" + tinyexec "^1.0.1" + +"@antfu/utils@^9.2.0": + version "9.2.0" + resolved "https://registry.npmjs.org/@antfu/utils/-/utils-9.2.0.tgz" + integrity sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw== "@babel/helper-string-parser@^7.27.1": - "integrity" "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" - "resolved" "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" - "version" "7.27.1" + version "7.27.1" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== "@babel/helper-validator-identifier@^7.27.1": - "integrity" "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==" - "resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz" - "version" "7.27.1" + version "7.27.1" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== -"@babel/parser@^7.27.2": - "integrity" "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==" - "resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz" - "version" "7.27.5" +"@babel/parser@^7.28.3": + version "7.28.3" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz" + integrity sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA== dependencies: - "@babel/types" "^7.27.3" + "@babel/types" "^7.28.2" -"@babel/types@^7.27.3": - "integrity" "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==" - "resolved" "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz" - "version" "7.27.6" +"@babel/types@^7.28.2": + version "7.28.2" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz" + integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ== dependencies: "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" "@docsearch/css@3.8.2": - "integrity" "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==" - "resolved" "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz" - "version" "3.8.2" + version "3.8.2" + resolved "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz" + integrity sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ== "@docsearch/js@3.8.2": - "integrity" "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==" - "resolved" "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz" - "version" "3.8.2" + version "3.8.2" + resolved "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz" + integrity sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ== dependencies: "@docsearch/react" "3.8.2" - "preact" "^10.0.0" + preact "^10.0.0" "@docsearch/react@3.8.2": - "integrity" "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==" - "resolved" "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz" - "version" "3.8.2" + version "3.8.2" + resolved "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz" + integrity sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg== dependencies: "@algolia/autocomplete-core" "1.17.7" "@algolia/autocomplete-preset-algolia" "1.17.7" "@docsearch/css" "3.8.2" - "algoliasearch" "^5.14.2" + algoliasearch "^5.14.2" "@esbuild/darwin-arm64@0.21.5": - "integrity" "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==" - "resolved" "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz" - "version" "0.21.5" + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== "@iconify-json/logos@^1.2.4": - "integrity" "sha512-XC4If5D/hbaZvUkTV8iaZuGlQCyG6CNOlaAaJaGa13V5QMYwYjgtKk3vPP8wz3wtTVNVEVk3LRx1fOJz+YnSMw==" - "resolved" "https://registry.npmjs.org/@iconify-json/logos/-/logos-1.2.4.tgz" - "version" "1.2.4" + version "1.2.9" + resolved "https://registry.npmjs.org/@iconify-json/logos/-/logos-1.2.9.tgz" + integrity sha512-G6VCdFnwZcrT6Eveq3m43oJfLw/CX8plwFcE+2jgv3fiGB64pTmnU7Yd1MNZ/eA+/Re2iEDhuCfSNOWTHwwK8w== dependencies: "@iconify/types" "*" "@iconify-json/simple-icons@^1.2.21": - "integrity" "sha512-jZwTBznpYVDYKWyAuRpepPpCiHScVrX6f8WRX8ReX6pdii99LYVHwJywKcH2excWQrWmBomC9nkxGlEKzXZ/wQ==" - "resolved" "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.37.tgz" - "version" "1.2.37" + version "1.2.49" + resolved "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.49.tgz" + integrity sha512-nRLwrHzz+cTAQYBNQrcr4eWOmQIcHObTj/QSi7nj0SFwVh5MvBsgx8OhoDC/R8iGklNmMpmoE/NKU0cPXMlOZw== dependencies: "@iconify/types" "*" -"@iconify-json/vscode-icons@^1.2.18": - "integrity" "sha512-xuWqr/SrckUoFi6kpSH/NrNGK+CuZ8LNnBY8qkRdkQvHmhirXvwsLfTKHoFndTsOlxfsHahlOLVCCb523kdqMA==" - "resolved" "https://registry.npmjs.org/@iconify-json/vscode-icons/-/vscode-icons-1.2.20.tgz" - "version" "1.2.20" +"@iconify-json/vscode-icons@^1.2.29": + version "1.2.30" + resolved "https://registry.npmjs.org/@iconify-json/vscode-icons/-/vscode-icons-1.2.30.tgz" + integrity sha512-dlTOc8w4a8/QNumZzMve+APJa6xQVXPZwo8qBk/MaYfY42NPrQT83QXkbTWKDkuEu/xgHPXvKZZBL7Yy12vYQw== dependencies: "@iconify/types" "*" "@iconify/types@*", "@iconify/types@^2.0.0": - "integrity" "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" - "resolved" "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz" - "version" "2.0.0" + version "2.0.0" + resolved "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz" + integrity sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg== -"@iconify/utils@^2.3.0": - "integrity" "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==" - "resolved" "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz" - "version" "2.3.0" +"@iconify/utils@^3.0.0": + version "3.0.1" + resolved "https://registry.npmjs.org/@iconify/utils/-/utils-3.0.1.tgz" + integrity sha512-A78CUEnFGX8I/WlILxJCuIJXloL0j/OJ9PSchPAfCargEIKmUBWvvEMmKWB5oONwiUqlNt+5eRufdkLxeHIWYw== dependencies: - "@antfu/install-pkg" "^1.0.0" - "@antfu/utils" "^8.1.0" + "@antfu/install-pkg" "^1.1.0" + "@antfu/utils" "^9.2.0" "@iconify/types" "^2.0.0" - "debug" "^4.4.0" - "globals" "^15.14.0" - "kolorist" "^1.8.0" - "local-pkg" "^1.0.0" - "mlly" "^1.7.4" - -"@jridgewell/sourcemap-codec@^1.5.0": - "integrity" "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" - "resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" - "version" "1.5.0" - -"@rollup/rollup-darwin-arm64@4.42.0": - "integrity" "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==" - "resolved" "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz" - "version" "4.42.0" + debug "^4.4.1" + globals "^15.15.0" + kolorist "^1.8.0" + local-pkg "^1.1.1" + mlly "^1.7.4" + +"@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@rollup/rollup-darwin-arm64@4.49.0": + version "4.49.0" + resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.49.0.tgz" + integrity sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw== "@shikijs/core@^2.1.0", "@shikijs/core@2.5.0": - "integrity" "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==" - "resolved" "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz" - "version" "2.5.0" + version "2.5.0" + resolved "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz" + integrity sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg== dependencies: "@shikijs/engine-javascript" "2.5.0" "@shikijs/engine-oniguruma" "2.5.0" "@shikijs/types" "2.5.0" "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" - "hast-util-to-html" "^9.0.4" + hast-util-to-html "^9.0.4" "@shikijs/engine-javascript@2.5.0": - "integrity" "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==" - "resolved" "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz" - "version" "2.5.0" + version "2.5.0" + resolved "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz" + integrity sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w== dependencies: "@shikijs/types" "2.5.0" "@shikijs/vscode-textmate" "^10.0.2" - "oniguruma-to-es" "^3.1.0" + oniguruma-to-es "^3.1.0" "@shikijs/engine-oniguruma@2.5.0": - "integrity" "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==" - "resolved" "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz" - "version" "2.5.0" + version "2.5.0" + resolved "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz" + integrity sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw== dependencies: "@shikijs/types" "2.5.0" "@shikijs/vscode-textmate" "^10.0.2" "@shikijs/langs@2.5.0": - "integrity" "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==" - "resolved" "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz" - "version" "2.5.0" + version "2.5.0" + resolved "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz" + integrity sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w== dependencies: "@shikijs/types" "2.5.0" "@shikijs/themes@2.5.0": - "integrity" "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==" - "resolved" "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz" - "version" "2.5.0" + version "2.5.0" + resolved "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz" + integrity sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw== dependencies: "@shikijs/types" "2.5.0" "@shikijs/transformers@^2.1.0": - "integrity" "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==" - "resolved" "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz" - "version" "2.5.0" + version "2.5.0" + resolved "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz" + integrity sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg== dependencies: "@shikijs/core" "2.5.0" "@shikijs/types" "2.5.0" "@shikijs/types@^2.1.0", "@shikijs/types@2.5.0": - "integrity" "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==" - "resolved" "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz" - "version" "2.5.0" + version "2.5.0" + resolved "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz" + integrity sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw== dependencies: "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" "@shikijs/vscode-textmate@^10.0.2": - "integrity" "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==" - "resolved" "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz" - "version" "10.0.2" + version "10.0.2" + resolved "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz" + integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg== -"@types/estree@1.0.7": - "integrity" "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" - "resolved" "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz" - "version" "1.0.7" +"@types/estree@1.0.8": + version "1.0.8" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== "@types/hast@^3.0.0", "@types/hast@^3.0.4": - "integrity" "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==" - "resolved" "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz" - "version" "3.0.4" + version "3.0.4" + resolved "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== dependencies: "@types/unist" "*" "@types/linkify-it@^5": - "integrity" "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==" - "resolved" "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz" - "version" "5.0.0" + version "5.0.0" + resolved "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz" + integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== "@types/markdown-it@^14.1.2": - "integrity" "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==" - "resolved" "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz" - "version" "14.1.2" + version "14.1.2" + resolved "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz" + integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog== dependencies: "@types/linkify-it" "^5" "@types/mdurl" "^2" "@types/mdast@^4.0.0": - "integrity" "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==" - "resolved" "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz" - "version" "4.0.4" + version "4.0.4" + resolved "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz" + integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== dependencies: "@types/unist" "*" "@types/mdurl@^2": - "integrity" "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==" - "resolved" "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz" - "version" "2.0.0" + version "2.0.0" + resolved "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz" + integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== "@types/unist@*", "@types/unist@^3.0.0": - "integrity" "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - "resolved" "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz" - "version" "3.0.3" + version "3.0.3" + resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== "@types/web-bluetooth@^0.0.21": - "integrity" "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==" - "resolved" "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz" - "version" "0.0.21" + version "0.0.21" + resolved "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz" + integrity sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA== "@ungap/structured-clone@^1.0.0": - "integrity" "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" - "resolved" "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz" - "version" "1.3.0" + version "1.3.0" + resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== "@vitejs/plugin-vue@^5.2.1": - "integrity" "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==" - "resolved" "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz" - "version" "5.2.4" - -"@vue/compiler-core@3.5.16": - "integrity" "sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==" - "resolved" "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.16.tgz" - "version" "3.5.16" - dependencies: - "@babel/parser" "^7.27.2" - "@vue/shared" "3.5.16" - "entities" "^4.5.0" - "estree-walker" "^2.0.2" - "source-map-js" "^1.2.1" - -"@vue/compiler-dom@3.5.16": - "integrity" "sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==" - "resolved" "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.16.tgz" - "version" "3.5.16" - dependencies: - "@vue/compiler-core" "3.5.16" - "@vue/shared" "3.5.16" - -"@vue/compiler-sfc@3.5.16": - "integrity" "sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==" - "resolved" "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.16.tgz" - "version" "3.5.16" - dependencies: - "@babel/parser" "^7.27.2" - "@vue/compiler-core" "3.5.16" - "@vue/compiler-dom" "3.5.16" - "@vue/compiler-ssr" "3.5.16" - "@vue/shared" "3.5.16" - "estree-walker" "^2.0.2" - "magic-string" "^0.30.17" - "postcss" "^8.5.3" - "source-map-js" "^1.2.1" - -"@vue/compiler-ssr@3.5.16": - "integrity" "sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==" - "resolved" "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.16.tgz" - "version" "3.5.16" - dependencies: - "@vue/compiler-dom" "3.5.16" - "@vue/shared" "3.5.16" + version "5.2.4" + resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz" + integrity sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA== + +"@vue/compiler-core@3.5.20": + version "3.5.20" + resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.20.tgz" + integrity sha512-8TWXUyiqFd3GmP4JTX9hbiTFRwYHgVL/vr3cqhr4YQ258+9FADwvj7golk2sWNGHR67QgmCZ8gz80nQcMokhwg== + dependencies: + "@babel/parser" "^7.28.3" + "@vue/shared" "3.5.20" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.2.1" + +"@vue/compiler-dom@3.5.20": + version "3.5.20" + resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.20.tgz" + integrity sha512-whB44M59XKjqUEYOMPYU0ijUV0G+4fdrHVKDe32abNdX/kJe1NUEMqsi4cwzXa9kyM9w5S8WqFsrfo1ogtBZGQ== + dependencies: + "@vue/compiler-core" "3.5.20" + "@vue/shared" "3.5.20" + +"@vue/compiler-sfc@3.5.20": + version "3.5.20" + resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.20.tgz" + integrity sha512-SFcxapQc0/feWiSBfkGsa1v4DOrnMAQSYuvDMpEaxbpH5dKbnEM5KobSNSgU+1MbHCl+9ftm7oQWxvwDB6iBfw== + dependencies: + "@babel/parser" "^7.28.3" + "@vue/compiler-core" "3.5.20" + "@vue/compiler-dom" "3.5.20" + "@vue/compiler-ssr" "3.5.20" + "@vue/shared" "3.5.20" + estree-walker "^2.0.2" + magic-string "^0.30.17" + postcss "^8.5.6" + source-map-js "^1.2.1" + +"@vue/compiler-ssr@3.5.20": + version "3.5.20" + resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.20.tgz" + integrity sha512-RSl5XAMc5YFUXpDQi+UQDdVjH9FnEpLDHIALg5J0ITHxkEzJ8uQLlo7CIbjPYqmZtt6w0TsIPbo1izYXwDG7JA== + dependencies: + "@vue/compiler-dom" "3.5.20" + "@vue/shared" "3.5.20" "@vue/devtools-api@^7.7.0": - "integrity" "sha512-b2Xx0KvXZObePpXPYHvBRRJLDQn5nhKjXh7vUhMEtWxz1AYNFOVIsh5+HLP8xDGL7sy+Q7hXeUxPHB/KgbtsPw==" - "resolved" "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.6.tgz" - "version" "7.7.6" - dependencies: - "@vue/devtools-kit" "^7.7.6" - -"@vue/devtools-kit@^7.7.6": - "integrity" "sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA==" - "resolved" "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.6.tgz" - "version" "7.7.6" - dependencies: - "@vue/devtools-shared" "^7.7.6" - "birpc" "^2.3.0" - "hookable" "^5.5.3" - "mitt" "^3.0.1" - "perfect-debounce" "^1.0.0" - "speakingurl" "^14.0.1" - "superjson" "^2.2.2" - -"@vue/devtools-shared@^7.7.6": - "integrity" "sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA==" - "resolved" "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.6.tgz" - "version" "7.7.6" - dependencies: - "rfdc" "^1.4.1" - -"@vue/reactivity@3.5.16": - "integrity" "sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==" - "resolved" "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.16.tgz" - "version" "3.5.16" - dependencies: - "@vue/shared" "3.5.16" - -"@vue/runtime-core@3.5.16": - "integrity" "sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==" - "resolved" "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.16.tgz" - "version" "3.5.16" - dependencies: - "@vue/reactivity" "3.5.16" - "@vue/shared" "3.5.16" - -"@vue/runtime-dom@3.5.16": - "integrity" "sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==" - "resolved" "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.16.tgz" - "version" "3.5.16" - dependencies: - "@vue/reactivity" "3.5.16" - "@vue/runtime-core" "3.5.16" - "@vue/shared" "3.5.16" - "csstype" "^3.1.3" - -"@vue/server-renderer@3.5.16": - "integrity" "sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==" - "resolved" "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.16.tgz" - "version" "3.5.16" - dependencies: - "@vue/compiler-ssr" "3.5.16" - "@vue/shared" "3.5.16" - -"@vue/shared@^3.5.13", "@vue/shared@3.5.16": - "integrity" "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==" - "resolved" "https://registry.npmjs.org/@vue/shared/-/shared-3.5.16.tgz" - "version" "3.5.16" + version "7.7.7" + resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz" + integrity sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg== + dependencies: + "@vue/devtools-kit" "^7.7.7" + +"@vue/devtools-kit@^7.7.7": + version "7.7.7" + resolved "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz" + integrity sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA== + dependencies: + "@vue/devtools-shared" "^7.7.7" + birpc "^2.3.0" + hookable "^5.5.3" + mitt "^3.0.1" + perfect-debounce "^1.0.0" + speakingurl "^14.0.1" + superjson "^2.2.2" + +"@vue/devtools-shared@^7.7.7": + version "7.7.7" + resolved "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz" + integrity sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw== + dependencies: + rfdc "^1.4.1" + +"@vue/reactivity@3.5.20": + version "3.5.20" + resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.20.tgz" + integrity sha512-hS8l8x4cl1fmZpSQX/NXlqWKARqEsNmfkwOIYqtR2F616NGfsLUm0G6FQBK6uDKUCVyi1YOL8Xmt/RkZcd/jYQ== + dependencies: + "@vue/shared" "3.5.20" + +"@vue/runtime-core@3.5.20": + version "3.5.20" + resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.20.tgz" + integrity sha512-vyQRiH5uSZlOa+4I/t4Qw/SsD/gbth0SW2J7oMeVlMFMAmsG1rwDD6ok0VMmjXY3eI0iHNSSOBilEDW98PLRKw== + dependencies: + "@vue/reactivity" "3.5.20" + "@vue/shared" "3.5.20" + +"@vue/runtime-dom@3.5.20": + version "3.5.20" + resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.20.tgz" + integrity sha512-KBHzPld/Djw3im0CQ7tGCpgRedryIn4CcAl047EhFTCCPT2xFf4e8j6WeKLgEEoqPSl9TYqShc3Q6tpWpz/Xgw== + dependencies: + "@vue/reactivity" "3.5.20" + "@vue/runtime-core" "3.5.20" + "@vue/shared" "3.5.20" + csstype "^3.1.3" + +"@vue/server-renderer@3.5.20": + version "3.5.20" + resolved "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.20.tgz" + integrity sha512-HthAS0lZJDH21HFJBVNTtx+ULcIbJQRpjSVomVjfyPkFSpCwvsPTA+jIzOaUm3Hrqx36ozBHePztQFg6pj5aKg== + dependencies: + "@vue/compiler-ssr" "3.5.20" + "@vue/shared" "3.5.20" + +"@vue/shared@^3.5.13", "@vue/shared@3.5.20": + version "3.5.20" + resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.5.20.tgz" + integrity sha512-SoRGP596KU/ig6TfgkCMbXkr4YJ91n/QSdMuqeP5r3hVIYA3CPHUBCc7Skak0EAKV+5lL4KyIh61VA/pK1CIAA== "@vueuse/core@^12.4.0", "@vueuse/core@12.8.2": - "integrity" "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==" - "resolved" "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz" - "version" "12.8.2" + version "12.8.2" + resolved "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz" + integrity sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ== dependencies: "@types/web-bluetooth" "^0.0.21" "@vueuse/metadata" "12.8.2" "@vueuse/shared" "12.8.2" - "vue" "^3.5.13" + vue "^3.5.13" "@vueuse/integrations@^12.4.0": - "integrity" "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==" - "resolved" "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz" - "version" "12.8.2" + version "12.8.2" + resolved "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz" + integrity sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g== dependencies: "@vueuse/core" "12.8.2" "@vueuse/shared" "12.8.2" - "vue" "^3.5.13" + vue "^3.5.13" "@vueuse/metadata@12.8.2": - "integrity" "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==" - "resolved" "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz" - "version" "12.8.2" + version "12.8.2" + resolved "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz" + integrity sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A== "@vueuse/shared@12.8.2": - "integrity" "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==" - "resolved" "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz" - "version" "12.8.2" - dependencies: - "vue" "^3.5.13" - -"acorn@^8.14.0": - "integrity" "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==" - "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz" - "version" "8.14.1" - -"algoliasearch@^5.14.2", "algoliasearch@>= 4.9.1 < 6": - "integrity" "sha512-2PvAgvxxJzA3+dB+ERfS2JPdvUsxNf89Cc2GF5iCcFupTULOwmbfinvqrC4Qj9nHJJDNf494NqEN/1f9177ZTQ==" - "resolved" "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.27.0.tgz" - "version" "5.27.0" - dependencies: - "@algolia/client-abtesting" "5.27.0" - "@algolia/client-analytics" "5.27.0" - "@algolia/client-common" "5.27.0" - "@algolia/client-insights" "5.27.0" - "@algolia/client-personalization" "5.27.0" - "@algolia/client-query-suggestions" "5.27.0" - "@algolia/client-search" "5.27.0" - "@algolia/ingestion" "1.27.0" - "@algolia/monitoring" "1.27.0" - "@algolia/recommend" "5.27.0" - "@algolia/requester-browser-xhr" "5.27.0" - "@algolia/requester-fetch" "5.27.0" - "@algolia/requester-node-http" "5.27.0" - -"birpc@^2.3.0": - "integrity" "sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==" - "resolved" "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz" - "version" "2.3.0" - -"ccount@^2.0.0": - "integrity" "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==" - "resolved" "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz" - "version" "2.0.1" - -"character-entities-html4@^2.0.0": - "integrity" "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==" - "resolved" "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz" - "version" "2.1.0" - -"character-entities-legacy@^3.0.0": - "integrity" "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==" - "resolved" "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz" - "version" "3.0.0" - -"comma-separated-tokens@^2.0.0": - "integrity" "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" - "resolved" "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz" - "version" "2.0.3" - -"confbox@^0.1.8": - "integrity" "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==" - "resolved" "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz" - "version" "0.1.8" - -"confbox@^0.2.1": - "integrity" "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==" - "resolved" "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz" - "version" "0.2.2" - -"copy-anything@^3.0.2": - "integrity" "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==" - "resolved" "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz" - "version" "3.0.5" - dependencies: - "is-what" "^4.1.8" - -"csstype@^3.1.3": - "integrity" "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" - "resolved" "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" - "version" "3.1.3" - -"debug@^4.4.0": - "integrity" "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==" - "resolved" "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz" - "version" "4.4.0" - dependencies: - "ms" "^2.1.3" - -"dequal@^2.0.0": - "integrity" "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" - "resolved" "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" - "version" "2.0.3" - -"devlop@^1.0.0": - "integrity" "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==" - "resolved" "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz" - "version" "1.1.0" - dependencies: - "dequal" "^2.0.0" - -"emoji-regex-xs@^1.0.0": - "integrity" "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==" - "resolved" "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz" - "version" "1.0.0" - -"entities@^4.5.0": - "integrity" "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - "resolved" "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" - "version" "4.5.0" - -"esbuild@^0.21.3": - "integrity" "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==" - "resolved" "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz" - "version" "0.21.5" + version "12.8.2" + resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz" + integrity sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w== + dependencies: + vue "^3.5.13" + +acorn@^8.15.0: + version "8.15.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +algoliasearch@^5.14.2, "algoliasearch@>= 4.9.1 < 6": + version "5.36.0" + resolved "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.36.0.tgz" + integrity sha512-FpwQ+p4x4RIsWnPj2z9idOC70T90ga7Oeh8BURSFKpqp5lITRsgkIj/bwYj2bY5xbyD7uBuP9AZRnM5EV20WOw== + dependencies: + "@algolia/abtesting" "1.2.0" + "@algolia/client-abtesting" "5.36.0" + "@algolia/client-analytics" "5.36.0" + "@algolia/client-common" "5.36.0" + "@algolia/client-insights" "5.36.0" + "@algolia/client-personalization" "5.36.0" + "@algolia/client-query-suggestions" "5.36.0" + "@algolia/client-search" "5.36.0" + "@algolia/ingestion" "1.36.0" + "@algolia/monitoring" "1.36.0" + "@algolia/recommend" "5.36.0" + "@algolia/requester-browser-xhr" "5.36.0" + "@algolia/requester-fetch" "5.36.0" + "@algolia/requester-node-http" "5.36.0" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +birpc@^2.3.0: + version "2.5.0" + resolved "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz" + integrity sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ== + +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== + +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + +confbox@^0.1.8: + version "0.1.8" + resolved "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz" + integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== + +confbox@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz" + integrity sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ== + +copy-anything@^3.0.2: + version "3.0.5" + resolved "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz" + integrity sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w== + dependencies: + is-what "^4.1.8" + +csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +debug@^4.4.1: + version "4.4.1" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +devlop@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + +emoji-regex-xs@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz" + integrity sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg== + +entities@^4.4.0, entities@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +esbuild@^0.21.3: + version "0.21.5" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== optionalDependencies: "@esbuild/aix-ppc64" "0.21.5" "@esbuild/android-arm" "0.21.5" @@ -649,308 +665,337 @@ "@esbuild/win32-ia32" "0.21.5" "@esbuild/win32-x64" "0.21.5" -"estree-walker@^2.0.2": - "integrity" "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - "resolved" "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" - "version" "2.0.2" +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== -"exsolve@^1.0.1": - "integrity" "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==" - "resolved" "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz" - "version" "1.0.5" +exsolve@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz" + integrity sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw== -"focus-trap@^7", "focus-trap@^7.6.4": - "integrity" "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==" - "resolved" "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz" - "version" "7.6.5" +focus-trap@^7, focus-trap@^7.6.4: + version "7.6.5" + resolved "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz" + integrity sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg== dependencies: - "tabbable" "^6.2.0" + tabbable "^6.2.0" -"fsevents@~2.3.2", "fsevents@~2.3.3": - "integrity" "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==" - "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" - "version" "2.3.3" +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -"globals@^15.14.0": - "integrity" "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==" - "resolved" "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz" - "version" "15.15.0" +globals@^15.15.0: + version "15.15.0" + resolved "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz" + integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== -"hast-util-to-html@^9.0.4": - "integrity" "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==" - "resolved" "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz" - "version" "9.0.5" +hast-util-to-html@^9.0.4: + version "9.0.5" + resolved "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz" + integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw== dependencies: "@types/hast" "^3.0.0" "@types/unist" "^3.0.0" - "ccount" "^2.0.0" - "comma-separated-tokens" "^2.0.0" - "hast-util-whitespace" "^3.0.0" - "html-void-elements" "^3.0.0" - "mdast-util-to-hast" "^13.0.0" - "property-information" "^7.0.0" - "space-separated-tokens" "^2.0.0" - "stringify-entities" "^4.0.0" - "zwitch" "^2.0.4" - -"hast-util-whitespace@^3.0.0": - "integrity" "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==" - "resolved" "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz" - "version" "3.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-whitespace "^3.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + +hast-util-whitespace@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz" + integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== dependencies: "@types/hast" "^3.0.0" -"hookable@^5.5.3": - "integrity" "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==" - "resolved" "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz" - "version" "5.5.3" - -"html-void-elements@^3.0.0": - "integrity" "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==" - "resolved" "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz" - "version" "3.0.0" - -"is-what@^4.1.8": - "integrity" "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==" - "resolved" "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz" - "version" "4.1.16" - -"kolorist@^1.8.0": - "integrity" "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==" - "resolved" "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz" - "version" "1.8.0" - -"local-pkg@^1.0.0": - "integrity" "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==" - "resolved" "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz" - "version" "1.1.1" - dependencies: - "mlly" "^1.7.4" - "pkg-types" "^2.0.1" - "quansync" "^0.2.8" - -"magic-string@^0.30.17": - "integrity" "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==" - "resolved" "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz" - "version" "0.30.17" - dependencies: - "@jridgewell/sourcemap-codec" "^1.5.0" - -"mark.js@8.11.1": - "integrity" "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==" - "resolved" "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz" - "version" "8.11.1" - -"mdast-util-to-hast@^13.0.0": - "integrity" "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==" - "resolved" "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz" - "version" "13.2.0" +hookable@^5.5.3: + version "5.5.3" + resolved "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz" + integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ== + +html-void-elements@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz" + integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== + +is-what@^4.1.8: + version "4.1.16" + resolved "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz" + integrity sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A== + +kolorist@^1.8.0: + version "1.8.0" + resolved "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz" + integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== + +linkify-it@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz" + integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== + dependencies: + uc.micro "^2.0.0" + +local-pkg@^1.1.1: + version "1.1.2" + resolved "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz" + integrity sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A== + dependencies: + mlly "^1.7.4" + pkg-types "^2.3.0" + quansync "^0.2.11" + +magic-string@^0.30.17: + version "0.30.18" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz" + integrity sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + +mark.js@8.11.1: + version "8.11.1" + resolved "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz" + integrity sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ== + +markdown-it@>=14: + version "14.1.0" + resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz" + integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg== + dependencies: + argparse "^2.0.1" + entities "^4.4.0" + linkify-it "^5.0.0" + mdurl "^2.0.0" + punycode.js "^2.3.1" + uc.micro "^2.1.0" + +mdast-util-to-hast@^13.0.0: + version "13.2.0" + resolved "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz" + integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== dependencies: "@types/hast" "^3.0.0" "@types/mdast" "^4.0.0" "@ungap/structured-clone" "^1.0.0" - "devlop" "^1.0.0" - "micromark-util-sanitize-uri" "^2.0.0" - "trim-lines" "^3.0.0" - "unist-util-position" "^5.0.0" - "unist-util-visit" "^5.0.0" - "vfile" "^6.0.0" - -"micromark-util-character@^2.0.0": - "integrity" "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==" - "resolved" "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz" - "version" "2.1.1" - dependencies: - "micromark-util-symbol" "^2.0.0" - "micromark-util-types" "^2.0.0" - -"micromark-util-encode@^2.0.0": - "integrity" "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==" - "resolved" "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz" - "version" "2.0.1" - -"micromark-util-sanitize-uri@^2.0.0": - "integrity" "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==" - "resolved" "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz" - "version" "2.0.1" - dependencies: - "micromark-util-character" "^2.0.0" - "micromark-util-encode" "^2.0.0" - "micromark-util-symbol" "^2.0.0" - -"micromark-util-symbol@^2.0.0": - "integrity" "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==" - "resolved" "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz" - "version" "2.0.1" - -"micromark-util-types@^2.0.0": - "integrity" "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==" - "resolved" "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz" - "version" "2.0.2" - -"minisearch@^7.1.1": - "integrity" "sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==" - "resolved" "https://registry.npmjs.org/minisearch/-/minisearch-7.1.2.tgz" - "version" "7.1.2" - -"mitt@^3.0.1": - "integrity" "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" - "resolved" "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz" - "version" "3.0.1" - -"mlly@^1.7.4": - "integrity" "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==" - "resolved" "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz" - "version" "1.7.4" - dependencies: - "acorn" "^8.14.0" - "pathe" "^2.0.1" - "pkg-types" "^1.3.0" - "ufo" "^1.5.4" - -"ms@^2.1.3": - "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - "version" "2.1.3" - -"nanoid@^3.3.11": - "integrity" "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==" - "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" - "version" "3.3.11" - -"oniguruma-to-es@^3.1.0": - "integrity" "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==" - "resolved" "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz" - "version" "3.1.1" - dependencies: - "emoji-regex-xs" "^1.0.0" - "regex" "^6.0.1" - "regex-recursion" "^6.0.2" - -"package-manager-detector@^1.3.0": - "integrity" "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==" - "resolved" "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz" - "version" "1.3.0" - -"pathe@^2.0.1", "pathe@^2.0.3": - "integrity" "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" - "resolved" "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" - "version" "2.0.3" - -"perfect-debounce@^1.0.0": - "integrity" "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==" - "resolved" "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz" - "version" "1.0.0" - -"picocolors@^1.1.1": - "integrity" "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - "resolved" "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" - "version" "1.1.1" - -"pkg-types@^1.3.0": - "integrity" "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==" - "resolved" "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz" - "version" "1.3.1" - dependencies: - "confbox" "^0.1.8" - "mlly" "^1.7.4" - "pathe" "^2.0.1" - -"pkg-types@^2.0.1": - "integrity" "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==" - "resolved" "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz" - "version" "2.1.0" - dependencies: - "confbox" "^0.2.1" - "exsolve" "^1.0.1" - "pathe" "^2.0.3" - -"postcss@^8", "postcss@^8.4.43", "postcss@^8.5.3": - "integrity" "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==" - "resolved" "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz" - "version" "8.5.4" - dependencies: - "nanoid" "^3.3.11" - "picocolors" "^1.1.1" - "source-map-js" "^1.2.1" - -"preact@^10.0.0": - "integrity" "sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ==" - "resolved" "https://registry.npmjs.org/preact/-/preact-10.26.8.tgz" - "version" "10.26.8" - -"property-information@^7.0.0": - "integrity" "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==" - "resolved" "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz" - "version" "7.1.0" - -"quansync@^0.2.8": - "integrity" "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==" - "resolved" "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz" - "version" "0.2.10" - -"regex-recursion@^6.0.2": - "integrity" "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==" - "resolved" "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz" - "version" "6.0.2" - dependencies: - "regex-utilities" "^2.3.0" - -"regex-utilities@^2.3.0": - "integrity" "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==" - "resolved" "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz" - "version" "2.3.0" - -"regex@^6.0.1": - "integrity" "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==" - "resolved" "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz" - "version" "6.0.1" - dependencies: - "regex-utilities" "^2.3.0" - -"rfdc@^1.4.1": - "integrity" "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" - "resolved" "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz" - "version" "1.4.1" - -"rollup@^4.20.0": - "integrity" "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==" - "resolved" "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz" - "version" "4.42.0" - dependencies: - "@types/estree" "1.0.7" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + +mdurl@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz" + integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== + +micromark-util-character@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + +micromark-util-types@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + +minisearch@^7.1.1: + version "7.1.2" + resolved "https://registry.npmjs.org/minisearch/-/minisearch-7.1.2.tgz" + integrity sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA== + +mitt@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + +mlly@^1.7.4: + version "1.8.0" + resolved "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz" + integrity sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g== + dependencies: + acorn "^8.15.0" + pathe "^2.0.3" + pkg-types "^1.3.1" + ufo "^1.6.1" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +oniguruma-to-es@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz" + integrity sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ== + dependencies: + emoji-regex-xs "^1.0.0" + regex "^6.0.1" + regex-recursion "^6.0.2" + +package-manager-detector@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz" + integrity sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ== + +pathe@^2.0.1, pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +perfect-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz" + integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +pkg-types@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz" + integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== + dependencies: + confbox "^0.1.8" + mlly "^1.7.4" + pathe "^2.0.1" + +pkg-types@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz" + integrity sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig== + dependencies: + confbox "^0.2.2" + exsolve "^1.0.7" + pathe "^2.0.3" + +postcss@^8, postcss@^8.4.43, postcss@^8.5.6: + version "8.5.6" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +preact@^10.0.0: + version "10.27.1" + resolved "https://registry.npmjs.org/preact/-/preact-10.27.1.tgz" + integrity sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ== + +property-information@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz" + integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== + +punycode.js@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + +quansync@^0.2.11: + version "0.2.11" + resolved "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz" + integrity sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA== + +regex-recursion@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz" + integrity sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg== + dependencies: + regex-utilities "^2.3.0" + +regex-utilities@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz" + integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== + +regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz" + integrity sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA== + dependencies: + regex-utilities "^2.3.0" + +rfdc@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +rollup@^4.20.0: + version "4.49.0" + resolved "https://registry.npmjs.org/rollup/-/rollup-4.49.0.tgz" + integrity sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA== + dependencies: + "@types/estree" "1.0.8" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.42.0" - "@rollup/rollup-android-arm64" "4.42.0" - "@rollup/rollup-darwin-arm64" "4.42.0" - "@rollup/rollup-darwin-x64" "4.42.0" - "@rollup/rollup-freebsd-arm64" "4.42.0" - "@rollup/rollup-freebsd-x64" "4.42.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.42.0" - "@rollup/rollup-linux-arm-musleabihf" "4.42.0" - "@rollup/rollup-linux-arm64-gnu" "4.42.0" - "@rollup/rollup-linux-arm64-musl" "4.42.0" - "@rollup/rollup-linux-loongarch64-gnu" "4.42.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.42.0" - "@rollup/rollup-linux-riscv64-gnu" "4.42.0" - "@rollup/rollup-linux-riscv64-musl" "4.42.0" - "@rollup/rollup-linux-s390x-gnu" "4.42.0" - "@rollup/rollup-linux-x64-gnu" "4.42.0" - "@rollup/rollup-linux-x64-musl" "4.42.0" - "@rollup/rollup-win32-arm64-msvc" "4.42.0" - "@rollup/rollup-win32-ia32-msvc" "4.42.0" - "@rollup/rollup-win32-x64-msvc" "4.42.0" - "fsevents" "~2.3.2" + "@rollup/rollup-android-arm-eabi" "4.49.0" + "@rollup/rollup-android-arm64" "4.49.0" + "@rollup/rollup-darwin-arm64" "4.49.0" + "@rollup/rollup-darwin-x64" "4.49.0" + "@rollup/rollup-freebsd-arm64" "4.49.0" + "@rollup/rollup-freebsd-x64" "4.49.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.49.0" + "@rollup/rollup-linux-arm-musleabihf" "4.49.0" + "@rollup/rollup-linux-arm64-gnu" "4.49.0" + "@rollup/rollup-linux-arm64-musl" "4.49.0" + "@rollup/rollup-linux-loongarch64-gnu" "4.49.0" + "@rollup/rollup-linux-ppc64-gnu" "4.49.0" + "@rollup/rollup-linux-riscv64-gnu" "4.49.0" + "@rollup/rollup-linux-riscv64-musl" "4.49.0" + "@rollup/rollup-linux-s390x-gnu" "4.49.0" + "@rollup/rollup-linux-x64-gnu" "4.49.0" + "@rollup/rollup-linux-x64-musl" "4.49.0" + "@rollup/rollup-win32-arm64-msvc" "4.49.0" + "@rollup/rollup-win32-ia32-msvc" "4.49.0" + "@rollup/rollup-win32-x64-msvc" "4.49.0" + fsevents "~2.3.2" "search-insights@>= 1 < 3": - "integrity" "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==" - "resolved" "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz" - "version" "2.17.3" + version "2.17.3" + resolved "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz" + integrity sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ== -"shiki@^2.1.0": - "integrity" "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==" - "resolved" "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz" - "version" "2.5.0" +shiki@^2.1.0: + version "2.5.0" + resolved "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz" + integrity sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ== dependencies: "@shikijs/core" "2.5.0" "@shikijs/engine-javascript" "2.5.0" @@ -961,139 +1006,144 @@ "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" -"source-map-js@^1.2.1": - "integrity" "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" - "resolved" "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" - "version" "1.2.1" - -"space-separated-tokens@^2.0.0": - "integrity" "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==" - "resolved" "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz" - "version" "2.0.2" - -"speakingurl@^14.0.1": - "integrity" "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==" - "resolved" "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz" - "version" "14.0.1" - -"stringify-entities@^4.0.0": - "integrity" "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==" - "resolved" "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz" - "version" "4.0.4" - dependencies: - "character-entities-html4" "^2.0.0" - "character-entities-legacy" "^3.0.0" - -"superjson@^2.2.2": - "integrity" "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==" - "resolved" "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz" - "version" "2.2.2" - dependencies: - "copy-anything" "^3.0.2" - -"tabbable@^6.2.0": - "integrity" "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" - "resolved" "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz" - "version" "6.2.0" - -"tinyexec@^1.0.1": - "integrity" "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==" - "resolved" "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz" - "version" "1.0.1" - -"trim-lines@^3.0.0": - "integrity" "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==" - "resolved" "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz" - "version" "3.0.1" - -"ufo@^1.5.4": - "integrity" "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==" - "resolved" "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz" - "version" "1.6.1" - -"unist-util-is@^6.0.0": - "integrity" "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==" - "resolved" "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz" - "version" "6.0.0" +source-map-js@^1.2.1: + 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== + +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== + +speakingurl@^14.0.1: + version "14.0.1" + resolved "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz" + integrity sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ== + +stringify-entities@^4.0.0: + version "4.0.4" + resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz" + integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== + dependencies: + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" + +superjson@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz" + integrity sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q== + dependencies: + copy-anything "^3.0.2" + +tabbable@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz" + integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew== + +tinyexec@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz" + integrity sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw== + +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + +uc.micro@^2.0.0, uc.micro@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + +ufo@^1.6.1: + version "1.6.1" + resolved "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz" + integrity sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA== + +unist-util-is@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz" + integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== dependencies: "@types/unist" "^3.0.0" -"unist-util-position@^5.0.0": - "integrity" "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==" - "resolved" "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz" - "version" "5.0.0" +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== dependencies: "@types/unist" "^3.0.0" -"unist-util-stringify-position@^4.0.0": - "integrity" "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==" - "resolved" "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz" - "version" "4.0.0" +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== dependencies: "@types/unist" "^3.0.0" -"unist-util-visit-parents@^6.0.0": - "integrity" "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==" - "resolved" "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz" - "version" "6.0.1" +unist-util-visit-parents@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz" + integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== dependencies: "@types/unist" "^3.0.0" - "unist-util-is" "^6.0.0" + unist-util-is "^6.0.0" -"unist-util-visit@^5.0.0": - "integrity" "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==" - "resolved" "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz" - "version" "5.0.0" +unist-util-visit@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz" + integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== dependencies: "@types/unist" "^3.0.0" - "unist-util-is" "^6.0.0" - "unist-util-visit-parents" "^6.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" -"vfile-message@^4.0.0": - "integrity" "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==" - "resolved" "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz" - "version" "4.0.2" +vfile-message@^4.0.0: + version "4.0.3" + resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz" + integrity sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw== dependencies: "@types/unist" "^3.0.0" - "unist-util-stringify-position" "^4.0.0" + unist-util-stringify-position "^4.0.0" -"vfile@^6.0.0": - "integrity" "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==" - "resolved" "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz" - "version" "6.0.3" +vfile@^6.0.0: + version "6.0.3" + resolved "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== dependencies: "@types/unist" "^3.0.0" - "vfile-message" "^4.0.0" + vfile-message "^4.0.0" -"vite@^5.0.0 || ^6.0.0", "vite@^5.4.14": - "integrity" "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==" - "resolved" "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz" - "version" "5.4.19" +"vite@^5.0.0 || ^6.0.0", vite@^5.4.14, vite@>=3: + version "5.4.19" + resolved "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz" + integrity sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA== dependencies: - "esbuild" "^0.21.3" - "postcss" "^8.4.43" - "rollup" "^4.20.0" + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" optionalDependencies: - "fsevents" "~2.3.3" + fsevents "~2.3.3" -"vitepress-plugin-group-icons@^1.5.2": - "integrity" "sha512-zen07KxZ83y3eecou4EraaEgwIriwHaB5Q0cHAmS4yO1UZEQvbljTylHPqiJ7LNkV39U8VehfcyquAJXg/26LA==" - "resolved" "https://registry.npmjs.org/vitepress-plugin-group-icons/-/vitepress-plugin-group-icons-1.5.2.tgz" - "version" "1.5.2" +vitepress-plugin-group-icons@^1.5.2: + version "1.6.3" + resolved "https://registry.npmjs.org/vitepress-plugin-group-icons/-/vitepress-plugin-group-icons-1.6.3.tgz" + integrity sha512-bvPD4lhraLJw3rPtLhUIVsOvNfnHnF+F1LH7BKHekEzeZ4uqdTdqnwEyaT580AoKjjT6/F8En6hVJj7takPKDA== dependencies: "@iconify-json/logos" "^1.2.4" - "@iconify-json/vscode-icons" "^1.2.18" - "@iconify/utils" "^2.3.0" + "@iconify-json/vscode-icons" "^1.2.29" + "@iconify/utils" "^3.0.0" -"vitepress-plugin-tabs@^0.7.1": - "integrity" "sha512-jxJvsicxnMSIYX9b8mAFLD2nwyKUcMO10dEt4nDSbinZhM8cGvAmMFOHPdf6TBX6gYZRl+/++/iYHtoM14fERQ==" - "resolved" "https://registry.npmjs.org/vitepress-plugin-tabs/-/vitepress-plugin-tabs-0.7.1.tgz" - "version" "0.7.1" +vitepress-plugin-tabs@^0.7.1: + version "0.7.1" + resolved "https://registry.npmjs.org/vitepress-plugin-tabs/-/vitepress-plugin-tabs-0.7.1.tgz" + integrity sha512-jxJvsicxnMSIYX9b8mAFLD2nwyKUcMO10dEt4nDSbinZhM8cGvAmMFOHPdf6TBX6gYZRl+/++/iYHtoM14fERQ== -"vitepress@^1.0.0", "vitepress@^1.6.3": - "integrity" "sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==" - "resolved" "https://registry.npmjs.org/vitepress/-/vitepress-1.6.3.tgz" - "version" "1.6.3" +vitepress@^1.0.0, vitepress@^1.6.3: + version "1.6.4" + resolved "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz" + integrity sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg== dependencies: "@docsearch/css" "3.8.2" "@docsearch/js" "3.8.2" @@ -1107,25 +1157,25 @@ "@vue/shared" "^3.5.13" "@vueuse/core" "^12.4.0" "@vueuse/integrations" "^12.4.0" - "focus-trap" "^7.6.4" - "mark.js" "8.11.1" - "minisearch" "^7.1.1" - "shiki" "^2.1.0" - "vite" "^5.4.14" - "vue" "^3.5.13" - -"vue@^3.2.25", "vue@^3.5.0", "vue@^3.5.13", "vue@3.5.16": - "integrity" "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==" - "resolved" "https://registry.npmjs.org/vue/-/vue-3.5.16.tgz" - "version" "3.5.16" - dependencies: - "@vue/compiler-dom" "3.5.16" - "@vue/compiler-sfc" "3.5.16" - "@vue/runtime-dom" "3.5.16" - "@vue/server-renderer" "3.5.16" - "@vue/shared" "3.5.16" - -"zwitch@^2.0.4": - "integrity" "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" - "resolved" "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz" - "version" "2.0.4" + focus-trap "^7.6.4" + mark.js "8.11.1" + minisearch "^7.1.1" + shiki "^2.1.0" + vite "^5.4.14" + vue "^3.5.13" + +vue@^3.2.25, vue@^3.5.0, vue@^3.5.13, vue@3.5.20: + version "3.5.20" + resolved "https://registry.npmjs.org/vue/-/vue-3.5.20.tgz" + integrity sha512-2sBz0x/wis5TkF1XZ2vH25zWq3G1bFEPOfkBcx2ikowmphoQsPH6X0V3mmPCXA2K1N/XGTnifVyDQP4GfDDeQw== + dependencies: + "@vue/compiler-dom" "3.5.20" + "@vue/compiler-sfc" "3.5.20" + "@vue/runtime-dom" "3.5.20" + "@vue/server-renderer" "3.5.20" + "@vue/shared" "3.5.20" + +zwitch@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==