From 258778f24594927aab808716e31f2c9eee5cdc23 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 12 Jan 2026 20:48:53 -0800 Subject: [PATCH 1/9] Allow to specify multiple Prebid script handlers --- crates/common/src/integrations/prebid.rs | 430 +++++++++++++---------- docs/guide/api-reference.md | 12 +- docs/guide/configuration-reference.md | 15 +- docs/guide/configuration.md | 4 +- docs/guide/environment-variables.md | 15 +- docs/guide/integration-guide.md | 4 +- docs/guide/integration_guide.md | 9 +- docs/guide/integrations-overview.md | 6 +- trusted-server.toml | 3 +- 9 files changed, 280 insertions(+), 218 deletions(-) diff --git a/crates/common/src/integrations/prebid.rs b/crates/common/src/integrations/prebid.rs index 51b1b9f..b8dd1ce 100644 --- a/crates/common/src/integrations/prebid.rs +++ b/crates/common/src/integrations/prebid.rs @@ -41,12 +41,16 @@ pub struct PrebidIntegrationConfig { deserialize_with = "crate::settings::vec_from_seq_or_map" )] pub bidders: Vec, - #[serde(default = "default_auto_configure")] - pub auto_configure: bool, #[serde(default)] pub debug: bool, - #[serde(default)] - pub script_handler: Option, + /// Patterns to match Prebid script URLs for serving empty JS. + /// Supports suffix matching (e.g., "/prebid.min.js" matches any path ending with that) + /// and wildcard patterns (e.g., "/static/prebid/*" matches paths under that prefix). + #[serde( + default = "default_script_remove_patterns", + deserialize_with = "crate::settings::vec_from_seq_or_map" + )] + pub script_remove_patterns: Vec, } impl IntegrationConfig for PrebidIntegrationConfig { @@ -63,12 +67,29 @@ fn default_bidders() -> Vec { vec!["mocktioneer".to_string()] } -fn default_auto_configure() -> bool { +fn default_enabled() -> bool { true } -fn default_enabled() -> bool { - true +/// Default suffixes that identify Prebid scripts +const PREBID_SCRIPT_SUFFIXES: &[&str] = &[ + "/prebid.js", + "/prebid.min.js", + "/prebidjs.js", + "/prebidjs.min.js", +]; + +fn default_script_remove_patterns() -> Vec { + // Default patterns to intercept Prebid scripts and serve empty JS + // - Exact paths like "/prebid.min.js" match only that path + // - Wildcard paths like "/static/prebid/*" match anything under that prefix + // and are filtered by PREBID_SCRIPT_SUFFIXES in matches_script_pattern() + vec![ + "/prebid.js".to_string(), + "/prebid.min.js".to_string(), + "/prebidjs.js".to_string(), + "/prebidjs.min.js".to_string(), + ] } #[derive(Debug, Deserialize)] @@ -118,6 +139,72 @@ impl PrebidIntegration { Arc::new(Self { config }) } + fn matches_script_url(&self, attr_value: &str) -> bool { + let trimmed = attr_value.trim(); + let without_query = trimmed.split(['?', '#']).next().unwrap_or(trimmed); + + if self.matches_script_pattern(without_query) { + return true; + } + + if !without_query.starts_with('/') + && !without_query.starts_with("//") + && !without_query.contains("://") + { + let with_slash = format!("/{without_query}"); + if self.matches_script_pattern(&with_slash) { + return true; + } + } + + let parsed = if without_query.starts_with("//") { + Url::parse(&format!("https:{without_query}")) + } else { + Url::parse(without_query) + }; + + parsed + .ok() + .is_some_and(|url| self.matches_script_pattern(url.path())) + } + + fn matches_script_pattern(&self, path: &str) -> bool { + // Normalize path to lowercase for case-insensitive matching + let path_lower = path.to_ascii_lowercase(); + + // Check if path matches any configured pattern + for pattern in &self.config.script_remove_patterns { + let pattern_lower = pattern.to_ascii_lowercase(); + + // Check for wildcard patterns: /* or {*name} + if pattern_lower.ends_with("/*") || pattern_lower.contains("{*") { + // Extract prefix before the wildcard + let prefix = if pattern_lower.ends_with("/*") { + &pattern_lower[..pattern_lower.len() - 1] // Remove trailing * + } else { + // Find {* and extract prefix before it + pattern_lower.split("{*").next().unwrap_or("") + }; + + if path_lower.starts_with(prefix) { + // Check if it ends with a known Prebid script suffix + if PREBID_SCRIPT_SUFFIXES + .iter() + .any(|suffix| path_lower.ends_with(suffix)) + { + return true; + } + } + } else { + // Exact match or suffix match + if path_lower.ends_with(&pattern_lower) { + return true; + } + } + } + false + } + fn error(message: impl Into) -> TrustedServerError { TrustedServerError::Integration { integration: PREBID_INTEGRATION_ID.to_string(), @@ -274,10 +361,11 @@ impl IntegrationProxy for PrebidIntegration { IntegrationEndpoint::post(ROUTE_THIRD_PARTY_AD), ]; - if let Some(script_path) = &self.config.script_handler { - // We need to leak the string to get a 'static str for IntegrationEndpoint - // This is safe because the config lives for the lifetime of the application - let static_path: &'static str = Box::leak(script_path.clone().into_boxed_str()); + // Register routes for script removal patterns + // Patterns can be exact paths (e.g., "/prebid.min.js") or use matchit wildcards + // (e.g., "/static/prebid/{*rest}") + for pattern in &self.config.script_remove_patterns { + let static_path: &'static str = Box::leak(pattern.clone().into_boxed_str()); routes.push(IntegrationEndpoint::get(static_path)); } @@ -293,15 +381,14 @@ impl IntegrationProxy for PrebidIntegration { let method = req.get_method().clone(); match method { - Method::GET if self.config.script_handler.as_ref() == Some(&path) => { - self.handle_script_handler() - } Method::GET if path == ROUTE_FIRST_PARTY_AD => { self.handle_first_party_ad(settings, req).await } Method::POST if path == ROUTE_THIRD_PARTY_AD => { self.handle_third_party_ad(settings, req).await } + // Serve empty JS for matching script patterns + Method::GET if self.matches_script_pattern(&path) => self.handle_script_handler(), _ => Err(Report::new(Self::error(format!( "Unsupported Prebid route: {path}" )))), @@ -315,7 +402,7 @@ impl IntegrationAttributeRewriter for PrebidIntegration { } fn handles_attribute(&self, attribute: &str) -> bool { - self.config.auto_configure && matches!(attribute, "src" | "href") + matches!(attribute, "src" | "href") } fn rewrite( @@ -324,7 +411,7 @@ impl IntegrationAttributeRewriter for PrebidIntegration { attr_value: &str, _ctx: &IntegrationAttributeContext<'_>, ) -> AttributeRewriteAction { - if self.config.auto_configure && is_prebid_script_url(attr_value) { + if self.matches_script_url(attr_value) { AttributeRewriteAction::remove_element() } else { AttributeRewriteAction::keep() @@ -388,16 +475,6 @@ fn build_openrtb_from_ts( } } -fn is_prebid_script_url(url: &str) -> bool { - let lower = url.to_ascii_lowercase(); - let without_query = lower.split('?').next().unwrap_or(""); - let filename = without_query.rsplit('/').next().unwrap_or(""); - matches!( - filename, - "prebid.js" | "prebid.min.js" | "prebidjs.js" | "prebidjs.min.js" - ) -} - async fn pbs_auction_for_get( settings: &Settings, req: Request, @@ -702,14 +779,11 @@ fn get_request_scheme(req: &Request) -> String { #[cfg(test)] mod tests { use super::*; - use crate::html_processor::{create_html_processor, HtmlProcessorConfig}; - use crate::integrations::{AttributeRewriteAction, IntegrationRegistry}; + use crate::integrations::{AttributeRewriteAction, IntegrationAttributeContext}; use crate::settings::Settings; - use crate::streaming_processor::{Compression, PipelineConfig, StreamingPipeline}; use crate::test_support::tests::crate_test_settings_str; use fastly::http::Method; use serde_json::json; - use std::io::Cursor; fn make_settings() -> Settings { Settings::from_toml(&crate_test_settings_str()).expect("should parse settings") @@ -721,30 +795,14 @@ mod tests { server_url: "https://prebid.example".to_string(), timeout_ms: 1000, bidders: vec!["exampleBidder".to_string()], - auto_configure: true, debug: false, - script_handler: None, + script_remove_patterns: default_script_remove_patterns(), } } - fn config_from_settings( - settings: &Settings, - registry: &IntegrationRegistry, - ) -> HtmlProcessorConfig { - HtmlProcessorConfig::from_settings( - settings, - registry, - "origin.example.com", - "test.example.com", - "https", - ) - } - #[test] fn attribute_rewriter_removes_prebid_scripts() { - let integration = PrebidIntegration { - config: base_config(), - }; + let integration = PrebidIntegration::new(base_config()); let ctx = IntegrationAttributeContext { attribute_name: "src", request_host: "pub.example", @@ -753,17 +811,21 @@ mod tests { }; let rewritten = integration.rewrite("src", "https://cdn.prebid.org/prebid.min.js", &ctx); - assert!(matches!(rewritten, AttributeRewriteAction::RemoveElement)); + assert!( + matches!(rewritten, AttributeRewriteAction::RemoveElement), + "Prebid script tags should be removed" + ); let untouched = integration.rewrite("src", "https://cdn.example.com/app.js", &ctx); - assert!(matches!(untouched, AttributeRewriteAction::Keep)); + assert!( + matches!(untouched, AttributeRewriteAction::Keep), + "Non-Prebid scripts should remain" + ); } #[test] - fn attribute_rewriter_handles_query_strings_and_links() { - let integration = PrebidIntegration { - config: base_config(), - }; + fn attribute_rewriter_handles_query_strings() { + let integration = PrebidIntegration::new(base_config()); let ctx = IntegrationAttributeContext { attribute_name: "href", request_host: "pub.example", @@ -773,107 +835,125 @@ mod tests { let rewritten = integration.rewrite("href", "https://cdn.prebid.org/prebid.js?v=1.2.3", &ctx); - assert!(matches!(rewritten, AttributeRewriteAction::RemoveElement)); + assert!( + matches!(rewritten, AttributeRewriteAction::RemoveElement), + "Prebid links with query strings should be removed" + ); } #[test] - fn html_processor_keeps_prebid_scripts_when_auto_config_disabled() { - let html = r#" - - - "#; - - let mut settings = make_settings(); - settings - .integrations - .insert_config( - "prebid", - &json!({ - "enabled": true, - "server_url": "https://test-prebid.com/openrtb2/auction", - "timeout_ms": 1000, - "bidders": ["mocktioneer"], - "auto_configure": false, - "debug": false - }), - ) - .expect("should update prebid config"); - let registry = IntegrationRegistry::new(&settings); - let config = config_from_settings(&settings, ®istry); - let processor = create_html_processor(config); - let pipeline_config = PipelineConfig { - input_compression: Compression::None, - output_compression: Compression::None, - chunk_size: 8192, + fn attribute_rewriter_matches_wildcard_patterns() { + let mut config = base_config(); + config.script_remove_patterns = vec!["/static/prebid/*".to_string()]; + let integration = PrebidIntegration::new(config); + let ctx = IntegrationAttributeContext { + attribute_name: "src", + request_host: "pub.example", + request_scheme: "https", + origin_host: "origin.example", }; - let mut pipeline = StreamingPipeline::new(pipeline_config, processor); - let mut output = Vec::new(); - let result = pipeline.process(Cursor::new(html.as_bytes()), &mut output); - assert!(result.is_ok()); - let processed = String::from_utf8_lossy(&output); - assert!( - processed.contains("tsjs-unified"), - "Unified bundle should be injected" + let rewritten = integration.rewrite( + "src", + "https://cdn.example.com/static/prebid/v1/prebid.min.js", + &ctx, ); assert!( - processed.contains("prebid.min.js"), - "Prebid script should remain when auto-config is disabled" + matches!(rewritten, AttributeRewriteAction::RemoveElement), + "Wildcard patterns should match prebid assets on full URLs" ); + + let rewritten_relative = integration.rewrite("src", "static/prebid/prebid.min.js", &ctx); assert!( - processed.contains("cdn.prebid.org/prebid.js"), - "Prebid preload should remain when auto-config is disabled" + matches!(rewritten_relative, AttributeRewriteAction::RemoveElement), + "Wildcard patterns should match relative paths without a leading slash" ); } #[test] - fn html_processor_removes_prebid_scripts_when_auto_config_enabled() { - let html = r#" - - - "#; - - let mut settings = make_settings(); - settings - .integrations - .insert_config( - "prebid", - &json!({ - "enabled": true, - "server_url": "https://test-prebid.com/openrtb2/auction", - "timeout_ms": 1000, - "bidders": ["mocktioneer"], - "auto_configure": true, - "debug": false - }), - ) - .expect("should update prebid config"); - let registry = IntegrationRegistry::new(&settings); - let config = config_from_settings(&settings, ®istry); - let processor = create_html_processor(config); - let pipeline_config = PipelineConfig { - input_compression: Compression::None, - output_compression: Compression::None, - chunk_size: 8192, - }; - let mut pipeline = StreamingPipeline::new(pipeline_config, processor); + fn script_pattern_matching_exact_paths() { + let integration = PrebidIntegration::new(base_config()); + + // Should match default exact patterns (suffix matching) + assert!(integration.matches_script_pattern("/prebid.js")); + assert!(integration.matches_script_pattern("/prebid.min.js")); + assert!(integration.matches_script_pattern("/prebidjs.js")); + assert!(integration.matches_script_pattern("/prebidjs.min.js")); + + // Suffix matching means nested paths also match + assert!(integration.matches_script_pattern("/static/prebid.min.js")); + assert!(integration.matches_script_pattern("/static/prebid/v8.53.0/prebid.min.js")); + + // Should not match other scripts + assert!(!integration.matches_script_pattern("/app.js")); + assert!(!integration.matches_script_pattern("/static/bundle.min.js")); + } - let mut output = Vec::new(); - let result = pipeline.process(Cursor::new(html.as_bytes()), &mut output); - assert!(result.is_ok()); - let processed = String::from_utf8_lossy(&output); - assert!( - processed.contains("tsjs-unified"), - "Unified bundle should be injected" - ); - assert!( - !processed.contains("prebid.min.js"), - "Prebid script should be removed when auto-config is enabled" - ); + #[test] + fn script_pattern_matching_wildcard_slash_star() { + // Test /* wildcard pattern matching + let mut config = base_config(); + config.script_remove_patterns = vec!["/static/prebid/*".to_string()]; + let integration = PrebidIntegration::new(config); + + // Should match paths under the prefix with known suffixes + assert!(integration.matches_script_pattern("/static/prebid/prebid.min.js")); + assert!(integration.matches_script_pattern("/static/prebid/v8.53.0/prebid.min.js")); + assert!(integration.matches_script_pattern("/static/prebid/prebidjs.js")); + + // Should not match paths outside prefix + assert!(!integration.matches_script_pattern("/prebid.min.js")); + assert!(!integration.matches_script_pattern("/other/prebid.min.js")); + + // Should not match non-prebid scripts even under prefix + assert!(!integration.matches_script_pattern("/static/prebid/app.js")); + } + + #[test] + fn script_pattern_matching_wildcard_matchit_syntax() { + // Test {*rest} matchit-style wildcard pattern matching + let mut config = base_config(); + config.script_remove_patterns = vec!["/wp-content/plugins/prebidjs/{*rest}".to_string()]; + let integration = PrebidIntegration::new(config); + + // Should match paths under the prefix with known suffixes assert!( - !processed.contains("cdn.prebid.org/prebid.js"), - "Prebid preload should be removed when auto-config is enabled" + integration.matches_script_pattern("/wp-content/plugins/prebidjs/js/prebidjs.min.js") ); + assert!(integration.matches_script_pattern("/wp-content/plugins/prebidjs/prebid.min.js")); + assert!(integration.matches_script_pattern("/wp-content/plugins/prebidjs/v1/v2/prebid.js")); + + // Should not match paths outside prefix + assert!(!integration.matches_script_pattern("/prebid.min.js")); + assert!(!integration.matches_script_pattern("/wp-content/other/prebid.min.js")); + + // Should not match non-prebid scripts even under prefix + assert!(!integration.matches_script_pattern("/wp-content/plugins/prebidjs/app.js")); + } + + #[test] + fn script_pattern_matching_case_insensitive() { + let integration = PrebidIntegration::new(base_config()); + + assert!(integration.matches_script_pattern("/Prebid.JS")); + assert!(integration.matches_script_pattern("/PREBID.MIN.JS")); + assert!(integration.matches_script_pattern("/Static/Prebid.min.js")); + } + + #[test] + fn routes_include_script_patterns() { + let integration = PrebidIntegration::new(base_config()); + let routes = integration.routes(); + + // Should include the default ad routes + assert!(routes.iter().any(|r| r.path == "/first-party/ad")); + assert!(routes.iter().any(|r| r.path == "/third-party/ad")); + + // Should include default script removal patterns + assert!(routes.iter().any(|r| r.path == "/prebid.js")); + assert!(routes.iter().any(|r| r.path == "/prebid.min.js")); + assert!(routes.iter().any(|r| r.path == "/prebidjs.js")); + assert!(routes.iter().any(|r| r.path == "/prebidjs.min.js")); } #[test] @@ -983,16 +1063,7 @@ mod tests { } #[test] - fn is_prebid_script_url_matches_common_variants() { - assert!(is_prebid_script_url("https://cdn.com/prebid.js")); - assert!(is_prebid_script_url( - "https://cdn.com/prebid.min.js?version=1" - )); - assert!(!is_prebid_script_url("https://cdn.com/app.js")); - } - - #[test] - fn test_script_handler_config_parsing() { + fn test_script_remove_patterns_config_parsing() { let toml_str = r#" [publisher] domain = "test-publisher.com" @@ -1009,7 +1080,7 @@ template = "{{client_ip}}:{{user_agent}}" [integrations.prebid] enabled = true server_url = "https://prebid.example" -script_handler = "/prebid.js" +script_remove_patterns = ["/static/prebid/*"] "#; let settings = Settings::from_toml(toml_str).expect("should parse TOML"); @@ -1018,11 +1089,11 @@ script_handler = "/prebid.js" .expect("should get config") .expect("should be enabled"); - assert_eq!(config.script_handler, Some("/prebid.js".to_string())); + assert_eq!(config.script_remove_patterns, vec!["/static/prebid/*"]); } #[test] - fn test_script_handler_none_by_default() { + fn test_script_remove_patterns_default() { let toml_str = r#" [publisher] domain = "test-publisher.com" @@ -1047,21 +1118,16 @@ server_url = "https://prebid.example" .expect("should get config") .expect("should be enabled"); - assert_eq!(config.script_handler, None); + // Should have default patterns + assert_eq!( + config.script_remove_patterns, + default_script_remove_patterns() + ); } #[test] fn test_script_handler_returns_empty_js() { - let config = PrebidIntegrationConfig { - enabled: true, - server_url: "https://prebid.example".to_string(), - timeout_ms: 1000, - bidders: vec![], - auto_configure: false, - debug: false, - script_handler: Some("/prebid.js".to_string()), - }; - let integration = PrebidIntegration::new(config); + let integration = PrebidIntegration::new(base_config()); let response = integration .handle_script_handler() @@ -1085,37 +1151,23 @@ server_url = "https://prebid.example" } #[test] - fn test_routes_includes_script_handler() { - let config = PrebidIntegrationConfig { - enabled: true, - server_url: "https://prebid.example".to_string(), - timeout_ms: 1000, - bidders: vec![], - auto_configure: false, - debug: false, - script_handler: Some("/prebid.js".to_string()), - }; + fn test_routes_with_default_patterns() { + let config = base_config(); // Has default script_remove_patterns let integration = PrebidIntegration::new(config); let routes = integration.routes(); - // Should have 3 routes: first-party ad, third-party ad, and script handler - assert_eq!(routes.len(), 3); + // Should have 2 ad routes + 4 default script patterns + assert_eq!(routes.len(), 6); - let has_script_route = routes - .iter() - .any(|r| r.path == "/prebid.js" && r.method == Method::GET); - assert!(has_script_route, "should register script handler route"); - } - - #[test] - fn test_routes_without_script_handler() { - let config = base_config(); // Has script_handler: None - let integration = PrebidIntegration::new(config); - - let routes = integration.routes(); + // Verify ad routes + assert!(routes.iter().any(|r| r.path == "/first-party/ad")); + assert!(routes.iter().any(|r| r.path == "/third-party/ad")); - // Should only have 2 routes: first-party ad and third-party ad - assert_eq!(routes.len(), 2); + // Verify script pattern routes + assert!(routes.iter().any(|r| r.path == "/prebid.js")); + assert!(routes.iter().any(|r| r.path == "/prebid.min.js")); + assert!(routes.iter().any(|r| r.path == "/prebidjs.js")); + assert!(routes.iter().any(|r| r.path == "/prebidjs.min.js")); } } diff --git a/docs/guide/api-reference.md b/docs/guide/api-reference.md index b10d345..ca7a0a1 100644 --- a/docs/guide/api-reference.md +++ b/docs/guide/api-reference.md @@ -421,18 +421,22 @@ See [First-Party Endpoints](#get-first-party-ad) above. #### POST /third-party/ad See [First-Party Endpoints](#post-third-party-ad) above. -#### GET /prebid.js (Optional) -Returns empty JavaScript to override Prebid.js when `script_handler` is configured. +#### GET /prebid.js, /prebid.min.js, etc. (Script Override) +Returns empty JavaScript to override Prebid.js scripts when the Prebid integration is enabled. By default, exact requests to `/prebid.js`, `/prebid.min.js`, `/prebidjs.js`, or `/prebidjs.min.js` will be intercepted and served an empty script. **Configuration:** ```toml [integrations.prebid] -script_handler = "/prebid.js" +# Default patterns (exact paths) +script_remove_patterns = ["/prebid.js", "/prebid.min.js", "/prebidjs.js", "/prebidjs.min.js"] + +# Use wildcard patterns to match paths under a prefix +# script_remove_patterns = ["/static/prebid/*"] ``` **Response:** - **Content-Type:** `application/javascript; charset=utf-8` -- **Body:** `// Prebid.js override by Trusted Server` +- **Body:** `// Script overridden by Trusted Server` - **Cache:** `immutable, max-age=31536000` --- diff --git a/docs/guide/configuration-reference.md b/docs/guide/configuration-reference.md index c820ca7..4bef2bd 100644 --- a/docs/guide/configuration-reference.md +++ b/docs/guide/configuration-reference.md @@ -597,9 +597,15 @@ All integrations support: | `server_url` | String | Required | Prebid Server endpoint URL | | `timeout_ms` | Integer | `1000` | Request timeout in milliseconds | | `bidders` | Array[String] | `[]` | List of enabled bidders | -| `auto_configure` | Boolean | `false` | Auto-inject Prebid.js shim | | `debug` | Boolean | `false` | Enable debug logging | -| `script_handler` | String | Optional | Custom script endpoint path | +| `script_remove_patterns` | Array[String] | See below | Patterns for removing Prebid script tags and intercepting requests | + +**Default `script_remove_patterns`**: +```toml +["/prebid.js", "/prebid.min.js", "/prebidjs.js", "/prebidjs.min.js"] +``` + +These patterns use suffix matching when stripping HTML, so `/static/prebid/v8/prebid.min.js` matches because it ends with `/prebid.min.js`. For request interception, exact paths are registered unless you use wildcard patterns (e.g., `/static/prebid/*`), which match paths under that prefix. **Example**: ```toml @@ -608,8 +614,8 @@ enabled = true server_url = "https://prebid-server.example/openrtb2/auction" timeout_ms = 1200 bidders = ["kargo", "rubicon", "appnexus", "openx"] -auto_configure = true debug = false +# script_remove_patterns = ["/static/prebid/*"] # Optional: restrict to specific path ``` **Environment Override**: @@ -618,8 +624,9 @@ TRUSTED_SERVER__INTEGRATIONS__PREBID__ENABLED=true TRUSTED_SERVER__INTEGRATIONS__PREBID__SERVER_URL=https://prebid.example/auction TRUSTED_SERVER__INTEGRATIONS__PREBID__TIMEOUT_MS=1200 TRUSTED_SERVER__INTEGRATIONS__PREBID__BIDDERS=kargo,rubicon,appnexus -TRUSTED_SERVER__INTEGRATIONS__PREBID__AUTO_CONFIGURE=true TRUSTED_SERVER__INTEGRATIONS__PREBID__DEBUG=false +TRUSTED_SERVER__INTEGRATIONS__PREBID__SCRIPT_REMOVE_PATTERNS__0=/prebid.js +TRUSTED_SERVER__INTEGRATIONS__PREBID__SCRIPT_REMOVE_PATTERNS__1=/prebid.min.js ``` ### Next.js Integration diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index 962e440..44bc870 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -95,7 +95,7 @@ enabled = true server_url = "https://prebid-server.com/openrtb2/auction" timeout_ms = 1200 bidders = ["kargo", "rubicon", "appnexus"] -auto_configure = false +# script_remove_patterns = ["/static/prebid/*"] ``` ### `fastly.toml` @@ -264,7 +264,7 @@ enabled = true server_url = "https://prebid-server.com/openrtb2/auction" timeout_ms = 1200 bidders = ["kargo", "rubicon", "appnexus"] -auto_configure = false +# script_remove_patterns = ["/static/prebid/*"] ``` **Next.js**: diff --git a/docs/guide/environment-variables.md b/docs/guide/environment-variables.md index 82a11f9..fe7a99f 100644 --- a/docs/guide/environment-variables.md +++ b/docs/guide/environment-variables.md @@ -201,14 +201,15 @@ TRUSTED_SERVER__INTEGRATIONS__PREBID__TIMEOUT_MS=1000 # Bidders (comma-separated) TRUSTED_SERVER__INTEGRATIONS__PREBID__BIDDERS="appnexus,rubicon,openx" -# Auto-remove Prebid.js scripts -TRUSTED_SERVER__INTEGRATIONS__PREBID__AUTO_CONFIGURE=true - # Enable debug logging TRUSTED_SERVER__INTEGRATIONS__PREBID__DEBUG=false -# Optional: Script handler path -TRUSTED_SERVER__INTEGRATIONS__PREBID__SCRIPT_HANDLER="/prebid.js" +# Script patterns to remove Prebid tags and serve empty JS (indexed format) +# Default patterns match common Prebid filenames at exact paths +TRUSTED_SERVER__INTEGRATIONS__PREBID__SCRIPT_REMOVE_PATTERNS__0="/prebid.js" +TRUSTED_SERVER__INTEGRATIONS__PREBID__SCRIPT_REMOVE_PATTERNS__1="/prebid.min.js" +# For versioned paths, use wildcards: +# TRUSTED_SERVER__INTEGRATIONS__PREBID__SCRIPT_REMOVE_PATTERNS__0="/static/prebid/{*rest}" ``` **TOML Equivalent:** @@ -218,9 +219,8 @@ enabled = true server_url = "https://prebid-server.example.com" timeout_ms = 1000 bidders = ["appnexus", "rubicon", "openx"] -auto_configure = true debug = false -script_handler = "/prebid.js" +script_remove_patterns = ["/prebid.js", "/prebid.min.js", "/prebidjs.js", "/prebidjs.min.js"] ``` --- @@ -642,7 +642,6 @@ export TRUSTED_SERVER__INTEGRATIONS__PREBID__ENABLED=true export TRUSTED_SERVER__INTEGRATIONS__PREBID__SERVER_URL="https://prebid-server.com" export TRUSTED_SERVER__INTEGRATIONS__PREBID__TIMEOUT_MS=2000 export TRUSTED_SERVER__INTEGRATIONS__PREBID__BIDDERS="appnexus,rubicon,openx" -export TRUSTED_SERVER__INTEGRATIONS__PREBID__AUTO_CONFIGURE=true # Optional: Security Headers export TRUSTED_SERVER__RESPONSE_HEADERS__STRICT_TRANSPORT_SECURITY="max-age=31536000" diff --git a/docs/guide/integration-guide.md b/docs/guide/integration-guide.md index 74f46cf..a5c7faf 100644 --- a/docs/guide/integration-guide.md +++ b/docs/guide/integration-guide.md @@ -276,7 +276,7 @@ enabled = true server_url = "https://prebid.example/openrtb2/auction" timeout_ms = 1200 bidders = ["equativ", "sampleBidder"] -auto_configure = true +# script_remove_patterns = ["/static/prebid/*"] ``` Tests or scaffolding can inject configs by calling `settings.integrations.insert_config("prebid", &serde_json::json!({...}))`, the same helper that other integrations use. @@ -287,7 +287,7 @@ Tests or scaffolding can inject configs by calling `settings.integrations.insert **3. HTML Rewrites Through the Registry** -When `auto_configure` is enabled, the integration's `IntegrationAttributeRewriter` removes any `"#, + mode_value + ) +} + #[derive(Debug, Clone, Deserialize, Serialize, Validate)] pub struct PrebidIntegrationConfig { #[serde(default = "default_enabled")] @@ -43,6 +62,9 @@ pub struct PrebidIntegrationConfig { pub bidders: Vec, #[serde(default)] pub debug: bool, + /// Optional default mode to enqueue when injecting the unified bundle. + #[serde(default)] + pub mode: Option, /// Patterns to match Prebid script URLs for serving empty JS. /// Supports suffix matching (e.g., "/prebid.min.js" matches any path ending with that) /// and wildcard patterns (e.g., "/static/prebid/*" matches paths under that prefix). @@ -344,7 +366,8 @@ pub fn register(settings: &Settings) -> Option { Some( IntegrationRegistration::builder(PREBID_INTEGRATION_ID) .with_proxy(integration.clone()) - .with_attribute_rewriter(integration) + .with_attribute_rewriter(integration.clone()) + .with_head_injector(integration) .build(), ) } @@ -415,6 +438,19 @@ impl IntegrationAttributeRewriter for PrebidIntegration { } } +impl IntegrationHeadInjector for PrebidIntegration { + fn integration_id(&self) -> &'static str { + PREBID_INTEGRATION_ID + } + + fn head_inserts(&self, _ctx: &IntegrationHtmlContext<'_>) -> Vec { + self.config + .mode + .map(|mode| vec![config_script_tag(mode)]) + .unwrap_or_default() + } +} + fn build_openrtb_from_ts( req: &AdRequest, settings: &Settings, @@ -792,6 +828,7 @@ mod tests { timeout_ms: 1000, bidders: vec!["exampleBidder".to_string()], debug: false, + mode: None, script_patterns: default_script_patterns(), } } @@ -1163,4 +1200,55 @@ server_url = "https://prebid.example" assert!(routes.iter().any(|r| r.path == "/prebidjs.js")); assert!(routes.iter().any(|r| r.path == "/prebidjs.min.js")); } + + #[test] + fn config_script_tag_generates_render_mode() { + let tag = config_script_tag(Mode::Render); + assert!(tag.starts_with("")); + assert!(tag.contains(r#"mode:"render""#)); + assert!(tag.contains("tsjs.setConfig")); + assert!(tag.contains("tsjs.que.push")); + } + + #[test] + fn config_script_tag_generates_auction_mode() { + let tag = config_script_tag(Mode::Auction); + assert!(tag.starts_with("")); + assert!(tag.contains(r#"mode:"auction""#)); + assert!(tag.contains("tsjs.setConfig")); + } + + #[test] + fn head_injector_returns_empty_when_mode_not_set() { + let integration = PrebidIntegration::new(base_config()); + let ctx = IntegrationHtmlContext { + request_host: "pub.example", + request_scheme: "https", + origin_host: "origin.example", + document_state: &Default::default(), + }; + let inserts = integration.head_inserts(&ctx); + assert!( + inserts.is_empty(), + "should not inject config when mode is None" + ); + } + + #[test] + fn head_injector_returns_config_script_when_mode_set() { + let mut config = base_config(); + config.mode = Some(Mode::Auction); + let integration = PrebidIntegration::new(config); + let ctx = IntegrationHtmlContext { + request_host: "pub.example", + request_scheme: "https", + origin_host: "origin.example", + document_state: &Default::default(), + }; + let inserts = integration.head_inserts(&ctx); + assert_eq!(inserts.len(), 1); + assert!(inserts[0].contains(r#"mode:"auction""#)); + } } diff --git a/crates/common/src/integrations/registry.rs b/crates/common/src/integrations/registry.rs index 819890d..734586a 100644 --- a/crates/common/src/integrations/registry.rs +++ b/crates/common/src/integrations/registry.rs @@ -357,6 +357,14 @@ pub trait IntegrationHtmlPostProcessor: Send + Sync { fn post_process(&self, html: &mut String, ctx: &IntegrationHtmlContext<'_>) -> bool; } +/// Trait for integration-provided HTML head injections. +pub trait IntegrationHeadInjector: Send + Sync { + /// Identifier for logging/diagnostics. + fn integration_id(&self) -> &'static str; + /// Return HTML snippets to insert at the start of ``. + fn head_inserts(&self, ctx: &IntegrationHtmlContext<'_>) -> Vec; +} + /// Registration payload returned by integration builders. pub struct IntegrationRegistration { pub integration_id: &'static str, @@ -364,6 +372,7 @@ pub struct IntegrationRegistration { pub attribute_rewriters: Vec>, pub script_rewriters: Vec>, pub html_post_processors: Vec>, + pub head_injectors: Vec>, } impl IntegrationRegistration { @@ -386,6 +395,7 @@ impl IntegrationRegistrationBuilder { attribute_rewriters: Vec::new(), script_rewriters: Vec::new(), html_post_processors: Vec::new(), + head_injectors: Vec::new(), }, } } @@ -420,6 +430,12 @@ impl IntegrationRegistrationBuilder { self } + #[must_use] + pub fn with_head_injector(mut self, injector: Arc) -> Self { + self.registration.head_injectors.push(injector); + self + } + #[must_use] pub fn build(self) -> IntegrationRegistration { self.registration @@ -441,6 +457,7 @@ struct IntegrationRegistryInner { html_rewriters: Vec>, script_rewriters: Vec>, html_post_processors: Vec>, + head_injectors: Vec>, } impl Default for IntegrationRegistryInner { @@ -455,6 +472,7 @@ impl Default for IntegrationRegistryInner { html_rewriters: Vec::new(), script_rewriters: Vec::new(), html_post_processors: Vec::new(), + head_injectors: Vec::new(), } } } @@ -539,6 +557,9 @@ impl IntegrationRegistry { inner .html_post_processors .extend(registration.html_post_processors.into_iter()); + inner + .head_injectors + .extend(registration.head_injectors.into_iter()); } } @@ -622,6 +643,18 @@ impl IntegrationRegistry { self.inner.html_post_processors.clone() } + /// Collect HTML snippets for insertion at the start of ``. + pub fn head_inserts(&self, ctx: &IntegrationHtmlContext<'_>) -> Vec { + let mut inserts = Vec::new(); + for injector in &self.inner.head_injectors { + let mut next = injector.head_inserts(ctx); + if !next.is_empty() { + inserts.append(&mut next); + } + } + inserts + } + /// Provide a snapshot of registered integrations and their hooks. pub fn registered_integrations(&self) -> Vec { let mut map: BTreeMap<&'static str, IntegrationMetadata> = BTreeMap::new(); @@ -668,6 +701,29 @@ impl IntegrationRegistry { html_rewriters: attribute_rewriters, script_rewriters, html_post_processors: Vec::new(), + head_injectors: Vec::new(), + }), + } + } + + #[cfg(test)] + pub fn from_rewriters_with_head_injectors( + attribute_rewriters: Vec>, + script_rewriters: Vec>, + head_injectors: Vec>, + ) -> Self { + Self { + inner: Arc::new(IntegrationRegistryInner { + get_router: Router::new(), + post_router: Router::new(), + put_router: Router::new(), + delete_router: Router::new(), + patch_router: Router::new(), + routes: Vec::new(), + html_rewriters: attribute_rewriters, + script_rewriters, + html_post_processors: Vec::new(), + head_injectors, }), } } @@ -711,6 +767,7 @@ impl IntegrationRegistry { html_rewriters: Vec::new(), script_rewriters: Vec::new(), html_post_processors: Vec::new(), + head_injectors: Vec::new(), }), } } diff --git a/docs/guide/configuration-reference.md b/docs/guide/configuration-reference.md index db4d7ec..3366973 100644 --- a/docs/guide/configuration-reference.md +++ b/docs/guide/configuration-reference.md @@ -598,6 +598,7 @@ All integrations support: | `timeout_ms` | Integer | `1000` | Request timeout in milliseconds | | `bidders` | Array[String] | `[]` | List of enabled bidders | | `debug` | Boolean | `false` | Enable debug logging | +| `mode` | String | None | Default TSJS request mode when Prebid is enabled (`render` or `auction`) | | `script_patterns` | Array[String] | See below | Patterns for removing Prebid script tags and intercepting requests | **Default `script_patterns`**: @@ -615,6 +616,7 @@ server_url = "https://prebid-server.example/openrtb2/auction" timeout_ms = 1200 bidders = ["kargo", "rubicon", "appnexus", "openx"] debug = false +mode = "auction" # script_patterns = ["/static/prebid/*"] # Optional: restrict to specific path ``` @@ -625,6 +627,7 @@ TRUSTED_SERVER__INTEGRATIONS__PREBID__SERVER_URL=https://prebid.example/auction TRUSTED_SERVER__INTEGRATIONS__PREBID__TIMEOUT_MS=1200 TRUSTED_SERVER__INTEGRATIONS__PREBID__BIDDERS=kargo,rubicon,appnexus TRUSTED_SERVER__INTEGRATIONS__PREBID__DEBUG=false +TRUSTED_SERVER__INTEGRATIONS__PREBID__MODE=auction TRUSTED_SERVER__INTEGRATIONS__PREBID__SCRIPT_PATTERNS__0=/prebid.js TRUSTED_SERVER__INTEGRATIONS__PREBID__SCRIPT_PATTERNS__1=/prebid.min.js ``` diff --git a/docs/guide/environment-variables.md b/docs/guide/environment-variables.md index ce4b5a6..7162d04 100644 --- a/docs/guide/environment-variables.md +++ b/docs/guide/environment-variables.md @@ -204,6 +204,9 @@ TRUSTED_SERVER__INTEGRATIONS__PREBID__BIDDERS="appnexus,rubicon,openx" # Enable debug logging TRUSTED_SERVER__INTEGRATIONS__PREBID__DEBUG=false +# Default tsjs mode when Prebid integration is enabled (optional) +TRUSTED_SERVER__INTEGRATIONS__PREBID__MODE="auction" # or "render" + # Script patterns to remove Prebid tags and serve empty JS (indexed format) # Default patterns match common Prebid filenames at exact paths TRUSTED_SERVER__INTEGRATIONS__PREBID__SCRIPT_PATTERNS__0="/prebid.js" @@ -220,6 +223,7 @@ server_url = "https://prebid-server.example.com" timeout_ms = 1000 bidders = ["appnexus", "rubicon", "openx"] debug = false +mode = "auction" script_patterns = ["/prebid.js", "/prebid.min.js", "/prebidjs.js", "/prebidjs.min.js"] ``` diff --git a/docs/guide/integration-guide.md b/docs/guide/integration-guide.md index cb38b7c..d5fc921 100644 --- a/docs/guide/integration-guide.md +++ b/docs/guide/integration-guide.md @@ -210,6 +210,8 @@ impl IntegrationScriptRewriter for MyIntegration { `html_processor.rs` calls these hooks after applying the standard origin→first-party rewrite, so you can simply swap URLs, append query parameters, or mutate inline JSON. Use this to point `"#, + bidders_json, config.force_render + ) +} + +pub struct GamIntegration { + config: GamIntegrationConfig, +} + +impl GamIntegration { + pub fn new(config: GamIntegrationConfig) -> Self { + Self { config } + } +} + +impl IntegrationHeadInjector for GamIntegration { + fn integration_id(&self) -> &'static str { + GAM_INTEGRATION_ID + } + + fn head_inserts(&self, _ctx: &IntegrationHtmlContext<'_>) -> Vec { + vec![gam_config_script_tag(&self.config)] + } +} + +/// Register the GAM integration if enabled. +pub fn register(settings: &crate::settings::Settings) -> Option { + use std::sync::Arc; + + let config: GamIntegrationConfig = + settings.integrations.get_typed(GAM_INTEGRATION_ID).ok()??; + + log::info!( + "GAM integration enabled: bidders={:?}, force_render={}", + config.bidders, + config.force_render + ); + + let integration = Arc::new(GamIntegration::new(config)); + + Some( + IntegrationRegistration::builder(GAM_INTEGRATION_ID) + .with_head_injector(integration) + .build(), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn gam_config_script_tag_with_bidders() { + let config = GamIntegrationConfig { + enabled: true, + bidders: vec!["mocktioneer".to_string(), "appnexus".to_string()], + force_render: false, + }; + let tag = gam_config_script_tag(&config); + assert!(tag.contains("window.tsGamConfig=")); + assert!(tag.contains("enabled:true")); + assert!(tag.contains(r#"bidders:["mocktioneer","appnexus"]"#)); + assert!(tag.contains("forceRender:false")); + } + + #[test] + fn gam_config_script_tag_empty_bidders() { + let config = GamIntegrationConfig { + enabled: true, + bidders: vec![], + force_render: true, + }; + let tag = gam_config_script_tag(&config); + assert!(tag.contains("bidders:[]")); + assert!(tag.contains("forceRender:true")); + } + + #[test] + fn gam_config_disabled_by_default() { + let config = GamIntegrationConfig::default(); + assert!(!config.enabled); + assert!(config.bidders.is_empty()); + assert!(!config.force_render); + } +} diff --git a/crates/common/src/integrations/lockr.rs b/crates/common/src/integrations/lockr.rs index 014e226..1bdd9a9 100644 --- a/crates/common/src/integrations/lockr.rs +++ b/crates/common/src/integrations/lockr.rs @@ -141,7 +141,7 @@ impl LockrIntegration { lockr_req.set_header(header::USER_AGENT, "TrustedServer/1.0"); lockr_req.set_header(header::ACCEPT, "application/javascript, */*"); - let backend_name = ensure_backend_from_url(sdk_url) + let backend_name = ensure_backend_from_url(sdk_url, true) .change_context(Self::error("Failed to determine backend for SDK fetch"))?; let mut lockr_response = @@ -235,7 +235,7 @@ impl LockrIntegration { } // Get backend and forward - let backend_name = ensure_backend_from_url(&self.config.api_endpoint) + let backend_name = ensure_backend_from_url(&self.config.api_endpoint, true) .change_context(Self::error("Failed to determine backend for API proxy"))?; let response = match target_req.send(backend_name) { diff --git a/crates/common/src/integrations/mod.rs b/crates/common/src/integrations/mod.rs index b758e8e..8e7e940 100644 --- a/crates/common/src/integrations/mod.rs +++ b/crates/common/src/integrations/mod.rs @@ -3,6 +3,7 @@ use crate::settings::Settings; pub mod didomi; +pub mod gam; pub mod lockr; pub mod nextjs; pub mod permutive; @@ -28,5 +29,6 @@ pub(crate) fn builders() -> &'static [IntegrationBuilder] { permutive::register, lockr::register, didomi::register, + gam::register, ] } diff --git a/crates/common/src/integrations/permutive.rs b/crates/common/src/integrations/permutive.rs index 6d565e8..986948c 100644 --- a/crates/common/src/integrations/permutive.rs +++ b/crates/common/src/integrations/permutive.rs @@ -118,7 +118,7 @@ impl PermutiveIntegration { permutive_req.set_header(header::USER_AGENT, "TrustedServer/1.0"); permutive_req.set_header(header::ACCEPT, "application/javascript, */*"); - let backend_name = ensure_backend_from_url(&sdk_url) + let backend_name = ensure_backend_from_url(&sdk_url, true) .change_context(Self::error("Failed to determine backend for SDK fetch"))?; let mut permutive_response = @@ -208,7 +208,7 @@ impl PermutiveIntegration { } // Get backend and forward - let backend_name = ensure_backend_from_url(&self.config.api_endpoint) + let backend_name = ensure_backend_from_url(&self.config.api_endpoint, true) .change_context(Self::error("Failed to determine backend for API proxy"))?; let response = target_req @@ -277,7 +277,7 @@ impl PermutiveIntegration { } // Get backend and forward - let backend_name = ensure_backend_from_url(&self.config.secure_signals_endpoint) + let backend_name = ensure_backend_from_url(&self.config.secure_signals_endpoint, true) .change_context(Self::error( "Failed to determine backend for Secure Signals proxy", ))?; @@ -342,7 +342,7 @@ impl PermutiveIntegration { } // Get backend and forward - let backend_name = ensure_backend_from_url("https://events.permutive.app") + let backend_name = ensure_backend_from_url("https://events.permutive.app", true) .change_context(Self::error("Failed to determine backend for Events proxy"))?; let response = target_req @@ -405,7 +405,7 @@ impl PermutiveIntegration { } // Get backend and forward - let backend_name = ensure_backend_from_url("https://sync.permutive.com") + let backend_name = ensure_backend_from_url("https://sync.permutive.com", true) .change_context(Self::error("Failed to determine backend for Sync proxy"))?; let response = target_req @@ -460,7 +460,7 @@ impl PermutiveIntegration { self.copy_request_headers(&req, &mut target_req); // Get backend and forward - let backend_name = ensure_backend_from_url("https://cdn.permutive.com") + let backend_name = ensure_backend_from_url("https://cdn.permutive.com", true) .change_context(Self::error("Failed to determine backend for CDN proxy"))?; let response = target_req diff --git a/crates/common/src/integrations/prebid.rs b/crates/common/src/integrations/prebid.rs index c2380fe..485cafd 100644 --- a/crates/common/src/integrations/prebid.rs +++ b/crates/common/src/integrations/prebid.rs @@ -2,13 +2,12 @@ use std::collections::HashMap; use std::sync::Arc; use async_trait::async_trait; -use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; use error_stack::{Report, ResultExt}; use fastly::http::{header, Method, StatusCode}; use fastly::{Request, Response}; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value as Json, Value as JsonValue}; -use url::Url; +use serde_json::{Value as Json, Value as JsonValue}; +use url::{form_urlencoded, Url}; use validator::Validate; use crate::backend::ensure_backend_from_url; @@ -16,13 +15,17 @@ use crate::constants::{HEADER_SYNTHETIC_FRESH, HEADER_SYNTHETIC_TRUSTED_SERVER}; use crate::creative; use crate::error::TrustedServerError; use crate::geo::GeoInfo; +use crate::http_util::compute_encrypted_sha256_token; use crate::http_util::RequestInfo; use crate::integrations::{ AttributeRewriteAction, IntegrationAttributeContext, IntegrationAttributeRewriter, IntegrationEndpoint, IntegrationHeadInjector, IntegrationHtmlContext, IntegrationProxy, IntegrationRegistration, }; -use crate::openrtb::{Banner, Format, Imp, ImpExt, OpenRtbRequest, PrebidImpExt, Site}; +use crate::openrtb::{ + Banner, Device, Format, Geo, Imp, ImpExt, OpenRtbRequest, OpenRtbResponse, PrebidImpExt, Regs, + RegsExt, RequestExt, Site, TrustedServerExt, User, UserExt, +}; use crate::request_signing::RequestSigner; use crate::settings::{IntegrationConfig, Settings}; use crate::synthetic::{generate_synthetic_id, get_or_generate_synthetic_id}; @@ -31,6 +34,12 @@ const PREBID_INTEGRATION_ID: &str = "prebid"; const ROUTE_RENDER: &str = "/ad/render"; const ROUTE_AUCTION: &str = "/ad/auction"; +/// Mode determines how TSJS handles ad requests. +/// +/// - `Render`: Uses iframe-based server-side rendering. +/// The server handles the full auction and returns ready-to-display HTML. +/// - `Auction`: Uses client-side OpenRTB auctions. +/// Clients (for example, Prebid.js) send OpenRTB to /ad/auction. #[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "lowercase")] pub enum Mode { @@ -38,15 +47,17 @@ pub enum Mode { Auction, } -pub fn config_script_tag(mode: Mode) -> String { - let mode_value = match mode { - Mode::Render => "render", - Mode::Auction => "auction", - }; - format!( - r#""#, - mode_value - ) +/// Generate TSJS config script tag based on mode. +/// This script runs after the unified TSJS bundle has loaded. +pub fn tsjs_config_script_tag(mode: Mode) -> String { + match mode { + Mode::Render => { + r#""#.to_string() + } + Mode::Auction => { + r#""#.to_string() + } + } } #[derive(Debug, Clone, Deserialize, Serialize, Validate)] @@ -195,6 +206,12 @@ impl PrebidIntegration { // Normalize path to lowercase for case-insensitive matching let path_lower = path.to_ascii_lowercase(); + log::debug!( + "matches_script_pattern: path='{}', patterns={:?}", + path, + self.config.script_patterns + ); + // Check if path matches any configured pattern for pattern in &self.config.script_patterns { let pattern_lower = pattern.to_ascii_lowercase(); @@ -209,22 +226,36 @@ impl PrebidIntegration { pattern_lower.split("{*").next().unwrap_or("") }; + log::debug!( + " wildcard pattern='{}', prefix='{}', path_starts_with_prefix={}", + pattern, + prefix, + path_lower.starts_with(prefix) + ); + if path_lower.starts_with(prefix) { // Check if it ends with a known Prebid script suffix - if PREBID_SCRIPT_SUFFIXES + let has_suffix = PREBID_SCRIPT_SUFFIXES .iter() - .any(|suffix| path_lower.ends_with(suffix)) - { + .any(|suffix| path_lower.ends_with(suffix)); + log::debug!( + " checking suffixes: path ends with known suffix={}", + has_suffix + ); + if has_suffix { return true; } } } else { // Exact match or suffix match - if path_lower.ends_with(&pattern_lower) { + let matches = path_lower.ends_with(&pattern_lower); + log::debug!(" exact/suffix pattern='{}', matches={}", pattern, matches); + if matches { return true; } } } + log::debug!(" no pattern matched, returning false"); false } @@ -238,49 +269,22 @@ impl PrebidIntegration { async fn handle_auction( &self, settings: &Settings, - mut req: Request, + req: Request, ) -> Result> { - let body: AdRequest = serde_json::from_slice(&req.take_body_bytes()).change_context( - TrustedServerError::Prebid { - message: "Failed to parse tsjs auction request".to_string(), - }, - )?; - - log::info!("/auction: received {} adUnits", body.ad_units.len()); - for unit in &body.ad_units { - if let Some(mt) = &unit.media_types { - if let Some(banner) = &mt.banner { - log::debug!("unit={} sizes={:?}", unit.code, banner.sizes); - } - } - } - - let openrtb = build_openrtb_from_ts(&body, settings, &self.config); - if let Ok(preview) = serde_json::to_string(&openrtb) { - log::debug!( - "OpenRTB payload (truncated): {}", - &preview.chars().take(512).collect::() - ); - } - - req.set_body_json(&openrtb) - .change_context(TrustedServerError::Prebid { - message: "Failed to set OpenRTB body".to_string(), - })?; - handle_prebid_auction(settings, req, &self.config).await } fn handle_script_handler(&self) -> Result> { - let body = "// Script overridden by Trusted Server\n"; - + // Serve empty JS - Prebid.js is already bundled in the unified TSJS bundle + // that gets injected into the page. We just need to prevent the original + // Prebid script from loading/executing. Ok(Response::from_status(StatusCode::OK) .with_header( header::CONTENT_TYPE, "application/javascript; charset=utf-8", ) .with_header(header::CACHE_CONTROL, "public, max-age=31536000, immutable") - .with_body(body)) + .with_body("/* prebid.js replaced by tsjs-unified */")) } async fn handle_render( @@ -404,14 +408,34 @@ impl IntegrationProxy for PrebidIntegration { let path = req.get_path().to_string(); let method = req.get_method().clone(); + log::debug!( + "Prebid handle: method={}, path='{}', script_patterns={:?}", + method, + path, + self.config.script_patterns + ); + match method { Method::GET if path == ROUTE_RENDER => self.handle_render(settings, req).await, Method::POST if path == ROUTE_AUCTION => self.handle_auction(settings, req).await, - // Serve empty JS for matching script patterns - Method::GET if self.matches_script_pattern(&path) => self.handle_script_handler(), - _ => Err(Report::new(Self::error(format!( - "Unsupported Prebid route: {path}" - )))), + // Serve empty JS for any other GET request that was routed here by matchit + // (i.e., matched one of our script_patterns wildcards). + // Prebid.js is already bundled in tsjs-unified, so we just need to + // prevent the original script from loading. + Method::GET => { + log::debug!("Prebid: serving empty JS stub for path '{}'", path); + self.handle_script_handler() + } + _ => { + log::debug!( + "Prebid: no handler matched for {} '{}', returning error", + method, + path + ); + Err(Report::new(Self::error(format!( + "Unsupported Prebid route: {path}" + )))) + } } } } @@ -445,9 +469,12 @@ impl IntegrationHeadInjector for PrebidIntegration { } fn head_inserts(&self, _ctx: &IntegrationHtmlContext<'_>) -> Vec { + // Only inject TSJS mode config if mode is set + // The Prebid bundle (served via script interception) already has s2sConfig built-in + // GAM config is now handled by the separate GAM integration self.config .mode - .map(|mode| vec![config_script_tag(mode)]) + .map(|mode| vec![tsjs_config_script_tag(mode)]) .unwrap_or_default() } } @@ -490,10 +517,15 @@ fn build_openrtb_from_ts( Imp { id: unit.code.clone(), - banner: Some(Banner { format: formats }), + banner: Some(Banner { + format: formats, + extra: HashMap::new(), + }), ext: Some(ImpExt { - prebid: PrebidImpExt { bidder }, + prebid: Some(PrebidImpExt { bidder }), + extra: HashMap::new(), }), + extra: HashMap::new(), } }) .collect(); @@ -504,7 +536,13 @@ fn build_openrtb_from_ts( site: Some(Site { domain: Some(settings.publisher.domain.clone()), page: Some(format!("https://{}", settings.publisher.domain)), + extra: HashMap::new(), }), + user: None, + regs: None, + device: None, + ext: None, + extra: HashMap::new(), } } @@ -548,11 +586,10 @@ async fn handle_prebid_auction( config: &PrebidIntegrationConfig, ) -> Result> { log::info!("Handling Prebid auction request"); - let mut openrtb_request: Json = serde_json::from_slice(&req.take_body_bytes()).change_context( - TrustedServerError::Prebid { + let mut openrtb_request: OpenRtbRequest = serde_json::from_slice(&req.take_body_bytes()) + .change_context(TrustedServerError::Prebid { message: "Failed to parse OpenRTB request".to_string(), - }, - )?; + })?; let synthetic_id = get_or_generate_synthetic_id(settings, &req)?; let fresh_id = generate_synthetic_id(settings, &req)?; @@ -570,6 +607,7 @@ async fn handle_prebid_auction( settings, &req, )?; + override_prebid_bidders(&mut openrtb_request, &config.bidders); let mut pbs_req = Request::new( Method::POST, @@ -583,7 +621,8 @@ async fn handle_prebid_auction( })?; log::info!("Sending request to Prebid Server"); - let backend_name = ensure_backend_from_url(&config.server_url)?; + let backend_name = + ensure_backend_from_url(&config.server_url, settings.proxy.certificate_check)?; let mut pbs_response = pbs_req .send(backend_name) @@ -593,20 +632,20 @@ async fn handle_prebid_auction( if pbs_response.get_status().is_success() { let response_body = pbs_response.take_body_bytes(); - match serde_json::from_slice::(&response_body) { - Ok(mut response_json) => { + match serde_json::from_slice::(&response_body) { + Ok(mut response) => { let request_info = RequestInfo::from_request(&req); transform_prebid_response( - &mut response_json, + &mut response, + settings, &request_info.host, &request_info.scheme, )?; - let transformed_body = serde_json::to_vec(&response_json).change_context( - TrustedServerError::Prebid { + let transformed_body = + serde_json::to_vec(&response).change_context(TrustedServerError::Prebid { message: "Failed to serialize transformed response".to_string(), - }, - )?; + })?; Ok(Response::from_status(StatusCode::OK) .with_header(header::CONTENT_TYPE, "application/json") @@ -625,154 +664,264 @@ async fn handle_prebid_auction( } fn enhance_openrtb_request( - request: &mut Json, + request: &mut OpenRtbRequest, synthetic_id: &str, fresh_id: &str, settings: &Settings, req: &Request, ) -> Result<(), Report> { - if !request["user"].is_object() { - request["user"] = json!({}); - } - request["user"]["id"] = json!(synthetic_id); - - if !request["user"]["ext"].is_object() { - request["user"]["ext"] = json!({}); - } - request["user"]["ext"]["synthetic_fresh"] = json!(fresh_id); + let user = request.user.get_or_insert_with(User::default); + user.id = Some(synthetic_id.to_string()); + let user_ext = user.ext.get_or_insert_with(UserExt::default); + user_ext.synthetic_fresh = Some(fresh_id.to_string()); if req.get_header("Sec-GPC").is_some() { - if !request["regs"].is_object() { - request["regs"] = json!({}); - } - if !request["regs"]["ext"].is_object() { - request["regs"]["ext"] = json!({}); - } - request["regs"]["ext"]["us_privacy"] = json!("1YYN"); + let regs = request.regs.get_or_insert_with(Regs::default); + let regs_ext = regs.ext.get_or_insert_with(RegsExt::default); + regs_ext.us_privacy = Some("1YYN".to_string()); } if let Some(geo_info) = GeoInfo::from_request(req) { - let geo_obj = json!({ - "type": 2, - "country": geo_info.country, - "city": geo_info.city, - "region": geo_info.region, - }); - - if !request["device"].is_object() { - request["device"] = json!({}); - } - request["device"]["geo"] = geo_obj; + let device = request.device.get_or_insert_with(Device::default); + let geo = device.geo.get_or_insert_with(Geo::default); + geo.geo_type = Some(2); + geo.country = Some(geo_info.country); + geo.city = Some(geo_info.city); + geo.region = geo_info.region; } - if !request["site"].is_object() { - request["site"] = json!({ - "domain": settings.publisher.domain, - "page": format!("https://{}", settings.publisher.domain), + if request.site.is_none() { + request.site = Some(Site { + domain: Some(settings.publisher.domain.clone()), + page: Some(format!("https://{}", settings.publisher.domain)), + extra: HashMap::new(), }); } if let Some(request_signing_config) = &settings.request_signing { - if request_signing_config.enabled && request["id"].is_string() { - if !request["ext"].is_object() { - request["ext"] = json!({}); - } - - let id = request["id"] - .as_str() - .expect("should have string id when is_string checked"); + if request_signing_config.enabled { let signer = RequestSigner::from_config()?; - let signature = signer.sign(id.as_bytes())?; - request["ext"]["trusted_server"] = json!({ - "signature": signature, - "kid": signer.kid - }); + let signature = signer.sign(request.id.as_bytes())?; + let ext = request.ext.get_or_insert_with(RequestExt::default); + let trusted = ext + .trusted_server + .get_or_insert_with(TrustedServerExt::default); + trusted.signature = Some(signature); + trusted.kid = Some(signer.kid); } } Ok(()) } +/// Override bidders in the OpenRTB request with the server-configured bidder list. +/// +/// This replaces any client-provided bidder configuration with the bidders specified +/// in `[integrations.prebid].bidders` from trusted-server.toml. This ensures that: +/// +/// 1. Only approved bidders are used (security/compliance control) +/// 2. Bidder params are managed server-side, not exposed to clients +/// 3. The publisher's Prebid.js config doesn't need bidder-specific setup +/// +/// Note: Client-provided bidder params (e.g., `appnexus: { placementId: '123' }`) +/// are intentionally stripped. Bidder params should be configured in Prebid Server +/// or through trusted-server's bidder configuration. +fn override_prebid_bidders(request: &mut OpenRtbRequest, bidders: &[String]) { + let bidder_map = bidders + .iter() + .map(|bidder| (bidder.clone(), JsonValue::Object(serde_json::Map::new()))) + .collect::>(); + + log::debug!( + "Overriding OpenRTB bidders from settings: count={}", + bidder_map.len() + ); + + for imp in &mut request.imp { + let ext = imp.ext.get_or_insert_with(|| ImpExt { + prebid: None, + extra: HashMap::new(), + }); + let prebid = ext.prebid.get_or_insert_with(PrebidImpExt::default); + prebid.bidder = bidder_map.clone(); + } +} + fn transform_prebid_response( - response: &mut Json, + response: &mut OpenRtbResponse, + settings: &Settings, request_host: &str, request_scheme: &str, ) -> Result<(), Report> { - if let Some(seatbids) = response["seatbid"].as_array_mut() { - for seatbid in seatbids { - if let Some(bids) = seatbid["bid"].as_array_mut() { - for bid in bids { - if let Some(adm) = bid["adm"].as_str() { - bid["adm"] = json!(rewrite_ad_markup(adm, request_host, request_scheme)); - } + let Some(seatbids) = response.seatbid.as_mut() else { + return Ok(()); + }; - if let Some(nurl) = bid["nurl"].as_str() { - bid["nurl"] = json!(make_first_party_proxy_url( - nurl, - request_host, - request_scheme, - "track" - )); - } + for seatbid in seatbids { + let Some(bids) = seatbid.bid.as_mut() else { + continue; + }; - if let Some(burl) = bid["burl"].as_str() { - bid["burl"] = json!(make_first_party_proxy_url( - burl, - request_host, - request_scheme, - "track" - )); - } + for bid in bids { + if let Some(adm) = bid.adm.as_deref() { + if looks_like_html(adm) { + bid.adm = Some(creative::rewrite_creative_html(adm, settings)); } } + + if let Some(nurl) = bid.nurl.as_deref() { + bid.nurl = Some(first_party_proxy_url( + settings, + request_host, + request_scheme, + nurl, + )); + } + + if let Some(burl) = bid.burl.as_deref() { + bid.burl = Some(first_party_proxy_url( + settings, + request_host, + request_scheme, + burl, + )); + } } } Ok(()) } -fn rewrite_ad_markup(markup: &str, request_host: &str, request_scheme: &str) -> String { - let mut content = markup.to_string(); - let cdn_patterns = vec![ - ("https://cdn.adsrvr.org", "adsrvr"), - ("https://ib.adnxs.com", "adnxs"), - ("https://rtb.openx.net", "openx"), - ("https://as.casalemedia.com", "casale"), - ("https://eus.rubiconproject.com", "rubicon"), - ]; - - for (cdn_url, cdn_name) in cdn_patterns { - if content.contains(cdn_url) { - let proxy_base = format!( - "{}://{}/ad-proxy/{}", - request_scheme, request_host, cdn_name - ); - content = content.replace(cdn_url, &proxy_base); - } +fn first_party_proxy_url( + settings: &Settings, + request_host: &str, + request_scheme: &str, + clear_url: &str, +) -> String { + let trimmed = clear_url.trim(); + let Some(abs) = normalize_absolute_url(trimmed, request_scheme) else { + return clear_url.to_string(); + }; + if is_excluded_by_domain(settings, &abs) { + return clear_url.to_string(); } - - content = content.replace( - "//cdn.adsrvr.org", - &format!("//{}/ad-proxy/adsrvr", request_host), - ); - content = content.replace( - "//ib.adnxs.com", - &format!("//{}/ad-proxy/adnxs", request_host), - ); - content + let signed = creative::build_proxy_url(settings, &abs); + let proxy_path = if signed == abs { + build_proxy_path_for_raw_url(settings, &abs) + } else { + signed + }; + absolutize_proxy_path(settings, request_host, request_scheme, proxy_path) } -fn make_first_party_proxy_url( - third_party_url: &str, +fn absolutize_proxy_path( + settings: &Settings, request_host: &str, request_scheme: &str, - proxy_type: &str, + proxy_path: String, ) -> String { - let encoded = BASE64.encode(third_party_url.as_bytes()); - format!( - "{}://{}/ad-proxy/{}/{}", - request_scheme, request_host, proxy_type, encoded - ) + if proxy_path.starts_with('/') { + let host = if request_host.is_empty() { + settings.publisher.domain.as_str() + } else { + request_host + }; + if host.is_empty() { + return proxy_path; + } + return format!("{request_scheme}://{host}{proxy_path}"); + } + proxy_path +} + +fn looks_like_html(markup: &str) -> bool { + let trimmed = markup.trim_start(); + if trimmed.is_empty() { + return false; + } + let lower = trimmed.to_ascii_lowercase(); + if !lower.starts_with('<') { + return false; + } + if lower.starts_with(" Option { + let lower = url.to_ascii_lowercase(); + if lower.starts_with("http://") || lower.starts_with("https://") { + return Some(url.to_string()); + } + if url.starts_with("//") { + return Some(format!("{}:{}", request_scheme, url)); + } + None +} + +fn is_excluded_by_domain(settings: &Settings, abs_url: &str) -> bool { + if settings.rewrite.exclude_domains.is_empty() { + return false; + } + if let Ok(parsed) = Url::parse(abs_url) { + return settings.rewrite.is_excluded(parsed.as_str()); + } + let Some(host) = extract_host(abs_url) else { + return false; + }; + let check = format!("https://{}", host); + settings.rewrite.is_excluded(&check) +} + +fn extract_host(abs_url: &str) -> Option<&str> { + let lower = abs_url.to_ascii_lowercase(); + let rest = if lower.starts_with("https://") { + &abs_url["https://".len()..] + } else if lower.starts_with("http://") { + &abs_url["http://".len()..] + } else { + return None; + }; + let host = rest.split(['/', '?', '#']).next().unwrap_or(""); + if host.is_empty() { + return None; + } + Some(host) +} + +fn build_proxy_path_for_raw_url(settings: &Settings, clear_url: &str) -> String { + let token = compute_encrypted_sha256_token(settings, clear_url); + let mut qs = form_urlencoded::Serializer::new(String::new()); + qs.append_pair("tsurl", clear_url); + qs.append_pair("tstoken", &token); + format!("/first-party/proxy?{}", qs.finish()) } fn copy_request_headers(from: &Request, to: &mut Request) { @@ -791,8 +940,6 @@ fn copy_request_headers(from: &Request, to: &mut Request) { } } -// Request host/scheme extraction is now centralized in http_util::RequestInfo - #[cfg(test)] mod tests { use super::*; @@ -977,40 +1124,101 @@ mod tests { #[test] fn enhance_openrtb_request_adds_ids_and_regs() { let settings = make_settings(); - let mut request_json = json!({ - "id": "openrtb-request-id" - }); + let mut request = OpenRtbRequest { + id: "openrtb-request-id".to_string(), + imp: Vec::new(), + site: None, + user: None, + regs: None, + device: None, + ext: None, + extra: HashMap::new(), + }; let synthetic_id = "synthetic-123"; let fresh_id = "fresh-456"; let mut req = Request::new(Method::POST, "https://edge.example/auction"); req.set_header("Sec-GPC", "1"); - enhance_openrtb_request(&mut request_json, synthetic_id, fresh_id, &settings, &req) + enhance_openrtb_request(&mut request, synthetic_id, fresh_id, &settings, &req) .expect("should enhance request"); - assert_eq!(request_json["user"]["id"], synthetic_id); - assert_eq!(request_json["user"]["ext"]["synthetic_fresh"], fresh_id); + let user = request.user.as_ref().expect("should have user"); + assert_eq!(user.id.as_deref(), Some(synthetic_id), "should set user id"); + let user_ext = user.ext.as_ref().expect("should have user ext"); assert_eq!( - request_json["regs"]["ext"]["us_privacy"], "1YYN", - "GPC header should map to US privacy flag" + user_ext.synthetic_fresh.as_deref(), + Some(fresh_id), + "should set synthetic fresh id" ); + let regs = request.regs.as_ref().expect("should have regs"); + let regs_ext = regs.ext.as_ref().expect("should have regs ext"); assert_eq!( - request_json["site"]["domain"], settings.publisher.domain, - "site domain should match publisher domain" + regs_ext.us_privacy.as_deref(), + Some("1YYN"), + "should map GPC header to US privacy flag" ); - assert!( - request_json["site"]["page"] - .as_str() - .unwrap() - .starts_with("https://"), - "site page should be populated" + let site = request.site.as_ref().expect("should have site"); + assert_eq!( + site.domain.as_deref(), + Some(settings.publisher.domain.as_str()), + "should set site domain" + ); + let page = site.page.as_ref().expect("should have site page"); + assert!(page.starts_with("https://"), "should set site page"); + } + + #[test] + fn override_prebid_bidders_replaces_request_values() { + let mut request: OpenRtbRequest = serde_json::from_value(json!({ + "id": "openrtb-request-id", + "imp": [ + { + "id": "slot-a", + "ext": { + "prebid": { + "bidder": { "legacy": {} } + } + } + }, + { "id": "slot-b" } + ] + })) + .expect("should parse openrtb request"); + let bidders = vec!["appnexus".to_string(), "rubicon".to_string()]; + + override_prebid_bidders(&mut request, &bidders); + + let expected = bidders + .iter() + .map(|bidder| (bidder.clone(), JsonValue::Object(serde_json::Map::new()))) + .collect::>(); + let first = request.imp.first().expect("should have first imp"); + let first_prebid = first + .ext + .as_ref() + .and_then(|ext| ext.prebid.as_ref()) + .expect("should have prebid ext for first imp"); + assert_eq!( + first_prebid.bidder, expected, + "should replace bidders in first imp" + ); + let second = request.imp.get(1).expect("should have second imp"); + let second_prebid = second + .ext + .as_ref() + .and_then(|ext| ext.prebid.as_ref()) + .expect("should have prebid ext for second imp"); + assert_eq!( + second_prebid.bidder, expected, + "should replace bidders in second imp" ); } #[test] fn transform_prebid_response_rewrites_creatives_and_tracking() { - let mut response = json!({ + let settings = crate::test_support::tests::create_test_settings(); + let mut response: OpenRtbResponse = serde_json::from_value(json!({ "seatbid": [{ "bid": [{ "adm": r#""#, @@ -1018,30 +1226,69 @@ mod tests { "burl": "https://notify.example/bill" }] }] - }); + })) + .expect("should parse openrtb response"); - transform_prebid_response(&mut response, "pub.example", "https") + transform_prebid_response(&mut response, &settings, "pub.example", "https") .expect("should rewrite response"); - let rewritten_adm = response["seatbid"][0]["bid"][0]["adm"] - .as_str() + let rewritten_adm = response + .seatbid + .as_ref() + .and_then(|seatbids| seatbids.first()) + .and_then(|seatbid| seatbid.bid.as_ref()) + .and_then(|bids| bids.first()) + .and_then(|bid| bid.adm.as_deref()) .expect("adm should be string"); assert!( - rewritten_adm.contains("/ad-proxy/adsrvr"), - "creative markup should proxy CDN urls" + rewritten_adm.contains("/first-party/proxy?tsurl="), + "creative markup should proxy asset urls" ); - for url_field in ["nurl", "burl"] { - let value = response["seatbid"][0]["bid"][0][url_field] - .as_str() - .unwrap(); + let bid = response + .seatbid + .as_ref() + .and_then(|seatbids| seatbids.first()) + .and_then(|seatbid| seatbid.bid.as_ref()) + .and_then(|bids| bids.first()) + .expect("should have bid"); + for value in [bid.nurl.as_deref(), bid.burl.as_deref()] { + let value = value.expect("should have tracking url"); assert!( - value.contains("/ad-proxy/track/"), + value.starts_with("https://pub.example/first-party/proxy?tsurl="), "tracking URLs should be proxied" ); } } + #[test] + fn transform_prebid_response_preserves_non_html_adm() { + let settings = crate::test_support::tests::create_test_settings(); + let adm = r#""#; + let mut response: OpenRtbResponse = serde_json::from_value(json!({ + "seatbid": [{ + "bid": [{ + "adm": adm, + "nurl": "https://notify.example/win" + }] + }] + })) + .expect("should parse openrtb response"); + + transform_prebid_response(&mut response, &settings, "pub.example", "https") + .expect("should rewrite response"); + + let rewritten_adm = response + .seatbid + .as_ref() + .and_then(|seatbids| seatbids.first()) + .and_then(|seatbid| seatbid.bid.as_ref()) + .and_then(|bids| bids.first()) + .and_then(|bid| bid.adm.as_deref()) + .expect("adm should be string"); + assert_eq!(rewritten_adm, adm, "non-html adm should remain unchanged"); + } + #[test] fn extract_adm_for_slot_prefers_exact_match() { let response = json!({ @@ -1065,19 +1312,50 @@ mod tests { } #[test] - fn make_first_party_proxy_url_base64_encodes_target() { + fn first_party_proxy_url_signs_target() { + let settings = crate::test_support::tests::create_test_settings(); let url = "https://cdn.example/path?x=1"; - let rewritten = make_first_party_proxy_url(url, "pub.example", "https", "track"); + let rewritten = first_party_proxy_url(&settings, "pub.example", "https", url); assert!( - rewritten.starts_with("https://pub.example/ad-proxy/track/"), + rewritten.starts_with("https://pub.example/first-party/proxy?tsurl="), "proxy prefix should be applied" ); + assert!( + rewritten.contains("tstoken="), + "proxy url should include a signature" + ); + } + + #[test] + fn first_party_proxy_url_handles_macros() { + let settings = crate::test_support::tests::create_test_settings(); + let url = "https://notify.example/win?price=${AUCTION_PRICE}&id=123"; + let rewritten = first_party_proxy_url(&settings, "pub.example", "https", url); + assert!( + rewritten.starts_with("https://pub.example/first-party/proxy?tsurl="), + "proxy prefix should be applied" + ); + assert!( + rewritten.contains("tstoken="), + "proxy url should include a signature" + ); + assert!( + rewritten.contains("notify.example"), + "proxy url should include target host" + ); + assert!( + rewritten.contains("AUCTION_PRICE"), + "proxy url should preserve macro tokens" + ); + } - let encoded = rewritten.split("/ad-proxy/track/").nth(1).unwrap(); - let decoded = BASE64 - .decode(encoded.as_bytes()) - .expect("should decode base64 proxy payload"); - assert_eq!(String::from_utf8(decoded).unwrap(), url); + #[test] + fn first_party_proxy_url_respects_exclude_domains() { + let mut settings = crate::test_support::tests::create_test_settings(); + settings.rewrite.exclude_domains = vec!["notify.example".to_string()]; + let url = "https://notify.example/win"; + let rewritten = first_party_proxy_url(&settings, "pub.example", "https", url); + assert_eq!(rewritten, url, "excluded domains should not be proxied"); } #[test] @@ -1162,7 +1440,10 @@ server_url = "https://prebid.example" assert!(cache_control.contains("immutable")); let body = response.into_body_str(); - assert!(body.contains("// Script overridden by Trusted Server")); + assert!( + body.contains("tsjs-unified"), + "should contain comment about replacement" + ); } #[test] @@ -1187,26 +1468,31 @@ server_url = "https://prebid.example" } #[test] - fn config_script_tag_generates_render_mode() { - let tag = config_script_tag(Mode::Render); + fn tsjs_config_script_tag_generates_render_mode() { + let tag = tsjs_config_script_tag(Mode::Render); assert!(tag.starts_with("")); assert!(tag.contains(r#"mode:"render""#)); assert!(tag.contains("tsjs.setConfig")); - assert!(tag.contains("tsjs.que.push")); + // Should have guard for tsjs existence + assert!(tag.contains("window.tsjs&&")); } #[test] - fn config_script_tag_generates_auction_mode() { - let tag = config_script_tag(Mode::Auction); + fn tsjs_config_script_tag_generates_auction_mode() { + let tag = tsjs_config_script_tag(Mode::Auction); assert!(tag.starts_with("")); assert!(tag.contains(r#"mode:"auction""#)); assert!(tag.contains("tsjs.setConfig")); + // Should have guard for tsjs existence + assert!(tag.contains("window.tsjs&&")); } #[test] - fn head_injector_returns_empty_when_mode_not_set() { + fn head_injector_returns_empty_when_no_mode() { + // When no mode is set, no head inserts needed + // (s2sConfig is built into the Prebid bundle served via script interception) let integration = PrebidIntegration::new(base_config()); let ctx = IntegrationHtmlContext { request_host: "pub.example", @@ -1215,14 +1501,11 @@ server_url = "https://prebid.example" document_state: &Default::default(), }; let inserts = integration.head_inserts(&ctx); - assert!( - inserts.is_empty(), - "should not inject config when mode is None" - ); + assert!(inserts.is_empty(), "no head inserts when mode not set"); } #[test] - fn head_injector_returns_config_script_when_mode_set() { + fn head_injector_injects_mode_config_when_mode_set() { let mut config = base_config(); config.mode = Some(Mode::Auction); let integration = PrebidIntegration::new(config); @@ -1233,7 +1516,28 @@ server_url = "https://prebid.example" document_state: &Default::default(), }; let inserts = integration.head_inserts(&ctx); - assert_eq!(inserts.len(), 1); - assert!(inserts[0].contains(r#"mode:"auction""#)); + assert_eq!(inserts.len(), 1, "should inject mode config"); + assert!( + inserts[0].contains(r#"mode:"auction""#), + "should contain mode config" + ); + } + + #[test] + fn script_handler_serves_empty_js_stub() { + let integration = PrebidIntegration::new(base_config()); + + let response = integration + .handle_script_handler() + .expect("should return response"); + + assert_eq!(response.get_status(), StatusCode::OK); + let body = response.into_body_str(); + // Should be a small stub comment, not the full bundle + assert!( + body.len() < 100, + "should serve empty JS stub, not full bundle" + ); + assert!(body.starts_with("/*"), "should be a JS comment"); } } diff --git a/crates/common/src/openrtb.rs b/crates/common/src/openrtb.rs index c73f974..0fcaabd 100644 --- a/crates/common/src/openrtb.rs +++ b/crates/common/src/openrtb.rs @@ -1,8 +1,10 @@ -use serde::Serialize; +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; use serde_json::Value; /// Minimal subset of OpenRTB 2.x bid request used by Trusted Server. -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub struct OpenRtbRequest { /// Unique ID of the bid request, provided by the exchange. @@ -10,42 +12,168 @@ pub struct OpenRtbRequest { pub imp: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub site: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub user: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub regs: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub device: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub ext: Option, + #[serde(default, flatten)] + pub extra: HashMap, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct Imp { pub id: String, #[serde(skip_serializing_if = "Option::is_none")] pub banner: Option, #[serde(skip_serializing_if = "Option::is_none")] pub ext: Option, + #[serde(default, flatten)] + pub extra: HashMap, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct Banner { + #[serde(default, skip_serializing_if = "Vec::is_empty")] pub format: Vec, + #[serde(default, flatten)] + pub extra: HashMap, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct Format { pub w: u32, pub h: u32, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct Site { #[serde(skip_serializing_if = "Option::is_none")] pub domain: Option, #[serde(skip_serializing_if = "Option::is_none")] pub page: Option, + #[serde(default, flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct User { + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub ext: Option, + #[serde(default, flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct UserExt { + #[serde(skip_serializing_if = "Option::is_none")] + pub synthetic_fresh: Option, + #[serde(default, flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Regs { + #[serde(skip_serializing_if = "Option::is_none")] + pub ext: Option, + #[serde(default, flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct RegsExt { + #[serde(skip_serializing_if = "Option::is_none")] + pub us_privacy: Option, + #[serde(default, flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Device { + #[serde(skip_serializing_if = "Option::is_none")] + pub geo: Option, + #[serde(default, flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Geo { + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub geo_type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub country: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub city: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub region: Option, + #[serde(default, flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct RequestExt { + #[serde(skip_serializing_if = "Option::is_none")] + pub trusted_server: Option, + #[serde(default, flatten)] + pub extra: HashMap, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct TrustedServerExt { + #[serde(skip_serializing_if = "Option::is_none")] + pub signature: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub kid: Option, + #[serde(default, flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Serialize, Deserialize)] pub struct ImpExt { - pub prebid: PrebidImpExt, + #[serde(skip_serializing_if = "Option::is_none")] + pub prebid: Option, + #[serde(default, flatten)] + pub extra: HashMap, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize, Default)] pub struct PrebidImpExt { - pub bidder: std::collections::HashMap, + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub bidder: HashMap, +} + +/// Minimal subset of OpenRTB 2.x bid response used by Trusted Server. +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct OpenRtbResponse { + #[serde(skip_serializing_if = "Option::is_none")] + pub seatbid: Option>, + #[serde(default, flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct SeatBid { + #[serde(skip_serializing_if = "Option::is_none")] + pub bid: Option>, + #[serde(default, flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Bid { + #[serde(skip_serializing_if = "Option::is_none")] + pub impid: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub adm: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub nurl: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub burl: Option, + #[serde(default, flatten)] + pub extra: HashMap, } diff --git a/crates/common/src/proxy.rs b/crates/common/src/proxy.rs index 64b72bc..6a7dbff 100644 --- a/crates/common/src/proxy.rs +++ b/crates/common/src/proxy.rs @@ -150,6 +150,11 @@ fn finalize_proxied_response( if ct.contains("text/html") { // HTML: rewrite and serve as HTML (safe to read as string) let body = beresp.take_body_str(); + // Debug: log first 500 chars of raw HTML to see if port is present + log::debug!( + "proxy: raw HTML body (first 500 chars): {}", + body.chars().take(500).collect::() + ); let rewritten = crate::creative::rewrite_creative_html(&body, settings); return rebuild_text_response(beresp, "text/html; charset=utf-8", rewritten); } @@ -411,10 +416,16 @@ async fn proxy_with_redirects( })); } - let backend_name = crate::backend::ensure_origin_backend(&scheme, host, parsed_url.port())?; + let backend_name = crate::backend::ensure_origin_backend( + &scheme, + host, + parsed_url.port(), + settings.proxy.certificate_check, + )?; let mut proxy_req = Request::new(current_method.clone(), ¤t_url); copy_proxy_forward_headers(req, &mut proxy_req); + if let Some(body_bytes) = body { proxy_req.set_body(body_bytes.to_vec()); } @@ -1086,6 +1097,28 @@ mod tests { assert_eq!(err.current_context().status_code(), StatusCode::BAD_GATEWAY); } + #[tokio::test] + async fn proxy_sign_preserves_non_standard_port() { + let settings = create_test_settings(); + // Test with non-standard port (e.g., 9443) + let body = serde_json::json!({ + "url": "https://cdn.example.com:9443/img/300x250.svg", + }); + let mut req = Request::new(Method::POST, "https://edge.example/first-party/sign"); + req.set_body(body.to_string()); + let mut resp = handle_first_party_proxy_sign(&settings, req) + .await + .expect("sign ok"); + assert_eq!(resp.get_status(), StatusCode::OK); + let json = resp.take_body_str(); + // Port 9443 should be preserved (URL-encoded as %3A9443) + assert!( + json.contains("%3A9443"), + "Port should be preserved in signed URL: {}", + json + ); + } + #[test] fn proxy_request_config_supports_streaming_and_headers() { let cfg = ProxyRequestConfig::new("https://example.com/asset") @@ -1464,6 +1497,44 @@ mod tests { assert_eq!(ct, "text/css; charset=utf-8"); } + #[test] + fn html_response_rewrite_preserves_non_standard_port() { + // Verify that HTML rewriting preserves non-standard ports in sub-resource URLs. + // This is the core test for the port preservation fix. + let settings = create_test_settings(); + + let html = r#" + + + + + + + +"#; + + let beresp = Response::from_status(StatusCode::OK) + .with_header(header::CONTENT_TYPE, "text/html; charset=utf-8") + .with_body(html); + + let req = Request::new(Method::GET, "https://edge.example/first-party/proxy"); + let mut out = finalize( + &settings, + &req, + "https://cdn.example.com:9443/creatives/300x250.html", + beresp, + ); + + let body = out.take_body_str(); + + // Port 9443 should be preserved (URL-encoded as %3A9443) + assert!( + body.contains("cdn.example.com%3A9443"), + "Port 9443 should be preserved in rewritten URLs. Body:\n{}", + body + ); + } + #[test] fn image_accept_sets_generic_content_type_when_missing() { let settings = create_test_settings(); diff --git a/crates/common/src/publisher.rs b/crates/common/src/publisher.rs index 796728f..09911ab 100644 --- a/crates/common/src/publisher.rs +++ b/crates/common/src/publisher.rs @@ -211,7 +211,10 @@ pub fn handle_publisher_request( has_synthetic_cookie ); - let backend_name = ensure_backend_from_url(&settings.publisher.origin_url)?; + let backend_name = ensure_backend_from_url( + &settings.publisher.origin_url, + settings.proxy.certificate_check, + )?; let origin_host = settings.publisher.origin_host(); log::debug!( diff --git a/crates/common/src/settings.rs b/crates/common/src/settings.rs index 4a41b1e..2a2880a 100644 --- a/crates/common/src/settings.rs +++ b/crates/common/src/settings.rs @@ -259,6 +259,28 @@ fn default_request_signing_enabled() -> bool { false } +/// Proxy settings for `/first-party/proxy` endpoint +#[derive(Debug, Deserialize, Serialize)] +pub struct Proxy { + /// Enable TLS certificate verification when proxying to HTTPS origins. + /// Defaults to true for secure production use. + /// Set to false for local development with self-signed certificates. + #[serde(default = "default_certificate_check")] + pub certificate_check: bool, +} + +fn default_certificate_check() -> bool { + true +} + +impl Default for Proxy { + fn default() -> Self { + Self { + certificate_check: default_certificate_check(), + } + } +} + #[derive(Debug, Default, Deserialize, Serialize, Validate)] pub struct Settings { #[validate(nested)] @@ -277,6 +299,8 @@ pub struct Settings { #[serde(default)] #[validate(nested)] pub rewrite: Rewrite, + #[serde(default)] + pub proxy: Proxy, } #[allow(unused)] diff --git a/crates/js/Cargo.toml b/crates/js/Cargo.toml index 1ebd755..3e16e47 100644 --- a/crates/js/Cargo.toml +++ b/crates/js/Cargo.toml @@ -15,6 +15,7 @@ repository = "https://example.invalid/trusted-server" readme = "README.md" [build-dependencies] +build-print = { workspace = true } which = { workspace = true } [dependencies] diff --git a/crates/js/build.rs b/crates/js/build.rs index 26d12a1..4058960 100644 --- a/crates/js/build.rs +++ b/crates/js/build.rs @@ -3,6 +3,8 @@ use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; +use build_print::{error, info, warn}; + const UNIFIED_BUNDLE: &str = "tsjs-unified.js"; fn main() { @@ -32,7 +34,7 @@ fn main() { // If Node/npm is absent, keep going if dist exists let npm = which::which("npm").ok(); if npm.is_none() { - println!("cargo:warning=tsjs: npm not found; will use existing dist if available"); + warn!("tsjs: npm not found; will use existing dist if available"); } // Install deps if node_modules missing @@ -44,9 +46,7 @@ fn main() { .current_dir(&ts_dir) .status(); if !status.as_ref().map(|s| s.success()).unwrap_or(false) { - println!( - "cargo:warning=tsjs: npm install failed; using existing dist if available" - ); + error!("tsjs: npm install failed; using existing dist if available"); } } } @@ -60,24 +60,24 @@ fn main() { .status(); } - // Build unified bundle + // Build unified bundle (includes Prebid.js) if !skip { if let Some(npm_path) = npm.clone() { - println!("cargo:warning=tsjs: Building unified bundle"); + info!("tsjs: Building unified bundle"); let js_modules = env::var("TSJS_MODULES").unwrap_or("".to_string()); let status = Command::new(&npm_path) .env("TSJS_MODULES", js_modules) - .args(["run", "build:custom"]) + .args(["run", "build"]) .current_dir(&ts_dir) .status(); if !status.as_ref().map(|s| s.success()).unwrap_or(false) { - panic!("tsjs: npm run build:custom failed - refusing to use stale bundle"); + panic!("tsjs: npm run build failed - refusing to use stale bundle"); } } } - // Copy unified bundle into OUT_DIR for include_str! + // Copy bundle into OUT_DIR for include_str! copy_bundle(UNIFIED_BUNDLE, true, &crate_dir, &dist_dir, &out_dir); } diff --git a/crates/js/lib/package-lock.json b/crates/js/lib/package-lock.json index 83411a3..cac5ded 100644 --- a/crates/js/lib/package-lock.json +++ b/crates/js/lib/package-lock.json @@ -7,12 +7,16 @@ "": { "name": "tsjs", "version": "0.1.0", + "dependencies": { + "prebid.js": "^10.18.0" + }, "devDependencies": { "@eslint/js": "^9.13.0", "@types/jsdom": "^27.0.0", "@types/node": "^24.10.0", "@typescript-eslint/eslint-plugin": "^8.6.0", "@typescript-eslint/parser": "^8.6.0", + "baseline-browser-mapping": "^2.9.16", "eslint": "^9.10.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-import": "^2.29.1", @@ -26,58 +30,1655 @@ "vitest": "^4.0.8" } }, - "node_modules/@acemir/cssom": { - "version": "0.9.23", - "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.23.tgz", - "integrity": "sha512-2kJ1HxBKzPLbmhZpxBiTZggjtgCwKg1ma5RHShxvd6zgqhDEdEkzpiwe7jLkI2p2BrZvFCXIihdoMkl1H39VnA==", - "dev": true, - "license": "MIT" + "node_modules/@acemir/cssom": { + "version": "0.9.23", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.23.tgz", + "integrity": "sha512-2kJ1HxBKzPLbmhZpxBiTZggjtgCwKg1ma5RHShxvd6zgqhDEdEkzpiwe7jLkI2p2BrZvFCXIihdoMkl1H39VnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.0.tgz", + "integrity": "sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.2" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.4.tgz", + "integrity": "sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.2" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.10" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.6.tgz", + "integrity": "sha512-9knsChgsMzBV5Yh3kkhrZNxH3oCYAfMBkNNaVN4cP2RVlFPe8wYdwwcnOsAbkdDoV9UjFtOXWrWB52M8W4jNeA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.28.6.tgz", + "integrity": "sha512-5suVoXjC14lUN6ZL9OLKIHCNVWCrqGqlmEp/ixdXjvgnEl/kauLvvMO/Xw9NyMc95Joj1AeLVPVMvibBgSoFlA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz", + "integrity": "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.6.tgz", + "integrity": "sha512-eZhoEZHYQLL5uc1gS5e9/oTknS0sSSAtd5TkKMUp3J+S/CaUjagc0kOUPsEbDmMeva0nC3WWl4SxVY6+OBuxfw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.5.tgz", + "integrity": "sha512-20NUVgOrinudkIBzQ2bNxP08YpKprUkRTiRSd2/Z5GOdPImJGkoN4Z7IQe1T5AdyKI1i5L6RBmluqdSzvaq9/w==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } }, - "node_modules/@asamuzakjp/css-color": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.0.tgz", - "integrity": "sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w==", - "dev": true, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", "license": "MIT", "dependencies": { - "@csstools/css-calc": "^2.1.4", - "@csstools/css-color-parser": "^3.1.0", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "lru-cache": "^11.2.2" + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@asamuzakjp/dom-selector": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.4.tgz", - "integrity": "sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==", - "dev": true, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", "license": "MIT", "dependencies": { - "@asamuzakjp/nwsapi": "^2.3.9", - "bidi-js": "^1.0.3", - "css-tree": "^3.1.0", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.2" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@asamuzakjp/nwsapi": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", - "dev": true, - "license": "MIT" + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@babel/helper-validator-identifier": { + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", + "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.6.tgz", + "integrity": "sha512-GaTI4nXDrs7l0qaJ6Rg06dtOXTBCG6TMDB44zbqofCIC4PqC7SEvmFFtpxzCDw9W5aJ7RKVshgXTLvLdBFV/qw==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.6", + "@babel/plugin-transform-async-to-generator": "^7.28.6", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.28.6", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.28.6", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", + "@babel/plugin-transform-modules-systemjs": "^7.28.5", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.6", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-typescript": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", + "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@csstools/color-helpers": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", @@ -925,13 +2526,51 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1298,6 +2937,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@standard-schema/spec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", @@ -1316,6 +2961,15 @@ "assertion-error": "^2.0.1" } }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", @@ -1346,7 +3000,6 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, "license": "MIT" }, "node_modules/@types/json5": { @@ -1360,7 +3013,6 @@ "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -1720,6 +3372,19 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -1771,11 +3436,70 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "license": "MIT", + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -1787,6 +3511,28 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/are-docs-informative": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", @@ -1804,6 +3550,24 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", @@ -1821,6 +3585,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, "node_modules/array-includes": { "version": "3.1.9", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", @@ -1936,6 +3706,15 @@ "node": ">=12" } }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -1962,18 +3741,73 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/baseline-browser-mapping": { - "version": "2.8.30", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.30.tgz", - "integrity": "sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==", - "dev": true, + "version": "2.9.16", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.16.tgz", + "integrity": "sha512-KeUZdBuxngy825i8xvzaK1Ncnkx0tBmb3k8DkEuqjKRkmtvNTjey2ZsNeh8Dw4lfKvbCOu9oeNx2TKm2vHqcRw==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -1989,6 +3823,75 @@ "require-from-string": "^2.0.2" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -2003,7 +3906,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -2016,7 +3918,6 @@ "version": "4.28.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2047,6 +3948,41 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bufferstreams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-1.0.1.tgz", + "integrity": "sha512-LZmiIfQprMLS6/k42w/PTc7awhU8AdNNcUerxTgr01WlP9agR2SgMv0wjlYYFD6eDOi8WvofrTX8RayjR/AeUQ==", + "dependencies": { + "readable-stream": "^1.0.33" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/bufferstreams/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/bufferstreams/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/bufferstreams/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, "node_modules/builtin-modules": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", @@ -2060,6 +3996,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -2083,7 +4028,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2097,7 +4041,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2124,7 +4067,6 @@ "version": "1.0.30001756", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz", "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2175,6 +4117,42 @@ "dev": true, "license": "MIT" }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ci-info": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", @@ -2214,11 +4192,21 @@ "node": ">=0.8.0" } }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2231,7 +4219,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/comment-parser": { @@ -2248,14 +4235,156 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/connect/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "deprecated": "Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog", + "license": "MIT", + "dependencies": { + "bluebird": "^3.1.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.47.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.47.0.tgz", "integrity": "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==", - "dev": true, "license": "MIT", "dependencies": { "browserslist": "^4.28.0" @@ -2265,6 +4394,25 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2280,6 +4428,12 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, "node_modules/css-tree": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", @@ -2309,6 +4463,12 @@ "node": ">=20" } }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "license": "MIT" + }, "node_modules/data-urls": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz", @@ -2377,11 +4537,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2445,6 +4613,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "license": "MIT" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -2458,11 +4657,31 @@ "node": ">=0.10.0" } }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "license": "MIT", + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -2473,13 +4692,83 @@ "node": ">= 0.4" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.259", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.259.tgz", "integrity": "sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==", - "dev": true, "license": "ISC" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz", + "integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.18.3" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz", + "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "punycode": "^1.4.1", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ent/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "license": "MIT" + }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -2566,7 +4855,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2576,7 +4864,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2593,7 +4880,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2649,6 +4935,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", @@ -2695,12 +4987,17 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3105,6 +5402,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -3155,12 +5465,26 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -3171,11 +5495,90 @@ "node": ">=12.0.0" } }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-glob": { @@ -3222,6 +5625,22 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -3249,7 +5668,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -3258,6 +5676,39 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3306,9 +5757,28 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3325,11 +5795,57 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-readfile-promise": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-readfile-promise/-/fs-readfile-promise-3.0.1.tgz", + "integrity": "sha512-LsSxMeaJdYH27XrW7Dmq0Gx63mioULCRel63B5VeELYLavi1wF5s0XfsIdKDFdCL9hsfQ2qBvXJszQtQJ9h17A==", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -3340,11 +5856,19 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fun-hooks": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fun-hooks/-/fun-hooks-1.1.0.tgz", + "integrity": "sha512-LTpm8ayq0ZgrrK0D/Fk+2cgCgkpLppsGKtQIdZBg/5ncKXTGPKuYz8DBG+z8FVCDsJM1DtnYGy4FfSYRLnWbsQ==", + "license": "MIT", + "dependencies": { + "typescript-tuple": "^2.2.1" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3391,11 +5915,28 @@ "node": ">= 0.4" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3420,7 +5961,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -3448,6 +5988,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3461,6 +6022,28 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -3495,22 +6078,107 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, + "license": "MIT" + }, + "node_modules/gulp-babel": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-8.0.0.tgz", + "integrity": "sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ==", + "license": "MIT", + "dependencies": { + "plugin-error": "^1.0.1", + "replace-ext": "^1.0.0", + "through2": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/gulp-wrap": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/gulp-wrap/-/gulp-wrap-0.15.0.tgz", + "integrity": "sha512-f17zkGObA+hE/FThlg55gfA0nsXbdmHK1WqzjjB2Ytq1TuhLR7JiCBJ3K4AlMzCyoFaCjfowos+VkToUNE0WTQ==", + "dependencies": { + "consolidate": "^0.15.1", + "es6-promise": "^4.2.6", + "fs-readfile-promise": "^3.0.1", + "js-yaml": "^3.13.0", + "lodash": "^4.17.11", + "node.extend": "2.0.2", + "plugin-error": "^1.0.1", + "through2": "^3.0.1", + "tryit": "^1.0.1", + "vinyl-bufferstream": "^1.0.1" + }, + "engines": { + "node": ">=6.14", + "npm": ">=1.4.3" + } + }, + "node_modules/gulp-wrap/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gulp-wrap/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/gulp-wrap/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.4.0" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -3567,7 +6235,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3580,7 +6247,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -3596,7 +6262,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3635,6 +6300,40 @@ ], "license": "MIT" }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -3663,6 +6362,36 @@ "node": ">= 14" } }, + "node_modules/iab-adcom": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/iab-adcom/-/iab-adcom-1.0.6.tgz", + "integrity": "sha512-XAJdidfrFgZNKmHqcXD3Zhqik2rdSmOs+PGgeVfPWgthxvzNBQxkZnKkW3QAau6mrLjtJc8yOQC6awcEv7gryA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/iab-native": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/iab-native/-/iab-native-1.0.0.tgz", + "integrity": "sha512-AxGYpKGRcyG5pbEAqj+ssxNwZAfxC0pRwyKc0MYoKjm0UeOoUNCWrZV0HGimcQii6ebe6MRqBQEeENyHM4qTdQ==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/iab-openrtb": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iab-openrtb/-/iab-openrtb-1.0.1.tgz", + "integrity": "sha512-egawJx6+pMh/6uA/hak1y+R2+XCSH2jxteSkWlY98/XdQQftaMUMllUFNMKrHwq9lgCI70Me06g4JCCnV6E62g==", + "license": "MIT", + "dependencies": { + "iab-adcom": "1.0.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -3726,6 +6455,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -3741,6 +6487,24 @@ "node": ">= 0.4" } }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.2.tgz", + "integrity": "sha512-a2xr4E3s1PjDS8ORcGgXpWx6V+liNs+O3JRD2mb9aeugD7rtkkZ0zgLdYgw0tWsKhsdiezGYptSiMlVazCBTuQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -3795,6 +6559,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", @@ -3845,7 +6621,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -3892,11 +6667,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3918,6 +6704,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", @@ -3942,7 +6737,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -3981,7 +6775,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -4004,6 +6797,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -4015,7 +6820,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -4163,6 +6967,18 @@ "dev": true, "license": "MIT" }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4170,6 +6986,21 @@ "dev": true, "license": "ISC" }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -4250,7 +7081,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -4293,6 +7123,84 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/karma": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", + "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.7.2", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-safarinative-launcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/karma-safarinative-launcher/-/karma-safarinative-launcher-1.1.0.tgz", + "integrity": "sha512-vdMjdQDHkSUbOZc8Zq2K5bBC0yJGFEgfrKRJTqt0Um0SC1Rt8drS2wcN6UA3h4LgsL3f1pMcmRSvKucbJE8Qdg==", + "license": "MIT", + "peerDependencies": { + "karma": ">=0.9" + } + }, + "node_modules/karma/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/karma/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4303,6 +7211,15 @@ "json-buffer": "3.0.1" } }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4317,6 +7234,28 @@ "node": ">= 0.8.0" } }, + "node_modules/live-connect-common": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/live-connect-common/-/live-connect-common-4.1.0.tgz", + "integrity": "sha512-sRklgbe13377aR+G0qCBiZPayQw5oZZozkuxKEoyipxscLbVzwe9gtA7CPpbmo6UcOdQxdCE6A7J1tI0wTSmqw==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/live-connect-js": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/live-connect-js/-/live-connect-js-7.2.0.tgz", + "integrity": "sha512-oZY4KqwrG1C+CDKApcsdDdMG4j2d44lhmvbNy4ZE6sPFr+W8R3m0+V+JxXB8p6tgSePJ8X/uhzAGos0lDg/MAg==", + "license": "Apache-2.0", + "dependencies": { + "live-connect-common": "^v4.1.0", + "tiny-hashes": "1.0.1" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4333,6 +7272,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4340,6 +7291,22 @@ "dev": true, "license": "MIT" }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "license": "Apache-2.0", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/lru-cache": { "version": "11.2.2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", @@ -4364,7 +7331,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4377,6 +7343,24 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4387,6 +7371,15 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -4401,6 +7394,39 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -4421,17 +7447,27 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -4453,19 +7489,58 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "license": "MIT" + }, + "node_modules/node.extend": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz", + "integrity": "sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ==", + "license": "(MIT OR GPL-2.0)", + "dependencies": { + "has": "^1.0.3", + "is": "^3.2.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, "node_modules/object-deep-merge": { "version": "2.0.0", @@ -4478,7 +7553,6 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4571,6 +7645,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4682,6 +7777,15 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4692,6 +7796,15 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4706,7 +7819,12 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, "node_modules/pathe": { @@ -4720,14 +7838,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -4736,6 +7852,21 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -4785,6 +7916,42 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prebid.js": { + "version": "10.20.0", + "resolved": "https://registry.npmjs.org/prebid.js/-/prebid.js-10.20.0.tgz", + "integrity": "sha512-k+tBZos6n9zPyb74B4UtQRJAd78gh0JZ56PUJaMcJ9c3mOM6LNaadLgeNp/o2pjEWYyGzYg48mf4L67bh/RL+g==", + "license": "Apache-2.0", + "dependencies": { + "@babel/core": "^7.28.4", + "@babel/plugin-transform-runtime": "^7.18.9", + "@babel/preset-env": "^7.27.2", + "@babel/preset-typescript": "^7.26.0", + "@babel/runtime": "^7.28.3", + "core-js": "^3.45.1", + "crypto-js": "^4.2.0", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "express": "^4.15.4", + "fun-hooks": "^1.1.0", + "gulp-babel": "^8.0.0", + "gulp-wrap": "^0.15.0", + "iab-adcom": "^1.0.6", + "iab-native": "^1.0.0", + "iab-openrtb": "^1.0.1", + "karma-safarinative-launcher": "^1.1.0", + "klona": "^2.0.6", + "live-connect-js": "^7.2.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + }, + "peerDependencies": { + "schema-utils": "^4.3.2" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4811,6 +7978,25 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4821,6 +8007,30 @@ "node": ">=6" } }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "license": "MIT", + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -4842,6 +8052,81 @@ ], "license": "MIT" }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -4865,6 +8150,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -4896,11 +8199,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, "node_modules/regjsparser": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.1.0" @@ -4909,16 +8234,39 @@ "regjsparser": "bin/parser" } }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, "node_modules/reserved-identifiers": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", @@ -4936,7 +8284,6 @@ "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.1", @@ -4974,6 +8321,28 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/rollup": { "version": "4.53.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", @@ -5060,6 +8429,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -5081,7 +8470,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -5099,33 +8487,152 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">=v12.22.7" + "node": ">=4" } }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" }, "engines": { - "node": ">=10" + "node": ">= 0.8.0" } }, "node_modules/set-function-length": { @@ -5177,6 +8684,12 @@ "node": ">= 0.4" } }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5204,7 +8717,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5224,7 +8736,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5241,7 +8752,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -5260,7 +8770,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -5283,6 +8792,56 @@ "dev": true, "license": "ISC" }, + "node_modules/socket.io": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", + "integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==", + "license": "MIT", + "dependencies": { + "debug": "~4.4.1", + "ws": "~8.18.3" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz", + "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5318,6 +8877,12 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -5325,6 +8890,15 @@ "dev": true, "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/std-env": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", @@ -5346,6 +8920,49 @@ "node": ">= 0.4" } }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "license": "MIT", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", @@ -5405,6 +9022,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -5458,7 +9087,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5474,6 +9102,22 @@ "dev": true, "license": "MIT" }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/tiny-hashes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tiny-hashes/-/tiny-hashes-1.0.1.tgz", + "integrity": "sha512-knIN5zj4fl7kW4EBU5sLP20DWUvi/rVouvJezV0UAym2DkQaqm365Nyc8F3QEiOvunNDMxR8UhcXd1d5g+Wg1g==", + "license": "MIT" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -5567,11 +9211,19 @@ "dev": true, "license": "MIT" }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -5597,6 +9249,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tough-cookie": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", @@ -5623,6 +9284,12 @@ "node": ">=20" } }, + "node_modules/tryit": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "integrity": "sha512-6C5h3CE+0qjGp+YKYTs74xR0k/Nw/ePtl/Lp6CCf44hqBQ66qnH1sDFR5mV/Gc48EsrHLB53lCFSffQCkka3kg==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -5662,6 +9329,19 @@ "node": ">= 0.8.0" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -5755,6 +9435,15 @@ "node": ">=14.17" } }, + "node_modules/typescript-compare": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", + "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==", + "license": "MIT", + "dependencies": { + "typescript-logic": "^0.0.0" + } + }, "node_modules/typescript-eslint": { "version": "8.47.0", "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.47.0.tgz", @@ -5779,6 +9468,47 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/typescript-logic": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz", + "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==", + "license": "MIT" + }, + "node_modules/typescript-tuple": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz", + "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==", + "license": "MIT", + "dependencies": { + "typescript-compare": "^0.0.2" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.41", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz", + "integrity": "sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -5802,14 +9532,70 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, "license": "MIT" }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -5846,6 +9632,56 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vinyl-bufferstream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vinyl-bufferstream/-/vinyl-bufferstream-1.0.1.tgz", + "integrity": "sha512-yCCIoTf26Q9SQ0L9cDSavSL7Nt6wgQw8TU1B/bb9b9Z4A3XTypXCGdc5BvXl4ObQvVY8JrDkFnWa/UqBqwM2IA==", + "dependencies": { + "bufferstreams": "1.0.1" + } + }, + "node_modules/vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", + "license": "ISC", + "dependencies": { + "source-map": "^0.5.1" + } + }, + "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/vite": { "version": "7.2.4", "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", @@ -6049,6 +9885,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -6241,11 +10086,33 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -6280,6 +10147,57 @@ "dev": true, "license": "MIT" }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/crates/js/lib/package.json b/crates/js/lib/package.json index 93164d3..d6b9316 100644 --- a/crates/js/lib/package.json +++ b/crates/js/lib/package.json @@ -6,7 +6,6 @@ "description": "Trusted Server tsjs TypeScript library with queue and simple banner rendering.", "scripts": { "build": "vite build", - "build:custom": "vite build", "dev": "vite build --watch", "test": "vitest run", "test:watch": "vitest", @@ -15,12 +14,16 @@ "format": "prettier --check \"**/*.{ts,tsx,js,json,css,md}\"", "format:write": "prettier --write \"**/*.{ts,tsx,js,json,css,md}\"" }, + "dependencies": { + "prebid.js": "^10.18.0" + }, "devDependencies": { "@eslint/js": "^9.13.0", "@types/jsdom": "^27.0.0", "@types/node": "^24.10.0", "@typescript-eslint/eslint-plugin": "^8.6.0", "@typescript-eslint/parser": "^8.6.0", + "baseline-browser-mapping": "^2.9.16", "eslint": "^9.10.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-import": "^2.29.1", diff --git a/crates/js/lib/src/core/config.ts b/crates/js/lib/src/core/config.ts index 918af65..b0fdbf9 100644 --- a/crates/js/lib/src/core/config.ts +++ b/crates/js/lib/src/core/config.ts @@ -1,9 +1,26 @@ // Global configuration storage for the tsjs runtime (mode, logging, etc.). import { log, LogLevel } from './log'; -import type { Config } from './types'; -import { RequestMode } from './types'; +import type { Config, GamConfig } from './types'; -let CONFIG: Config = { mode: RequestMode.Render }; +let CONFIG: Config = { mode: 'render' }; + +// Lazy import to avoid circular dependencies - GAM integration may not be present +let setGamConfigFn: ((cfg: GamConfig) => void) | null | undefined = undefined; + +function getSetGamConfig(): ((cfg: GamConfig) => void) | null { + if (setGamConfigFn === undefined) { + try { + // Dynamic import path - bundler will include if gam integration is present + // eslint-disable-next-line @typescript-eslint/no-require-imports + const gam = require('../integrations/gam/index'); + setGamConfigFn = gam.setGamConfig || null; + } catch { + // GAM integration not available + setGamConfigFn = null; + } + } + return setGamConfigFn ?? null; +} // Merge publisher-provided config and adjust the log level accordingly. export function setConfig(cfg: Config): void { @@ -12,6 +29,15 @@ export function setConfig(cfg: Config): void { const l = cfg.logLevel as LogLevel | undefined; if (typeof l === 'string') log.setLevel(l); else if (debugFlag === true) log.setLevel('debug'); + + // Forward GAM config to the GAM integration if present + if (cfg.gam) { + const setGam = getSetGamConfig(); + if (setGam) { + setGam(cfg.gam); + } + } + log.info('setConfig:', cfg); } diff --git a/crates/js/lib/src/core/global.d.ts b/crates/js/lib/src/core/global.d.ts index c7c8b08..ea91f97 100644 --- a/crates/js/lib/src/core/global.d.ts +++ b/crates/js/lib/src/core/global.d.ts @@ -3,7 +3,9 @@ import type { TsjsApi } from './types'; declare global { interface Window { tsjs?: TsjsApi; - pbjs?: TsjsApi; + // pbjs is Prebid.js which has its own types + // eslint-disable-next-line @typescript-eslint/no-explicit-any + pbjs?: any; } } diff --git a/crates/js/lib/src/core/index.ts b/crates/js/lib/src/core/index.ts index 227b1a3..41c8270 100644 --- a/crates/js/lib/src/core/index.ts +++ b/crates/js/lib/src/core/index.ts @@ -1,12 +1,8 @@ -// Public tsjs core bundle: sets up the global API, queue, and default methods. -export type { AdUnit, TsjsApi } from './types'; +// Public tsjs core bundle: sets up the global API. +export type { TsjsApi } from './types'; import type { TsjsApi } from './types'; -import { addAdUnits } from './registry'; -import { renderAdUnit, renderAllAdUnits } from './render'; import { log } from './log'; import { setConfig, getConfig } from './config'; -import { requestAds } from './request'; -import { installQueue } from './queue'; const VERSION = '0.1.0'; @@ -15,45 +11,12 @@ const w: Window & { tsjs?: TsjsApi } = tsjs?: TsjsApi; }) || ({} as Window & { tsjs?: TsjsApi }); -// Collect existing tsjs queued fns before we overwrite -const pending: Array<() => void> = Array.isArray(w.tsjs?.que) ? [...w.tsjs.que] : []; - // Create API and attach methods const api: TsjsApi = (w.tsjs ??= {} as TsjsApi); api.version = VERSION; -api.addAdUnits = addAdUnits; -api.renderAdUnit = renderAdUnit; -api.renderAllAdUnits = () => renderAllAdUnits(); api.log = log; api.setConfig = setConfig; api.getConfig = getConfig; -// Provide core requestAds API -api.requestAds = requestAds; -// Point global tsjs w.tsjs = api; -// Single shared queue -installQueue(api, w); - -// Flush prior queued callbacks -for (const fn of pending) { - try { - if (typeof fn === 'function') { - fn.call(api); - log.debug('queue: flushed callback'); - } - } catch { - /* ignore queued callback error */ - } -} - -log.info('tsjs initialized', { - methods: [ - 'setConfig', - 'getConfig', - 'requestAds', - 'addAdUnits', - 'renderAdUnit', - 'renderAllAdUnits', - ], -}); +log.info('tsjs initialized', { version: VERSION }); diff --git a/crates/js/lib/src/core/queue.ts b/crates/js/lib/src/core/queue.ts deleted file mode 100644 index 73c2741..0000000 --- a/crates/js/lib/src/core/queue.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Minimal Prebid-style queue shim that executes callbacks immediately. -import { log } from './log'; - -// Replace the legacy Prebid-style queue with an immediate executor so queued work runs in order. -export function installQueue void> }>( - target: T, - w: Window & { tsjs?: T } -) { - const q: Array<() => void> = []; - q.push = ((fn: () => void) => { - if (typeof fn === 'function') { - try { - fn.call(target); - log.debug('queue: push executed immediately'); - } catch { - /* ignore queued fn error */ - } - } - return q.length; - }) as typeof q.push; - target.que = q; - if (w.tsjs) w.tsjs.que = q; -} diff --git a/crates/js/lib/src/core/registry.ts b/crates/js/lib/src/core/registry.ts deleted file mode 100644 index ea77937..0000000 --- a/crates/js/lib/src/core/registry.ts +++ /dev/null @@ -1,36 +0,0 @@ -// In-memory registry for ad units registered via tsjs (used by core + extensions). -import type { AdUnit, Size } from './types'; -import { toArray } from './util'; -import { log } from './log'; - -const registry = new Map(); - -// Merge ad unit definitions into the in-memory registry (supports array or single unit). -export function addAdUnits(units: AdUnit | AdUnit[]): void { - for (const u of toArray(units)) { - if (!u || !u.code) continue; - registry.set(u.code, { ...registry.get(u.code), ...u }); - } - log.info('addAdUnits:', { count: toArray(units).length }); -} - -// Convenience helper to grab the first banner size off an ad unit. -export function firstSize(unit: AdUnit): Size | null { - const sizes = unit.mediaTypes?.banner?.sizes; - return sizes && sizes.length ? sizes[0] : null; -} - -// Return a snapshot array of all registered ad units. -export function getAllUnits(): AdUnit[] { - return Array.from(registry.values()); -} - -// Look up a unit by its code. -export function getUnit(code: string): AdUnit | undefined { - return registry.get(code); -} - -// Extract just the ad unit codes for quick iteration. -export function getAllCodes(): string[] { - return Array.from(registry.keys()); -} diff --git a/crates/js/lib/src/core/render.ts b/crates/js/lib/src/core/render.ts deleted file mode 100644 index 5464eb5..0000000 --- a/crates/js/lib/src/core/render.ts +++ /dev/null @@ -1,176 +0,0 @@ -// Rendering utilities for Trusted Server demo placements: find slots, seed placeholders, -// and inject creatives into sandboxed iframes. -import { log } from './log'; -import type { AdUnit } from './types'; -import { getUnit, getAllUnits, firstSize } from './registry'; -import NORMALIZE_CSS from './styles/normalize.css?inline'; -import IFRAME_TEMPLATE from './templates/iframe.html?raw'; - -function normalizeId(raw: string): string { - const s = String(raw ?? '').trim(); - return s.startsWith('#') ? s.slice(1) : s; -} - -// Locate an ad slot element by id, tolerating funky selectors provided by tag managers. -export function findSlot(id: string): HTMLElement | null { - const nid = normalizeId(id); - // Fast path - const byId = document.getElementById(nid) as HTMLElement | null; - if (byId) return byId; - // Fallback for odd IDs (special chars) or if provided with quotes/etc. - try { - const selector = `[id="${nid.replace(/"/g, '\\"')}"]`; - const byAttr = document.querySelector(selector) as HTMLElement | null; - if (byAttr) return byAttr; - } catch { - // Ignore selector errors (e.g., invalid characters) - } - return null; -} - -function ensureSlot(id: string): HTMLElement { - const nid = normalizeId(id); - let el = document.getElementById(nid) as HTMLElement | null; - if (el) return el; - el = document.createElement('div'); - el.id = nid; - const body: HTMLElement | null = typeof document !== 'undefined' ? document.body : null; - if (body && typeof body.appendChild === 'function') { - body.appendChild(el); - } else { - // DOM not ready — attach once available - const element = el; - const onReady = () => { - const readyBody = document.body; - if (readyBody && !document.getElementById(nid) && element) readyBody.appendChild(element); - }; - document.addEventListener('DOMContentLoaded', onReady, { once: true }); - } - return el; -} - -// Drop a placeholder message into the slot so pages don't sit empty pre-render. -export function renderAdUnit(codeOrUnit: string | AdUnit): void { - const code = typeof codeOrUnit === 'string' ? codeOrUnit : codeOrUnit?.code; - if (!code) return; - const unit = typeof codeOrUnit === 'string' ? getUnit(code) : codeOrUnit; - const size = (unit && firstSize(unit)) || [300, 250]; - const el = ensureSlot(code); - try { - el.textContent = `Trusted Server — ${size[0]}x${size[1]}`; - log.info('renderAdUnit: rendered placeholder', { code, size }); - } catch { - log.warn('renderAdUnit: failed', { code }); - } -} - -// Render placeholders for every registered ad unit (used in simple publisher demos). -export function renderAllAdUnits(): void { - try { - const parentReady = - typeof document !== 'undefined' && (document.body || document.documentElement); - if (!parentReady) { - log.warn('renderAllAdUnits: DOM not ready; skipping'); - return; - } - const units = getAllUnits(); - for (const u of units) { - renderAdUnit(u); - } - log.info('renderAllAdUnits: rendered all placeholders', { count: units.length }); - } catch (e) { - log.warn('renderAllAdUnits: failed', e as unknown); - } -} - -// Swap the slot contents for a creative iframe and write HTML into it safely. -export function renderCreativeIntoSlot(slotId: string, html: string): void { - const el = findSlot(slotId); - if (!el) { - log.warn('renderCreativeIntoSlot: slot not found; skipping render', { slotId }); - return; - } - try { - // Clear previous content - el.innerHTML = ''; - // Determine size if available - const unit = getUnit(slotId); - const sz = (unit && firstSize(unit)) || [300, 250]; - const iframe = createAdIframe(el, { - name: `tsjs_iframe_${slotId}`, - title: 'Ad content', - width: sz[0], - height: sz[1], - }); - writeHtmlToIframe(iframe, html); - log.info('renderCreativeIntoSlot: rendered', { slotId, width: sz[0], height: sz[1] }); - } catch (err) { - log.warn('renderCreativeIntoSlot: failed', { slotId, err }); - } -} - -type IframeOptions = { name?: string; title?: string; width?: number; height?: number }; - -// Construct a sandboxed iframe sized for the ad so we can render arbitrary HTML. -export function createAdIframe( - container: HTMLElement, - opts: IframeOptions = {} -): HTMLIFrameElement { - const iframe = document.createElement('iframe'); - // Attributes - iframe.scrolling = 'no'; - iframe.frameBorder = '0'; - iframe.setAttribute('marginwidth', '0'); - iframe.setAttribute('marginheight', '0'); - if (opts.name) iframe.name = String(opts.name); - iframe.title = opts.title || 'Ad content'; - iframe.setAttribute('aria-label', 'Advertisement'); - // Sandbox permissions for creatives - try { - iframe.sandbox.add( - 'allow-forms', - 'allow-popups', - 'allow-popups-to-escape-sandbox', - 'allow-same-origin', - 'allow-scripts', - 'allow-top-navigation-by-user-activation' - ); - } catch (err) { - log.debug('createAdIframe: sandbox add failed', err); - } - // Sizing + style - const w = Math.max(0, Number(opts.width ?? 0) | 0); - const h = Math.max(0, Number(opts.height ?? 0) | 0); - if (w > 0) iframe.width = String(w); - if (h > 0) iframe.height = String(h); - const s = iframe.style; - s.setProperty('border', '0'); - s.setProperty('margin', '0'); - s.setProperty('overflow', 'hidden'); - s.setProperty('display', 'block'); - if (w > 0) s.setProperty('width', `${w}px`); - if (h > 0) s.setProperty('height', `${h}px`); - // Insert into container - container.appendChild(iframe); - return iframe; -} - -function writeHtmlToIframe(iframe: HTMLIFrameElement, creativeHtml: string): void { - try { - const doc = (iframe.contentDocument || iframe.contentWindow?.document) as Document | undefined; - if (!doc) return; - const html = buildIframeDocument(creativeHtml); - doc.open(); - doc.write(html); - doc.close(); - } catch (err) { - log.warn('renderCreativeIntoSlot: iframe write failed', { err }); - } -} - -function buildIframeDocument(creativeHtml: string): string { - return IFRAME_TEMPLATE.replace('%NORMALIZE_CSS%', NORMALIZE_CSS).replace( - '%CREATIVE_HTML%', - creativeHtml - ); -} diff --git a/crates/js/lib/src/core/request.ts b/crates/js/lib/src/core/request.ts deleted file mode 100644 index 03799af..0000000 --- a/crates/js/lib/src/core/request.ts +++ /dev/null @@ -1,166 +0,0 @@ -// Request orchestration for tsjs: fires render (iframe) or auction (JSON) requests. -import { delay } from '../shared/async'; - -import { log } from './log'; -import { getAllUnits, firstSize } from './registry'; -import { renderCreativeIntoSlot, renderAllAdUnits, createAdIframe, findSlot } from './render'; -import { getConfig } from './config'; -import { RequestMode } from './types'; -import type { RequestAdsCallback, RequestAdsOptions } from './types'; - -// getHighestCpmBids is provided by the Prebid extension (shim) to mirror Prebid's API - -// Entry point matching Prebid's requestBids signature; decides render/auction mode. -export function requestAds( - callbackOrOpts?: RequestAdsCallback | RequestAdsOptions, - maybeOpts?: RequestAdsOptions -): void { - let callback: RequestAdsCallback | undefined; - let opts: RequestAdsOptions | undefined; - if (typeof callbackOrOpts === 'function') { - callback = callbackOrOpts as RequestAdsCallback; - opts = maybeOpts; - } else { - opts = callbackOrOpts as RequestAdsOptions | undefined; - callback = opts?.bidsBackHandler; - } - - const mode = resolveRequestMode(getConfig().mode); - log.info('requestAds: called', { hasCallback: typeof callback === 'function', mode }); - try { - const adUnits = getAllUnits(); - const payload = { adUnits, config: {} }; - log.debug('requestAds: payload', { units: adUnits.length }); - if (mode === RequestMode.Render) void requestAdsRender(adUnits); - else requestAdsAuction(payload); - // Synchronously invoke callback to match test expectations - try { - if (callback) callback(); - } catch { - /* ignore callback errors */ - } - // network handled in requestAdsAuction; no-op here - } catch { - log.warn('requestAds: failed to initiate'); - } -} - -function resolveRequestMode(mode: unknown): RequestMode { - if (mode === RequestMode.Render || mode === RequestMode.Auction) { - return mode; - } - if (mode !== undefined) { - log.warn('requestAds: invalid mode; defaulting to render', { mode }); - } - return RequestMode.Render; -} - -// Create per-slot iframe requests served directly from the edge via /ad/render. -async function requestAdsRender(adUnits: ReadonlyArray<{ code: string }>) { - for (const unit of adUnits) { - const size = (firstSize(unit) ?? [300, 250]) as readonly [number, number]; - const slotId = unit.code; - - const attemptInsert = async (attemptsRemaining: number): Promise => { - const container = findSlot(slotId) as HTMLElement | null; - if (container) { - const iframe = createAdIframe(container, { - name: `tsjs_iframe_${slotId}`, - title: 'Ad content', - width: size[0], - height: size[1], - }); - iframe.src = `/ad/render?slot=${encodeURIComponent(slotId)}&w=${encodeURIComponent(String(size[0]))}&h=${encodeURIComponent(String(size[1]))}`; - return; - } - - if (attemptsRemaining <= 0) { - log.warn('requestAds(render): slot not found; skipping iframe', { slotId }); - return; - } - - if (typeof document !== 'undefined' && document.readyState === 'loading') { - document.addEventListener( - 'DOMContentLoaded', - () => { - void attemptInsert(attemptsRemaining - 1); - }, - { once: true } - ); - return; - } - - await delay(50); - await attemptInsert(attemptsRemaining - 1); - }; - - void attemptInsert(10); - } -} - -// Fire a JSON POST to /ad/auction and render returned creatives. -function requestAdsAuction(payload: { adUnits: unknown[]; config: unknown }) { - // Render simple placeholders immediately so pages have content - renderAllAdUnits(); - if (typeof fetch !== 'function') { - log.warn('requestAds: fetch not available; nothing to render'); - return; - } - log.info('requestAds: sending request to /ad/auction', { - units: (payload.adUnits || []).length, - }); - void fetch('/ad/auction', { - method: 'POST', - headers: { 'content-type': 'application/json' }, - credentials: 'same-origin', - body: JSON.stringify(payload), - keepalive: true, - }) - .then(async (res) => { - log.debug('requestAds: sent'); - try { - const ct = res.headers.get('content-type') || ''; - if (res.ok && ct.includes('application/json')) { - const data: unknown = await res.json(); - for (const bid of parseSeatBids(data)) { - if (bid.impid && bid.adm) renderCreativeIntoSlot(String(bid.impid), bid.adm); - } - log.info('requestAds: rendered creatives from response'); - return; - } - log.warn('requestAds: unexpected response', { ok: res.ok, status: res.status, ct }); - } catch (err) { - log.warn('requestAds: failed to process response', err); - } - }) - .catch((e) => { - log.warn('requestAds: failed', e); - }); -} - -// Local minimal OpenRTB typing to keep core decoupled from Prebid extension types -type RtBid = { impid?: string; adm?: string }; -type RtSeatBid = { bid?: RtBid[] | null }; -type RtResponse = { seatbid?: RtSeatBid[] | null }; - -function isSeatBidArray(x: unknown): x is RtSeatBid[] { - return Array.isArray(x); -} - -// Minimal OpenRTB seatbid parser—just enough to render adm by impid. -function parseSeatBids(data: unknown): RtBid[] { - const out: RtBid[] = []; - const resp = data as Partial; - const seatbids = resp && resp.seatbid; - if (!seatbids || !isSeatBidArray(seatbids)) return out; - for (const sb of seatbids) { - const bids = sb && sb.bid; - if (!Array.isArray(bids)) continue; - for (const b of bids) { - const impid = typeof b?.impid === 'string' ? b!.impid : undefined; - const adm = typeof b?.adm === 'string' ? b!.adm : undefined; - out.push({ impid, adm }); - } - } - return out; -} diff --git a/crates/js/lib/src/core/styles/normalize.css b/crates/js/lib/src/core/styles/normalize.css deleted file mode 100644 index da77bad..0000000 --- a/crates/js/lib/src/core/styles/normalize.css +++ /dev/null @@ -1,169 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ -button, -hr, -input { - overflow: visible; -} - -progress, -sub, -sup { - vertical-align: baseline; -} - -[type='checkbox'], -[type='radio'], -legend { - box-sizing: border-box; - padding: 0; -} - -html { - line-height: 1.15; - -webkit-text-size-adjust: 100%; -} - -body { - margin: 0; -} - -details, -main { - display: block; -} - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -hr { - box-sizing: content-box; - height: 0; -} - -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} - -a { - background-color: transparent; -} - -abbr[title] { - border-bottom: none; - text-decoration: underline dotted; -} - -b, -strong { - font-weight: bolder; -} - -small { - font-size: 80%; -} - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -img { - border-style: none; -} - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - font-size: 100%; - line-height: 1.15; - margin: 0; -} - -button, -select { - text-transform: none; -} - -[type='button'], -[type='reset'], -[type='submit'], -button { - -webkit-appearance: button; -} - -[type='button']::-moz-focus-inner, -[type='reset']::-moz-focus-inner, -[type='submit']::-moz-focus-inner, -button::-moz-focus-inner { - border-style: none; - padding: 0; -} - -[type='button']:-moz-focusring, -[type='reset']:-moz-focusring, -[type='submit']:-moz-focusring, -button:-moz-focusring { - outline: 1px dotted ButtonText; -} - -fieldset { - padding: 0.35em 0.75em 0.625em; -} - -legend { - color: inherit; - display: table; - max-width: 100%; - white-space: normal; -} - -textarea { - overflow: auto; -} - -[type='number']::-webkit-inner-spin-button, -[type='number']::-webkit-outer-spin-button { - height: auto; -} - -[type='search'] { - -webkit-appearance: textfield; - outline-offset: -2px; -} - -[type='search']::-webkit-search-decoration { - -webkit-appearance: none; -} - -::-webkit-file-upload-button { - -webkit-appearance: button; - font: inherit; -} - -summary { - display: list-item; -} - -[hidden], -template { - display: none; -} diff --git a/crates/js/lib/src/core/templates/iframe.html b/crates/js/lib/src/core/templates/iframe.html deleted file mode 100644 index 55af0d5..0000000 --- a/crates/js/lib/src/core/templates/iframe.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - %CREATIVE_HTML% - diff --git a/crates/js/lib/src/core/types.ts b/crates/js/lib/src/core/types.ts index e65641a..acbbc6a 100644 --- a/crates/js/lib/src/core/types.ts +++ b/crates/js/lib/src/core/types.ts @@ -1,75 +1,37 @@ -// Shared TypeScript types for the tsjs core API and extensions. -export type Size = readonly [number, number]; +// Shared TypeScript types for the tsjs core API. -export interface Banner { - sizes: ReadonlyArray; -} +export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug'; -export interface MediaTypes { - banner?: Banner; +export interface GamConfig { + /** Enable the GAM interceptor. Defaults to false. */ + enabled?: boolean; + /** Only intercept bids from these bidders. Empty array = all bidders. */ + bidders?: string[]; + /** Force render Prebid creative even if GAM returned a line item. Defaults to false. */ + forceRender?: boolean; } -export interface AdUnit { - code: string; - mediaTypes?: MediaTypes; +export interface Config { + debug?: boolean; + logLevel?: LogLevel; + /** Select ad serving mode: 'render' or 'auction'. */ + mode?: 'render' | 'auction'; + /** GAM interceptor configuration. */ + gam?: GamConfig; + // Extendable for future fields + [key: string]: unknown; } export interface TsjsApi { version: string; - que: Array<() => void>; - addAdUnits(units: AdUnit | AdUnit[]): void; - renderAdUnit(codeOrUnit: string | AdUnit): void; - renderAllAdUnits(): void; - setConfig?(cfg: Config): void; - getConfig?(): Config; - // Core API: requestAds; accepts same signatures as Prebid's requestBids - requestAds?(opts?: RequestAdsOptions): void; - requestAds?(callback: RequestAdsCallback, opts?: RequestAdsOptions): void; - getHighestCpmBids?(adUnitCodes?: string | string[]): ReadonlyArray; - log?: { - setLevel(l: 'silent' | 'error' | 'warn' | 'info' | 'debug'): void; - getLevel(): 'silent' | 'error' | 'warn' | 'info' | 'debug'; + setConfig(cfg: Config): void; + getConfig(): Config; + log: { + setLevel(l: LogLevel): void; + getLevel(): LogLevel; info(...args: unknown[]): void; warn(...args: unknown[]): void; error(...args: unknown[]): void; debug(...args: unknown[]): void; }; } - -export enum RequestMode { - Render = 'render', - Auction = 'auction', -} - -export interface Config { - debug?: boolean; - logLevel?: 'silent' | 'error' | 'warn' | 'info' | 'debug'; - /** Select ad serving mode. Default is RequestMode.Render. */ - mode?: RequestMode; - // Extendable for future fields - [key: string]: unknown; -} - -// Core-neutral request types -export type RequestAdsCallback = () => void; -export interface RequestAdsOptions { - bidsBackHandler?: RequestAdsCallback; - timeout?: number; -} - -// Back-compat aliases for Prebid-style naming (used by the extension shim) -export type RequestBidsCallback = RequestAdsCallback; - -export interface HighestCpmBid { - adUnitCode: string; - width: number; - height: number; - cpm: number; - currency: string; - bidderCode: string; - creativeId: string; - adserverTargeting: Record; -} - -// Minimal OpenRTB response typing -// OpenRTB response typing is specific to the Prebid extension and lives in src/ext/types.ts diff --git a/crates/js/lib/src/core/util.ts b/crates/js/lib/src/core/util.ts deleted file mode 100644 index be9a855..0000000 --- a/crates/js/lib/src/core/util.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Tiny shared helpers used across core modules. -export function isArray(v: unknown): v is T[] { - return Array.isArray(v); -} - -// Normalise a single value into an array for simple iteration helpers. -export function toArray(v: T | T[]): T[] { - return isArray(v) ? v : [v]; -} diff --git a/crates/js/lib/src/index.ts b/crates/js/lib/src/index.ts index bfe6e69..9a5a505 100644 --- a/crates/js/lib/src/index.ts +++ b/crates/js/lib/src/index.ts @@ -35,7 +35,7 @@ for (const [moduleName, moduleExports] of Object.entries(modules)) { } // Re-export core types for convenience -export type { AdUnit, TsjsApi } from './core/types'; +export type { TsjsApi } from './core/types'; // Export the modules object for advanced use cases export { modules }; diff --git a/crates/js/lib/src/integrations/ext/index.ts b/crates/js/lib/src/integrations/ext/index.ts deleted file mode 100644 index 2cf7971..0000000 --- a/crates/js/lib/src/integrations/ext/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { installPrebidJsShim } from './prebidjs'; - -// Execute immediately on import; safe no-op if pbjs is not present. -void installPrebidJsShim(); diff --git a/crates/js/lib/src/integrations/ext/prebidjs.ts b/crates/js/lib/src/integrations/ext/prebidjs.ts deleted file mode 100644 index 242ebc0..0000000 --- a/crates/js/lib/src/integrations/ext/prebidjs.ts +++ /dev/null @@ -1,121 +0,0 @@ -// Prebid.js compatibility shim: exposes tsjs API through the legacy pbjs global. -import type { - TsjsApi, - HighestCpmBid, - RequestAdsCallback, - RequestAdsOptions, -} from '../../core/types'; -import { log } from '../../core/log'; -import { installQueue } from '../../core/queue'; -import { getAllCodes, getAllUnits, firstSize } from '../../core/registry'; -import { resolvePrebidWindow, PrebidWindow } from '../../shared/globals'; -type RequestBidsFunction = ( - callbackOrOpts?: RequestAdsCallback | RequestAdsOptions, - opts?: RequestAdsOptions -) => void; - -/** - * Shim implementation for pbjs.getHighestCpmBids that returns synthetic - * placeholder bids derived from the registered core ad units. - */ -function getHighestCpmBidsShim(adUnitCodes?: string | string[]): ReadonlyArray { - const codes: string[] = - typeof adUnitCodes === 'string' ? [adUnitCodes] : (adUnitCodes ?? getAllCodes()); - const results: HighestCpmBid[] = []; - for (const code of codes) { - const unit = getAllUnits().find((u) => u.code === code); - if (!unit) continue; - const size = (firstSize(unit) ?? [300, 250]) as readonly [number, number]; - results.push({ - adUnitCode: code, - width: size[0], - height: size[1], - cpm: 0, - currency: 'USD', - bidderCode: 'tsjs', - creativeId: 'tsjs-placeholder', - adserverTargeting: {}, - }); - } - return results; -} - -/** - * Shim implementation for pbjs.requestBids that forwards to core requestAds. - */ -function requestBidsShim(api: TsjsApi): RequestBidsFunction { - return (callbackOrOpts?: RequestAdsCallback | RequestAdsOptions, opts?: RequestAdsOptions) => { - const requestAds = api.requestAds as - | ((options?: RequestAdsOptions) => void) - | ((callback: RequestAdsCallback, options?: RequestAdsOptions) => void) - | undefined; - if (!requestAds) return; - if (typeof callbackOrOpts === 'function') { - requestAds(callbackOrOpts, opts); - } else { - requestAds(callbackOrOpts); - } - }; -} - -// Guarantee a tsjs API stub exists so we can alias pbjs onto it. -function ensureTsjsApi(win: PrebidWindow): TsjsApi { - if (win.tsjs) return win.tsjs; - const stub: TsjsApi = { - version: '0.0.0', - que: [], - addAdUnits: () => undefined, - renderAdUnit: () => undefined, - renderAllAdUnits: () => undefined, - }; - win.tsjs = stub; - return stub; -} - -// Bridge the minimal tsjs API onto the legacy pbjs global so existing tags keep working. -export function installPrebidJsShim(): boolean { - const w = resolvePrebidWindow(); - - // Ensure core exists - const api = ensureTsjsApi(w); - - // Capture any queued pbjs callbacks before aliasing - const pending: Array<() => void> = Array.isArray(w.pbjs?.que) ? [...(w.pbjs?.que ?? [])] : []; - - // Core provides requestAds/getHighestCpmBids; extension aliases pbjs and shims requestBids → requestAds - - // Alias pbjs to tsjs and ensure a single shared queue - w.pbjs = api; - if (!Array.isArray(api.que)) { - installQueue(api, w); - } - const pbjsApi = w.pbjs as TsjsApi & { requestBids?: RequestBidsFunction }; - // Make sure both globals share the same queue - if (Array.isArray(api.que)) { - pbjsApi.que = api.que; - } - // Shim Prebid-style API surface - pbjsApi.requestBids = requestBidsShim(api); - pbjsApi.getHighestCpmBids = getHighestCpmBidsShim; - - // Flush previously queued pbjs callbacks - for (const fn of pending) { - try { - if (typeof fn === 'function') { - fn.call(api); - log.debug('prebidjs extension: flushed callback'); - } - } catch (err) { - log.debug('prebidjs extension: queued callback failed', err); - } - } - - log.info('prebidjs extension installed', { - hasRequestBids: typeof pbjsApi.requestBids === 'function', - hasGetHighestCpmBids: typeof pbjsApi.getHighestCpmBids === 'function', - }); - - return true; -} - -export default installPrebidJsShim; diff --git a/crates/js/lib/src/integrations/ext/types.ts b/crates/js/lib/src/integrations/ext/types.ts deleted file mode 100644 index a83a499..0000000 --- a/crates/js/lib/src/integrations/ext/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Minimal OpenRTB response typing (used by the Prebid extension) -export interface OpenRtbBid { - impid?: string; - adm?: string; - [key: string]: unknown; -} -export interface OpenRtbSeatBid { - bid?: OpenRtbBid[] | null; -} -export interface OpenRtbBidResponse { - seatbid?: OpenRtbSeatBid[] | null; -} diff --git a/crates/js/lib/src/integrations/gam/index.ts b/crates/js/lib/src/integrations/gam/index.ts new file mode 100644 index 0000000..2832067 --- /dev/null +++ b/crates/js/lib/src/integrations/gam/index.ts @@ -0,0 +1,396 @@ +// GAM (Google Ad Manager) Interceptor - forces Prebid creatives to render when +// GAM doesn't have matching line items configured. +// +// This integration intercepts GPT's slotRenderEnded event and replaces GAM's +// creative with the Prebid winning bid when: +// 1. A Prebid bid exists for the slot (hb_adid targeting is set) +// 2. The bid meets the configured criteria (specific bidder or any bidder) +// +// Configuration options: +// - enabled: boolean (default: false) - Master switch for the interceptor +// - bidders: string[] (default: []) - Only intercept for these bidders. Empty = all bidders +// - forceRender: boolean (default: false) - Render even if GAM has a line item +// +// Usage: +// window.tsGamConfig = { enabled: true, bidders: ['mocktioneer'] }; +// // or via tsjs.setConfig({ gam: { enabled: true, bidders: ['mocktioneer'] } }) + +import { log } from '../../core/log'; + +export interface TsGamConfig { + /** Enable the GAM interceptor. Defaults to false. */ + enabled?: boolean; + /** Only intercept bids from these bidders. Empty array = all bidders. */ + bidders?: string[]; + /** Force render Prebid creative even if GAM returned a line item. Defaults to false. */ + forceRender?: boolean; +} + +export interface TsGamApi { + setConfig(cfg: TsGamConfig): void; + getConfig(): TsGamConfig; + getStats(): GamInterceptStats; +} + +interface GamInterceptStats { + intercepted: number; + rendered: Array<{ + slotId: string; + adId: string; + bidder: string; + method: string; + timestamp: number; + }>; +} + +type GamWindow = Window & { + googletag?: { + pubads?: () => { + addEventListener: (event: string, callback: (e: SlotRenderEndedEvent) => void) => void; + getSlots?: () => GptSlot[]; + }; + }; + pbjs?: { + getBidResponsesForAdUnitCode?: (code: string) => { bids?: PrebidBid[] }; + renderAd?: (doc: Document, adId: string) => void; + }; + tsGamConfig?: TsGamConfig; + __tsGamInstalled?: boolean; +}; + +interface SlotRenderEndedEvent { + slot: GptSlot; + isEmpty: boolean; + lineItemId: number | null; +} + +interface GptSlot { + getSlotElementId(): string; + getTargeting(key: string): string[]; + getTargetingKeys(): string[]; +} + +interface PrebidBid { + adId?: string; + ad?: string; + adUrl?: string; + bidder?: string; + cpm?: number; +} + +interface IframeAttrs { + src: string; + width?: string; + height?: string; +} + +/** + * Extract iframe attributes from a creative that is just an iframe wrapper. + * Returns null if the creative is not a simple iframe tag. + * Exported for testing. + */ +export function extractIframeAttrs(html: string): IframeAttrs | null { + const trimmed = html.trim(); + // Check if it's a simple iframe tag (possibly with whitespace/newline after) + if (!trimmed.toLowerCase().startsWith(''; + expect(extractIframeSrc(html)).toBe('/first-party/proxy?tsurl=https://example.com'); + }); + + it('handles trailing newline (mocktioneer style)', async () => { + const { extractIframeSrc } = await import('../../../src/integrations/gam/index'); + + const html = + '\n'; + expect(extractIframeSrc(html)).toBe( + '/first-party/proxy?tsurl=https%3A%2F%2Flocal.mocktioneer.com' + ); + }); + + it('returns null for non-iframe content', async () => { + const { extractIframeSrc } = await import('../../../src/integrations/gam/index'); + + expect(extractIframeSrc('
not an iframe
')).toBeNull(); + expect(extractIframeSrc('')).toBeNull(); + }); + + it('returns null for iframe without src', async () => { + const { extractIframeSrc } = await import('../../../src/integrations/gam/index'); + + expect(extractIframeSrc('')).toBeNull(); + }); + + it('returns null for complex content with iframe', async () => { + const { extractIframeSrc } = await import('../../../src/integrations/gam/index'); + + expect(extractIframeSrc('
')).toBeNull(); + }); + }); +}); diff --git a/crates/js/lib/vite.config.ts b/crates/js/lib/vite.config.ts index 8b9ed86..a6cc700 100644 --- a/crates/js/lib/vite.config.ts +++ b/crates/js/lib/vite.config.ts @@ -84,8 +84,6 @@ export type ModuleName = ${finalModules.map((m) => `'${m}'`).join(' | ')}; export default defineConfig(() => { const distDir = path.resolve(__dirname, '../dist'); - const buildTimestamp = new Date().toISOString(); - const banner = `// build: ${buildTimestamp}\n`; return { build: { @@ -93,11 +91,11 @@ export default defineConfig(() => { outDir: distDir, assetsDir: '.', sourcemap: false, - minify: 'esbuild', + minify: 'esbuild' as const, rollupOptions: { input: path.resolve(__dirname, 'src/index.ts'), output: { - format: 'iife', + format: 'iife' as const, dir: distDir, entryFileNames: 'tsjs-unified.js', inlineDynamicImports: true, diff --git a/crates/js/src/bundle.rs b/crates/js/src/bundle.rs index ef29249..60daa6e 100644 --- a/crates/js/src/bundle.rs +++ b/crates/js/src/bundle.rs @@ -43,7 +43,8 @@ impl TsjsBundle { } } - pub(crate) const fn bundle(self) -> &'static str { + /// Returns the JavaScript bundle content. + pub const fn bundle(self) -> &'static str { METAS[self as usize].bundle } diff --git a/docs/guide/api-reference.md b/docs/guide/api-reference.md index 535ab49..1bd35ec 100644 --- a/docs/guide/api-reference.md +++ b/docs/guide/api-reference.md @@ -46,23 +46,34 @@ curl "https://edge.example.com/ad/render?slot=header-banner&w=728&h=90" ### POST /ad/auction -Client-side auction endpoint for TSJS library. +OpenRTB auction endpoint for client-side integrations (for example, Prebid.js S2S). **Request Body:** ```json { - "adUnits": [ + "id": "auction-1", + "imp": [ { - "code": "header-banner", - "mediaTypes": { - "banner": { - "sizes": [[728, 90], [970, 250]] + "id": "header-banner", + "banner": { + "format": [ + { "w": 728, "h": 90 }, + { "w": 970, "h": 250 } + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": {}, + "rubicon": {} + } } } } ], - "config": { - "debug": false + "site": { + "domain": "example.com", + "page": "https://example.com" } } ``` @@ -90,7 +101,15 @@ Client-side auction endpoint for TSJS library. ```bash curl -X POST https://edge.example.com/ad/auction \ -H "Content-Type: application/json" \ - -d '{"adUnits":[{"code":"banner","mediaTypes":{"banner":{"sizes":[[300,250]]}}}]}' + -d '{ + "id": "auction-1", + "imp": [{ + "id": "banner", + "banner": { "format": [{ "w": 300, "h": 250 }] }, + "ext": { "prebid": { "bidder": { "appnexus": {} } } } + }], + "site": { "domain": "example.com", "page": "https://example.com" } + }' ``` --- diff --git a/docs/guide/configuration-reference.md b/docs/guide/configuration-reference.md index 3366973..8370187 100644 --- a/docs/guide/configuration-reference.md +++ b/docs/guide/configuration-reference.md @@ -598,7 +598,7 @@ All integrations support: | `timeout_ms` | Integer | `1000` | Request timeout in milliseconds | | `bidders` | Array[String] | `[]` | List of enabled bidders | | `debug` | Boolean | `false` | Enable debug logging | -| `mode` | String | None | Default TSJS request mode when Prebid is enabled (`render` or `auction`) | +| `mode` | String | None | Default TSJS request mode when Prebid is enabled (`render` or `auction`); `auction` expects OpenRTB clients (for example, Prebid.js) calling `/ad/auction` | | `script_patterns` | Array[String] | See below | Patterns for removing Prebid script tags and intercepting requests | **Default `script_patterns`**: @@ -616,7 +616,7 @@ server_url = "https://prebid-server.example/openrtb2/auction" timeout_ms = 1200 bidders = ["kargo", "rubicon", "appnexus", "openx"] debug = false -mode = "auction" +mode = "auction" # OpenRTB clients (for example, Prebid.js) # script_patterns = ["/static/prebid/*"] # Optional: restrict to specific path ``` diff --git a/docs/guide/environment-variables.md b/docs/guide/environment-variables.md index 7162d04..5784a78 100644 --- a/docs/guide/environment-variables.md +++ b/docs/guide/environment-variables.md @@ -205,6 +205,7 @@ TRUSTED_SERVER__INTEGRATIONS__PREBID__BIDDERS="appnexus,rubicon,openx" TRUSTED_SERVER__INTEGRATIONS__PREBID__DEBUG=false # Default tsjs mode when Prebid integration is enabled (optional) +# "auction" expects OpenRTB clients (for example, Prebid.js) calling /ad/auction TRUSTED_SERVER__INTEGRATIONS__PREBID__MODE="auction" # or "render" # Script patterns to remove Prebid tags and serve empty JS (indexed format) @@ -223,12 +224,49 @@ server_url = "https://prebid-server.example.com" timeout_ms = 1000 bidders = ["appnexus", "rubicon", "openx"] debug = false -mode = "auction" +mode = "auction" # OpenRTB clients (for example, Prebid.js) script_patterns = ["/prebid.js", "/prebid.min.js", "/prebidjs.js", "/prebidjs.min.js"] ``` --- +## GAM Integration + +The GAM (Google Ad Manager) interceptor forces Prebid creatives to render when GAM +doesn't have matching line items configured. This is useful for testing and development. + +```bash +# Enable GAM interceptor +TRUSTED_SERVER__INTEGRATIONS__GAM__ENABLED=true + +# Only intercept specific bidders (comma-separated, empty = all bidders) +TRUSTED_SERVER__INTEGRATIONS__GAM__BIDDERS="mocktioneer,appnexus" + +# Force render even if GAM returned a line item (default: false) +TRUSTED_SERVER__INTEGRATIONS__GAM__FORCE_RENDER=false +``` + +**TOML Equivalent:** +```toml +[integrations.gam] +enabled = true +bidders = ["mocktioneer"] +force_render = false +``` + +**How it works:** +1. When a GPT slot renders, the interceptor checks if there's a Prebid bid (`hb_adid` targeting) +2. If `bidders` is set, only those bidders' creatives are intercepted +3. If `force_render` is false (default), only renders when GAM has no matching line item +4. If `force_render` is true, always renders Prebid creative regardless of GAM + +**Use cases:** +- **Development/Testing**: Render mocktioneer test ads without GAM line items +- **A/B Testing**: Force Prebid creatives for specific bidders +- **Fallback**: Use Prebid as fallback when GAM has no matching ads + +--- + ## Next.js Integration ```bash @@ -647,6 +685,11 @@ export TRUSTED_SERVER__INTEGRATIONS__PREBID__SERVER_URL="https://prebid-server.c export TRUSTED_SERVER__INTEGRATIONS__PREBID__TIMEOUT_MS=2000 export TRUSTED_SERVER__INTEGRATIONS__PREBID__BIDDERS="appnexus,rubicon,openx" +# Optional: GAM Interceptor (forces Prebid creatives when GAM has no line items) +export TRUSTED_SERVER__INTEGRATIONS__GAM__ENABLED=true +export TRUSTED_SERVER__INTEGRATIONS__GAM__BIDDERS="mocktioneer" +export TRUSTED_SERVER__INTEGRATIONS__GAM__FORCE_RENDER=false + # Optional: Security Headers export TRUSTED_SERVER__RESPONSE_HEADERS__STRICT_TRANSPORT_SECURITY="max-age=31536000" export TRUSTED_SERVER__RESPONSE_HEADERS__X_CONTENT_TYPE_OPTIONS="nosniff" diff --git a/docs/guide/integration-guide.md b/docs/guide/integration-guide.md index d5fc921..1790d24 100644 --- a/docs/guide/integration-guide.md +++ b/docs/guide/integration-guide.md @@ -258,11 +258,11 @@ Two built-in integrations demonstrate how the framework pieces fit together: ### Prebid -**Purpose**: Production Prebid Server bridge that owns `/ad/render` & `/ad/auction`, injects synthetic IDs, rewrites creatives/notification URLs, and removes publisher-supplied Prebid scripts because the shim already ships in the unified TSJS build. +**Purpose**: Production Prebid Server bridge that owns `/ad/render` & `/ad/auction`, injects synthetic IDs, rewrites creatives/notification URLs, and removes publisher-supplied Prebid scripts because the Prebid.js bundle already ships in the unified TSJS build. **Key files**: - `crates/common/src/integrations/prebid.rs` - Rust implementation -- `crates/js/lib/src/ext/prebidjs.ts` - TypeScript shim +- `crates/js/lib/src/integrations/prebid/index.ts` - Prebid.js bundle integration #### Prebid Integration Details @@ -285,7 +285,7 @@ Tests or scaffolding can inject configs by calling `settings.integrations.insert **2. Routes Owned by the Integration** -`IntegrationProxy::routes` declares the `/ad/render` (GET) and `/ad/auction` (POST) endpoints. Both handlers share helpers that shape OpenRTB payloads, inject synthetic IDs + geo/request-signing context, forward requests via `ensure_backend_from_url`, and run the HTML creative rewrites before responding. These routes are intentionally un-namespaced to match the TSJS client. +`IntegrationProxy::routes` declares the `/ad/render` (GET) and `/ad/auction` (POST) endpoints. `/ad/render` builds OpenRTB from slot parameters, while `/ad/auction` accepts OpenRTB directly from the client. Both paths inject synthetic IDs + geo/request-signing context, forward requests via `ensure_backend_from_url`, and run the HTML creative rewrites before responding. These routes are intentionally un-namespaced to match client integrations. **3. HTML Rewrites Through the Registry** @@ -293,7 +293,7 @@ When the integration is enabled, the `IntegrationAttributeRewriter` removes any **4. TSJS Assets & Testing** -The shim implementation lives in `crates/js/lib/src/ext/prebidjs.ts`. Tests typically assert that publisher references disappear, relying on the html processor's unified bundle injection to deliver the shim. +The Prebid.js bundle integration lives in `crates/js/lib/src/integrations/prebid/index.ts`. Tests typically assert that publisher references disappear, relying on the html processor's unified bundle injection to deliver the bundle. Reusing these patterns makes it straightforward to convert additional legacy flows (for example, Next.js rewrites) into first-class integrations. diff --git a/docs/guide/integration_guide.md b/docs/guide/integration_guide.md index d756659..2045870 100644 --- a/docs/guide/integration_guide.md +++ b/docs/guide/integration_guide.md @@ -303,7 +303,7 @@ Two built-in integrations demonstrate how the framework pieces fit together: | Integration | Purpose | Key files | | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | `testlight` | Sample partner stub showing request proxying, attribute rewrites, and asset injection. | `crates/common/src/integrations/testlight.rs`, `crates/js/lib/src/integrations/testlight.ts` | -| `prebid` | Production Prebid Server bridge that owns `/ad/render` & `/ad/auction`, injects synthetic IDs, rewrites creatives/notification URLs, and removes publisher-supplied Prebid scripts because the shim already ships in the unified TSJS build. | `crates/common/src/integrations/prebid.rs`, `crates/js/lib/src/ext/prebidjs.ts` | +| `prebid` | Production Prebid Server bridge that owns `/ad/render` & `/ad/auction`, injects synthetic IDs, rewrites creatives/notification URLs, and removes publisher-supplied Prebid scripts because the Prebid.js bundle already ships in the unified TSJS build. | `crates/common/src/integrations/prebid.rs`, `crates/js/lib/src/integrations/prebid/index.ts` | ### Example: Prebid integration @@ -328,10 +328,11 @@ Prebid applies the same steps outlined above with a few notable patterns: other integrations use. 2. **Routes owned by the integration** – `IntegrationProxy::routes` declares the - `/ad/render` (GET) and `/ad/auction` (POST) endpoints. Both handlers share helpers that shape - OpenRTB payloads, inject synthetic IDs + geo/request-signing context, forward requests via - `ensure_backend_from_url`, and run the HTML creative rewrites before responding. These routes - are intentionally un-namespaced to match the TSJS client. + `/ad/render` (GET) and `/ad/auction` (POST) endpoints. `/ad/render` builds OpenRTB from slot + parameters, while `/ad/auction` accepts OpenRTB directly from the client. Both paths inject + synthetic IDs + geo/request-signing context, forward requests via `ensure_backend_from_url`, + and run the HTML creative rewrites before responding. These routes are intentionally + un-namespaced to match client integrations. 3. **HTML rewrites through the registry** – When the integration is enabled, the `IntegrationAttributeRewriter` removes any `