diff --git a/confidence-cloudflare-resolver/src/lib.rs b/confidence-cloudflare-resolver/src/lib.rs index 4eddedd87..5db136f87 100644 --- a/confidence-cloudflare-resolver/src/lib.rs +++ b/confidence-cloudflare-resolver/src/lib.rs @@ -1,7 +1,7 @@ mod materialization; use confidence_resolver::{ - apply_dedup::ApplyDedup, + apply_dedup::{ApplyDedup, ApplyDedupSnapshot}, assign_logger, flag_logger, proto::{confidence, google::Struct}, resolve_logger, @@ -51,6 +51,22 @@ thread_local! { static FLAG_LOG: RefCell> = const { RefCell::new(None) }; static APPLY_DEDUP: RefCell = RefCell::new(ApplyDedup::new(120, 100_000)); static APPLY_DEDUP_ENABLED: Cell = const { Cell::new(false) }; + static LAST_DEDUP_SNAPSHOT: RefCell = + RefCell::new(ApplyDedupSnapshot::default()); +} + +fn dedup_telemetry_delta() -> Option { + if !APPLY_DEDUP_ENABLED.with(|c| c.get()) { + return None; + } + let current = APPLY_DEDUP.with(|d| d.borrow().telemetry_snapshot()); + let prev = LAST_DEDUP_SNAPSHOT.with(|s| std::mem::replace(&mut *s.borrow_mut(), current.clone())); + let delta = current.to_proto_delta(&prev); + if delta.applies_total > 0 || current.map_size > 0 { + Some(delta) + } else { + None + } } /// Queues one request's flag log and sweeps the apply-dedup map. Called via @@ -497,6 +513,7 @@ pub async fn main(req: Request, env: Env, ctx: Context) -> Result { let mut td = telemetry::build_request_telemetry(elapsed_us, &reasons); td.sdk = Some(sdk_info()); + td.apply_dedup = dedup_telemetry_delta(); log.telemetry_data = Some(td); event_ctx.wait_until(queue_flag_log(log)); @@ -540,9 +557,10 @@ pub async fn main(req: Request, env: Env, ctx: Context) -> Result { Response::error(msg, 500)?.with_cors_headers(&allowed_origin) } }; - // Unlike resolve there is no telemetry to attach, so - // skip queueing when the apply logged nothing (an - // errored apply). + if let Some(dedup_delta) = dedup_telemetry_delta() { + let td = log.telemetry_data.get_or_insert_with(Default::default); + td.apply_dedup = Some(dedup_delta); + } if log != WriteFlagLogsRequest::default() { event_ctx.wait_until(queue_flag_log(log)); } @@ -704,11 +722,6 @@ async fn consume_flag_logs( let req = flag_logger::aggregate_batch(logs); - // Accumulate telemetry deltas into KV-backed cumulative snapshot for /metrics. - if let Ok(kv) = env.kv("CONFIDENCE_METRICS_KV") { - update_prometheus_kv(&kv, &req).await; - } - let client_secret = CONFIDENCE_CLIENT_SECRET.get().unwrap().as_str(); let account_id = CDN_STATE_REQUEST.account_id.as_str(); let destinations = &*LOG_DESTINATIONS; @@ -719,13 +732,15 @@ async fn consume_flag_logs( (destinations[0], None) }; - if let Err(reason) = deliver_flag_logs(client_secret, account_id, &req, primary).await { + let delivered = if let Err(reason) = + deliver_flag_logs(client_secret, account_id, &req, primary).await + { console_log!( "flag log delivery to {:?} failed ({}), trying fallback", primary, reason ); - let fallback_delivered = match fallback { + match fallback { Some(fb) => match deliver_flag_logs(client_secret, account_id, &req, fb).await { Ok(()) => true, Err(fb_reason) => { @@ -738,16 +753,19 @@ async fn consume_flag_logs( } }, None => false, - }; - if !fallback_delivered { - // Returning Err makes Cloudflare Queues redeliver the batch, - // so a delivery outage doesn't silently drop logs. The - // telemetry KV update above may run again on redelivery — - // acceptable for metrics. - return Err(worker::Error::RustError( - "flag log delivery failed on all destinations".to_string(), - )); } + } else { + true + }; + + if let Ok(kv) = env.kv("CONFIDENCE_METRICS_KV") { + update_kv_snapshot(&kv, req.telemetry_data.as_ref(), Some(delivered), None).await; + } + + if !delivered { + return Err(worker::Error::RustError( + "flag log delivery failed on all destinations".to_string(), + )); } } @@ -774,22 +792,51 @@ async fn deliver_flag_logs( } } -/// Accumulate telemetry deltas from all isolates into a cumulative -/// `TelemetrySnapshot` stored in KV, then write its Prometheus text -/// representation for the /metrics endpoint. +/// Single read-modify-write of the KV-backed cumulative telemetry snapshot. /// -/// Note: concurrent queue consumer invocations can race on KV read-modify-write. -/// Acceptable for metrics — at worst one batch's deltas are lost, not cumulative state. -async fn update_prometheus_kv(kv: &kv::KvStore, req: &WriteFlagLogsRequest) { +/// Merges flag-log telemetry deltas, flush delivery results, and event +/// delivery results into one KV update — avoiding cross-queue races that +/// would occur if flag-log and event consumers each did independent +/// read-modify-write cycles on the same "snapshot" key. +/// +/// Note: concurrent invocations of the *same* queue can still race on +/// KV read-modify-write. Acceptable for metrics — at worst one batch's +/// deltas are lost, not cumulative state. +async fn update_kv_snapshot( + kv: &kv::KvStore, + telemetry_delta: Option<&confidence_resolver::proto::confidence::flags::resolver::v1::TelemetryData>, + flush_result: Option, + event_result: Option<(u64, bool)>, +) { let mut cumulative = match kv.get("snapshot").text().await { Ok(Some(text)) => serde_json::from_str::(&text).unwrap_or_default(), _ => TelemetrySnapshot::default(), }; - if let Some(td) = &req.telemetry_data { + if let Some(td) = telemetry_delta { cumulative.accumulate_delta(td); } + match flush_result { + Some(true) => { + cumulative.flush.succeeded = cumulative.flush.succeeded.wrapping_add(1); + } + Some(false) => { + cumulative.flush.failed = cumulative.flush.failed.wrapping_add(1); + } + None => {} + } + + if let Some((event_count, succeeded)) = event_result { + if succeeded { + cumulative.events.published = cumulative.events.published.wrapping_add(event_count); + cumulative.events.batches_succeeded = + cumulative.events.batches_succeeded.wrapping_add(1); + } else { + cumulative.events.batches_failed = cumulative.events.batches_failed.wrapping_add(1); + } + } + let prom_text = cumulative.to_prometheus( "cf-resolver", &confidence_resolver::telemetry::PrometheusConfig::default(), @@ -970,7 +1017,7 @@ fn build_publish_events_request( async fn consume_events_queue( message_batch: MessageBatch, - _env: Env, + env: Env, ) -> Result<()> { let messages = message_batch.messages()?; let raw: Vec = messages.iter().map(|m| m.body().clone()).collect(); @@ -980,6 +1027,8 @@ async fn consume_events_queue( return Ok(()); } + let event_count = all_events.len() as u64; + let client_secret = CONFIDENCE_CLIENT_SECRET .get() .ok_or_else(|| worker::Error::RustError("client secret not configured".into()))?; @@ -991,17 +1040,32 @@ async fn consume_events_queue( &now.as_string().unwrap_or_default(), ); - let resp = send_events(&publish_request).await?; - if resp.status_code() >= 400 { - return Err(worker::Error::RustError(format!( - "events delivery failed: HTTP {}", - resp.status_code() - ))); + let delivered = match send_events(&publish_request).await { + Ok(resp) if resp.status_code() < 400 => true, + Ok(resp) => { + console_log!("events delivery failed: HTTP {}", resp.status_code()); + false + } + Err(e) => { + console_log!("events delivery error: {:?}", e); + false + } + }; + + if let Ok(kv) = env.kv("CONFIDENCE_METRICS_KV") { + update_kv_snapshot(&kv, None, None, Some((event_count, delivered))).await; + } + + if !delivered { + return Err(worker::Error::RustError( + "events delivery failed".to_string(), + )); } Ok(()) } + async fn send_events(body: &serde_json::Value) -> Result { let mut init = RequestInit::new(); let headers = Headers::new(); diff --git a/confidence-cloudflare-resolver/wrangler-test.toml b/confidence-cloudflare-resolver/wrangler-test.toml new file mode 100644 index 000000000..93138bbda --- /dev/null +++ b/confidence-cloudflare-resolver/wrangler-test.toml @@ -0,0 +1,32 @@ +name = "confidence-telemetry-test" +main = "build/worker/shim.mjs" +compatibility_date = "2025-01-28" + +[observability] +enabled = true + +[[queues.consumers]] +queue = "telemetry-test-flag-logs" +max_batch_size = 100 +max_batch_timeout = 10 + +[[queues.producers]] +queue = "telemetry-test-flag-logs" +binding = "flag_logs_queue" + +[[queues.consumers]] +queue = "telemetry-test-events" +max_batch_size = 100 +max_batch_timeout = 10 + +[[queues.producers]] +queue = "telemetry-test-events" +binding = "events_queue" + +[[kv_namespaces]] +binding = "CONFIDENCE_METRICS_KV" +id = "05a77023635a4dbaa4744a20d6419a53" + +[vars] +CONFIDENCE_CLIENT_SECRET = "mkjJruAATQWjeY7foFIWfVAcBWnci2YF" +ENABLE_APPLY_DEDUP = "true" diff --git a/confidence-resolver/protos/confidence/flags/resolver/v1/internal_api.proto b/confidence-resolver/protos/confidence/flags/resolver/v1/internal_api.proto index 9a9c781f6..7d422a207 100644 --- a/confidence-resolver/protos/confidence/flags/resolver/v1/internal_api.proto +++ b/confidence-resolver/protos/confidence/flags/resolver/v1/internal_api.proto @@ -132,6 +132,34 @@ message TelemetryData { repeated ProviderInitRate provider_init_rate = 9; + ApplyDedupTelemetry apply_dedup = 10; + + FlushTelemetry flush = 11; + EventsTelemetry events = 12; + + message FlushTelemetry { + uint32 succeeded = 1; + uint32 failed = 2; + } + + message EventsTelemetry { + uint32 published = 1; + uint32 batches_succeeded = 2; + uint32 batches_failed = 3; + } + + message ApplyDedupTelemetry { + // Delta counters (since last flush): + uint32 applies_total = 1; + uint32 applies_deduped = 2; + uint32 apply_dedup_overflow = 3; + uint32 sweeps = 4; + + // Gauges (point-in-time): + uint32 map_size = 5; + uint32 map_capacity = 6; + } + message ProviderInitRate { uint32 count = 1; reserved 2; // status — tbd diff --git a/confidence-resolver/src/apply_dedup.rs b/confidence-resolver/src/apply_dedup.rs index f0b2d9490..df432c541 100644 --- a/confidence-resolver/src/apply_dedup.rs +++ b/confidence-resolver/src/apply_dedup.rs @@ -3,6 +3,7 @@ use std::hash::{BuildHasherDefault, Hasher}; use crate::proto::confidence::flags::resolver::v1::events::FallthroughAssignment; use crate::proto::confidence::flags::resolver::v1::resolve_token_v1::AssignedFlag; +use crate::proto::confidence::flags::resolver::v1::telemetry_data::ApplyDedupTelemetry; use crate::FlagToApply; const HASH_INIT: u64 = 0xCBF2_9CE4_8422_2325; @@ -152,11 +153,53 @@ pub fn compute_dedup_hash(assigned: &AssignedFlag) -> u64 { /// iteration would repeat the O(n) scan. const SWEEP_MIN_INTERVAL_SECONDS: i64 = 10; +/// Point-in-time snapshot of apply-dedup telemetry counters. +/// +/// Counter fields are cumulative (monotonically increasing). Gauge fields +/// (`map_size`, `map_capacity`) are latest values. +#[derive(Clone, Default)] +#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))] +pub struct ApplyDedupSnapshot { + pub applies_total: u64, + pub applies_deduped: u64, + #[cfg_attr(feature = "json", serde(alias = "overflow"))] + pub apply_dedup_overflow: u64, + pub sweeps: u64, + pub map_size: u32, + pub map_capacity: u32, +} + +impl ApplyDedupSnapshot { + /// Compute a proto delta relative to a previous snapshot, using wrapping + /// subtraction for counters and latest values for gauges. + pub fn to_proto_delta(&self, previous: &ApplyDedupSnapshot) -> ApplyDedupTelemetry { + ApplyDedupTelemetry { + applies_total: self.applies_total.wrapping_sub(previous.applies_total) as u32, + applies_deduped: self.applies_deduped.wrapping_sub(previous.applies_deduped) as u32, + apply_dedup_overflow: self + .apply_dedup_overflow + .wrapping_sub(previous.apply_dedup_overflow) + as u32, + sweeps: self.sweeps.wrapping_sub(previous.sweeps) as u32, + map_size: self.map_size, + map_capacity: self.map_capacity, + } + } + + pub fn has_activity(&self) -> bool { + self.applies_total > 0 + } +} + pub struct ApplyDedup { seen: HashMap, ttl_seconds: i64, max_entries: usize, last_sweep_seconds: i64, + applies_total: u64, + applies_deduped: u64, + apply_dedup_overflow: u64, + sweeps: u64, } impl ApplyDedup { @@ -170,6 +213,21 @@ impl ApplyDedup { ttl_seconds, max_entries, last_sweep_seconds: 0, + applies_total: 0, + applies_deduped: 0, + apply_dedup_overflow: 0, + sweeps: 0, + } + } + + pub fn telemetry_snapshot(&self) -> ApplyDedupSnapshot { + ApplyDedupSnapshot { + applies_total: self.applies_total, + applies_deduped: self.applies_deduped, + apply_dedup_overflow: self.apply_dedup_overflow, + sweeps: self.sweeps, + map_size: self.seen.len() as u32, + map_capacity: self.max_entries as u32, } } @@ -190,14 +248,18 @@ impl ApplyDedup { let now_seconds = now_seconds.max(self.last_sweep_seconds); let mut keep = DedupResult::new(flags.len()); for (i, fta) in flags.iter().enumerate() { + self.applies_total = self.applies_total.wrapping_add(1); let hash = compute_dedup_hash(fta.assigned_flag); // Presence means duplicate — stale entries are removed by sweep, // after which the flag gets re-logged on its next resolve. if self.seen.contains_key(&hash) { + self.applies_deduped = self.applies_deduped.wrapping_add(1); continue; } if self.seen.len() < self.max_entries { self.seen.insert(hash, now_seconds); + } else { + self.apply_dedup_overflow = self.apply_dedup_overflow.wrapping_add(1); } keep.mark(i); } @@ -214,6 +276,7 @@ impl ApplyDedup { return; } self.last_sweep_seconds = now_seconds; + self.sweeps = self.sweeps.wrapping_add(1); let ttl = self.ttl_seconds; self.seen .retain(|_, ts| now_seconds.saturating_sub(*ts) < ttl); @@ -1171,4 +1234,74 @@ mod tests { // sanity: cache holds all entries assert_eq!(dedup.seen.len(), total); } + + #[test] + fn telemetry_counters_basic() { + let mut dedup = ApplyDedup::new(120, 1000); + let flags = vec![ + make_flag_to_apply("flags/a", "user1", "on"), + make_flag_to_apply("flags/b", "user1", "off"), + ]; + + dedup.filter_duplicates(&flags, 1000); + let snap = dedup.telemetry_snapshot(); + assert_eq!(snap.applies_total, 2); + assert_eq!(snap.applies_deduped, 0); + assert_eq!(snap.apply_dedup_overflow, 0); + assert_eq!(snap.map_size, 2); + assert_eq!(snap.map_capacity, 1000); + + // Same flags again — all deduped + dedup.filter_duplicates(&flags, 1001); + let snap = dedup.telemetry_snapshot(); + assert_eq!(snap.applies_total, 4); + assert_eq!(snap.applies_deduped, 2); + } + + #[test] + fn telemetry_overflow_when_full() { + let mut dedup = ApplyDedup::new(120, 2); + + dedup.filter_duplicates(&[make_flag_to_apply("flags/a", "u1", "on")], 1000); + dedup.filter_duplicates(&[make_flag_to_apply("flags/b", "u1", "on")], 1000); + // Map is full — new entry passes through but is not cached + dedup.filter_duplicates(&[make_flag_to_apply("flags/c", "u1", "on")], 1000); + + let snap = dedup.telemetry_snapshot(); + assert_eq!(snap.applies_total, 3); + assert_eq!(snap.apply_dedup_overflow, 1); + assert_eq!(snap.map_size, 2); + } + + #[test] + fn telemetry_sweep_counter() { + let mut dedup = ApplyDedup::new(10, 1000); + dedup.filter_duplicates(&[make_flag_to_apply("flags/a", "u1", "on")], 100); + + dedup.sweep(105); // runs + dedup.sweep(112); // throttled + dedup.sweep(115); // runs + + let snap = dedup.telemetry_snapshot(); + assert_eq!(snap.sweeps, 2); + } + + #[test] + fn telemetry_proto_delta() { + let mut dedup = ApplyDedup::new(120, 1000); + let flags = vec![make_flag_to_apply("flags/a", "user1", "on")]; + + dedup.filter_duplicates(&flags, 1000); + let snap1 = dedup.telemetry_snapshot(); + + dedup.filter_duplicates(&flags, 1001); // duplicate + dedup.filter_duplicates(&[make_flag_to_apply("flags/b", "u1", "on")], 1001); + let snap2 = dedup.telemetry_snapshot(); + + let delta = snap2.to_proto_delta(&snap1); + assert_eq!(delta.applies_total, 2); // 1 dup + 1 new + assert_eq!(delta.applies_deduped, 1); + assert_eq!(delta.map_size, 2); + assert_eq!(delta.map_capacity, 1000); + } } diff --git a/confidence-resolver/src/telemetry.rs b/confidence-resolver/src/telemetry.rs index bfd18de55..6209612c4 100644 --- a/confidence-resolver/src/telemetry.rs +++ b/confidence-resolver/src/telemetry.rs @@ -6,6 +6,8 @@ use arc_swap::ArcSwap; use crate::ResolveReason; +use crate::apply_dedup::ApplyDedupSnapshot; + mod pb { pub use crate::proto::confidence::flags::resolver::v1::telemetry_data::{ BucketSpan, ResolveLatency, ResolveRate, StateAge, @@ -111,6 +113,24 @@ pub struct TelemetrySnapshot { pub latency: HistogramSnapshot, pub resolve_rates: Vec, pub memory_bytes: u64, + pub apply_dedup: Option, + pub flush: FlushSnapshot, + pub events: EventsSnapshot, +} + +#[derive(Clone, Default)] +#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))] +pub struct FlushSnapshot { + pub succeeded: u64, + pub failed: u64, +} + +#[derive(Clone, Default)] +#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))] +pub struct EventsSnapshot { + pub published: u64, + pub batches_succeeded: u64, + pub batches_failed: u64, } #[derive(Clone, Default)] @@ -188,6 +208,39 @@ impl TelemetrySnapshot { if td.memory_bytes > 0 { self.memory_bytes = td.memory_bytes; } + + if let Some(dedup) = &td.apply_dedup { + let ad = self + .apply_dedup + .get_or_insert_with(ApplyDedupSnapshot::default); + ad.applies_total = ad.applies_total.wrapping_add(dedup.applies_total as u64); + ad.applies_deduped = ad + .applies_deduped + .wrapping_add(dedup.applies_deduped as u64); + ad.apply_dedup_overflow = ad + .apply_dedup_overflow + .wrapping_add(dedup.apply_dedup_overflow as u64); + ad.sweeps = ad.sweeps.wrapping_add(dedup.sweeps as u64); + ad.map_size = dedup.map_size; + ad.map_capacity = dedup.map_capacity; + } + + if let Some(flush) = &td.flush { + self.flush.succeeded = self.flush.succeeded.wrapping_add(flush.succeeded as u64); + self.flush.failed = self.flush.failed.wrapping_add(flush.failed as u64); + } + + if let Some(events) = &td.events { + self.events.published = self.events.published.wrapping_add(events.published as u64); + self.events.batches_succeeded = self + .events + .batches_succeeded + .wrapping_add(events.batches_succeeded as u64); + self.events.batches_failed = self + .events + .batches_failed + .wrapping_add(events.batches_failed as u64); + } } /// Format the snapshot as Prometheus exposition text. @@ -215,6 +268,9 @@ impl TelemetrySnapshot { self.write_histogram(w, resolver_id, config)?; self.write_resolve_rates(w, resolver_id, config)?; self.write_memory(w, resolver_id, config)?; + self.write_apply_dedup(w, resolver_id, config)?; + self.write_flush(w, resolver_id, config)?; + self.write_events(w, resolver_id, config)?; if config.openmetrics { writeln!(w, "# EOF")?; } @@ -354,6 +410,208 @@ impl TelemetrySnapshot { self.memory_bytes ) } + + fn write_apply_dedup( + &self, + w: &mut dyn fmt::Write, + resolver_id: &str, + config: &PrometheusConfig, + ) -> fmt::Result { + let ad = match &self.apply_dedup { + Some(ad) if ad.has_activity() => ad, + _ => return Ok(()), + }; + let suffix = if config.openmetrics { ".0" } else { "" }; + + let type_name = if config.openmetrics { + "confidence_apply_dedup_applies" + } else { + "confidence_apply_dedup_applies_total" + }; + writeln!( + w, + "# HELP {type_name} Total flag apply events processed by dedup." + )?; + writeln!(w, "# TYPE {type_name} counter")?; + writeln!( + w, + "confidence_apply_dedup_applies_total{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + ad.applies_total + )?; + + let type_name = if config.openmetrics { + "confidence_apply_dedup_deduped" + } else { + "confidence_apply_dedup_deduped_total" + }; + writeln!(w, "# HELP {type_name} Duplicate apply events filtered out.")?; + writeln!(w, "# TYPE {type_name} counter")?; + writeln!( + w, + "confidence_apply_dedup_deduped_total{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + ad.applies_deduped + )?; + + let type_name = if config.openmetrics { + "confidence_apply_dedup_overflow" + } else { + "confidence_apply_dedup_overflow_total" + }; + writeln!( + w, + "# HELP {type_name} Unique apply events that passed the dedup filter but could not be cached (map full)." + )?; + writeln!(w, "# TYPE {type_name} counter")?; + writeln!( + w, + "confidence_apply_dedup_overflow_total{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + ad.apply_dedup_overflow + )?; + + let type_name = if config.openmetrics { + "confidence_apply_dedup_sweeps" + } else { + "confidence_apply_dedup_sweeps_total" + }; + writeln!( + w, + "# HELP {type_name} Number of dedup map sweep operations." + )?; + writeln!(w, "# TYPE {type_name} counter")?; + writeln!( + w, + "confidence_apply_dedup_sweeps_total{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + ad.sweeps + )?; + + writeln!( + w, + "# HELP confidence_apply_dedup_map_size Current entries in the dedup map." + )?; + writeln!(w, "# TYPE confidence_apply_dedup_map_size gauge")?; + writeln!( + w, + "confidence_apply_dedup_map_size{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + ad.map_size + )?; + + writeln!( + w, + "# HELP confidence_apply_dedup_map_capacity Maximum entries allowed in the dedup map." + )?; + writeln!(w, "# TYPE confidence_apply_dedup_map_capacity gauge")?; + writeln!( + w, + "confidence_apply_dedup_map_capacity{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + ad.map_capacity + ) + } + + fn write_flush( + &self, + w: &mut dyn fmt::Write, + resolver_id: &str, + config: &PrometheusConfig, + ) -> fmt::Result { + if self.flush.succeeded == 0 && self.flush.failed == 0 { + return Ok(()); + } + let suffix = if config.openmetrics { ".0" } else { "" }; + + let type_name = if config.openmetrics { + "confidence_flush_succeeded" + } else { + "confidence_flush_succeeded_total" + }; + writeln!( + w, + "# HELP {type_name} Successful WriteFlagLogs batch deliveries." + )?; + writeln!(w, "# TYPE {type_name} counter")?; + writeln!( + w, + "confidence_flush_succeeded_total{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + self.flush.succeeded + )?; + + let type_name = if config.openmetrics { + "confidence_flush_failed" + } else { + "confidence_flush_failed_total" + }; + writeln!( + w, + "# HELP {type_name} Failed WriteFlagLogs batch deliveries." + )?; + writeln!(w, "# TYPE {type_name} counter")?; + writeln!( + w, + "confidence_flush_failed_total{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + self.flush.failed + ) + } + + fn write_events( + &self, + w: &mut dyn fmt::Write, + resolver_id: &str, + config: &PrometheusConfig, + ) -> fmt::Result { + if self.events.published == 0 + && self.events.batches_succeeded == 0 + && self.events.batches_failed == 0 + { + return Ok(()); + } + let suffix = if config.openmetrics { ".0" } else { "" }; + + if self.events.published > 0 { + let type_name = if config.openmetrics { + "confidence_events_published" + } else { + "confidence_events_published_total" + }; + writeln!(w, "# HELP {type_name} Total events published.")?; + writeln!(w, "# TYPE {type_name} counter")?; + writeln!( + w, + "confidence_events_published_total{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + self.events.published + )?; + } + + if self.events.batches_succeeded > 0 { + let type_name = if config.openmetrics { + "confidence_event_batches_succeeded" + } else { + "confidence_event_batches_succeeded_total" + }; + writeln!(w, "# HELP {type_name} Successful event batch deliveries.")?; + writeln!(w, "# TYPE {type_name} counter")?; + writeln!( + w, + "confidence_event_batches_succeeded_total{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + self.events.batches_succeeded + )?; + } + + if self.events.batches_failed > 0 { + let type_name = if config.openmetrics { + "confidence_event_batches_failed" + } else { + "confidence_event_batches_failed_total" + }; + writeln!(w, "# HELP {type_name} Failed event batch deliveries.")?; + writeln!(w, "# TYPE {type_name} counter")?; + writeln!( + w, + "confidence_event_batches_failed_total{{resolver_id=\"{resolver_id}\"}} {}{suffix}", + self.events.batches_failed + )?; + } + + Ok(()) + } } /// Concurrent telemetry collector. @@ -420,6 +678,9 @@ impl Telemetry { .map(|c| c.load(Ordering::Relaxed)) .collect(), memory_bytes: (self.memory_provider)(), + apply_dedup: None, + flush: FlushSnapshot::default(), + events: EventsSnapshot::default(), } } @@ -499,6 +760,9 @@ impl Telemetry { memory_bytes: (self.memory_provider)(), resolver_version: crate::version::VERSION.to_string(), provider_init_rate: Vec::new(), + apply_dedup: None, + flush: None, + events: None, } } } @@ -1124,4 +1388,173 @@ mod tests { let total: u64 = snap.latency.buckets.iter().sum(); assert_eq!(total, 0); } + + #[test] + fn accumulate_delta_with_apply_dedup() { + use crate::proto::confidence::flags::resolver::v1::telemetry_data::ApplyDedupTelemetry; + + let mut snap = TelemetrySnapshot::default(); + let td = pb::TelemetryData { + apply_dedup: Some(ApplyDedupTelemetry { + applies_total: 10, + applies_deduped: 3, + apply_dedup_overflow: 1, + sweeps: 2, + map_size: 50, + map_capacity: 100_000, + }), + ..Default::default() + }; + + snap.accumulate_delta(&td); + let ad = snap.apply_dedup.as_ref().unwrap(); + assert_eq!(ad.applies_total, 10); + assert_eq!(ad.applies_deduped, 3); + assert_eq!(ad.apply_dedup_overflow, 1); + assert_eq!(ad.sweeps, 2); + assert_eq!(ad.map_size, 50); + assert_eq!(ad.map_capacity, 100_000); + + // Second accumulation adds counters, replaces gauges + snap.accumulate_delta(&td); + let ad = snap.apply_dedup.as_ref().unwrap(); + assert_eq!(ad.applies_total, 20); + assert_eq!(ad.applies_deduped, 6); + assert_eq!(ad.map_size, 50); // gauge, replaced + } + + #[test] + fn prometheus_apply_dedup_metrics() { + let mut snap = TelemetrySnapshot::default(); + snap.apply_dedup = Some(ApplyDedupSnapshot { + applies_total: 100, + applies_deduped: 40, + apply_dedup_overflow: 5, + sweeps: 3, + map_size: 200, + map_capacity: 100_000, + }); + + let config = PrometheusConfig::default(); + let prom = snap.to_prometheus("w0", &config); + + assert!(prom.contains("# HELP confidence_apply_dedup_applies_total")); + assert!(prom.contains("# TYPE confidence_apply_dedup_applies_total counter")); + assert!(prom.contains(r#"confidence_apply_dedup_applies_total{resolver_id="w0"} 100"#)); + + assert!(prom.contains(r#"confidence_apply_dedup_deduped_total{resolver_id="w0"} 40"#)); + + assert!(prom.contains(r#"confidence_apply_dedup_overflow_total{resolver_id="w0"} 5"#)); + + assert!(prom.contains(r#"confidence_apply_dedup_sweeps_total{resolver_id="w0"} 3"#)); + + assert!(prom.contains(r#"confidence_apply_dedup_map_size{resolver_id="w0"} 200"#)); + assert!(prom.contains("# TYPE confidence_apply_dedup_map_size gauge")); + + assert!(prom.contains(r#"confidence_apply_dedup_map_capacity{resolver_id="w0"} 100000"#)); + assert!(prom.contains("# TYPE confidence_apply_dedup_map_capacity gauge")); + } + + #[test] + fn prometheus_no_dedup_when_none() { + let snap = TelemetrySnapshot::default(); + let prom = snap.to_prometheus("w0", &PrometheusConfig::default()); + assert!(!prom.contains("apply_dedup")); + } + + #[test] + fn prometheus_flush_counters() { + let mut snap = TelemetrySnapshot::default(); + snap.flush.succeeded = 10; + snap.flush.failed = 2; + + let config = PrometheusConfig::default(); + let prom = snap.to_prometheus("w0", &config); + + assert!(prom.contains(r#"confidence_flush_succeeded_total{resolver_id="w0"} 10"#)); + assert!(prom.contains(r#"confidence_flush_failed_total{resolver_id="w0"} 2"#)); + } + + #[test] + fn accumulate_delta_flush_counters() { + use crate::proto::confidence::flags::resolver::v1::telemetry_data::FlushTelemetry; + + let mut snap = TelemetrySnapshot::default(); + let td = pb::TelemetryData { + flush: Some(FlushTelemetry { + succeeded: 5, + failed: 1, + }), + ..Default::default() + }; + + snap.accumulate_delta(&td); + assert_eq!(snap.flush.succeeded, 5); + assert_eq!(snap.flush.failed, 1); + + snap.accumulate_delta(&td); + assert_eq!(snap.flush.succeeded, 10); + assert_eq!(snap.flush.failed, 2); + } + + #[test] + fn accumulate_delta_event_counters() { + use crate::proto::confidence::flags::resolver::v1::telemetry_data::EventsTelemetry; + + let mut snap = TelemetrySnapshot::default(); + let td = pb::TelemetryData { + events: Some(EventsTelemetry { + published: 42, + batches_succeeded: 3, + batches_failed: 1, + }), + ..Default::default() + }; + + snap.accumulate_delta(&td); + assert_eq!(snap.events.published, 42); + assert_eq!(snap.events.batches_succeeded, 3); + assert_eq!(snap.events.batches_failed, 1); + + snap.accumulate_delta(&td); + assert_eq!(snap.events.published, 84); + assert_eq!(snap.events.batches_succeeded, 6); + assert_eq!(snap.events.batches_failed, 2); + } + + #[test] + fn openmetrics_with_all_telemetry() { + let tel = Telemetry::with_memory_provider(|| 1_048_576); + tel.record_latency_us(100); + tel.mark_resolve(ResolveReason::Match); + + let mut snap = tel.snapshot(); + snap.apply_dedup = Some(ApplyDedupSnapshot { + applies_total: 10, + applies_deduped: 3, + apply_dedup_overflow: 0, + sweeps: 1, + map_size: 5, + map_capacity: 100_000, + }); + snap.flush.succeeded = 2; + snap.flush.failed = 1; + snap.events.published = 50; + snap.events.batches_succeeded = 4; + snap.events.batches_failed = 1; + + let config = PrometheusConfig { + openmetrics: true, + ..PrometheusConfig::default() + }; + let output = snap.to_prometheus("w0", &config); + + assert!(output.trim_end().ends_with("# EOF")); + let result = openmetrics_parser::openmetrics::parse_openmetrics(&output); + assert!( + result.is_ok(), + "OpenMetrics parser rejected output with all telemetry: {:?}\n\nRaw:\n{output}", + result.err() + ); + } } diff --git a/openfeature-provider/go/confidence/integration_test.go b/openfeature-provider/go/confidence/integration_test.go index 93c46ef5b..8777a5695 100644 --- a/openfeature-provider/go/confidence/integration_test.go +++ b/openfeature-provider/go/confidence/integration_test.go @@ -56,6 +56,8 @@ func (t *trackingFlagLogger) Shutdown() { t.actualLogger.Shutdown() } +func (t *trackingFlagLogger) RecordEventBatch(eventCount int, succeeded bool) {} + func (t *trackingFlagLogger) GetLogsSentCount() int32 { return atomic.LoadInt32(&t.logsSentCount) } diff --git a/openfeature-provider/go/confidence/internal/flag_logger/capturing.go b/openfeature-provider/go/confidence/internal/flag_logger/capturing.go index 101739dde..8a8535d0e 100644 --- a/openfeature-provider/go/confidence/internal/flag_logger/capturing.go +++ b/openfeature-provider/go/confidence/internal/flag_logger/capturing.go @@ -53,6 +53,9 @@ func (c *CapturingFlagLogger) Shutdown() { c.shutdownCalled = true } +// RecordEventBatch is a no-op for the capturing logger. +func (c *CapturingFlagLogger) RecordEventBatch(eventCount int, succeeded bool) {} + // GetCapturedRequests returns a copy of all captured requests func (c *CapturingFlagLogger) GetCapturedRequests() []*resolverv1.WriteFlagLogsRequest { c.mu.Lock() diff --git a/openfeature-provider/go/confidence/internal/flag_logger/counter_restore_test.go b/openfeature-provider/go/confidence/internal/flag_logger/counter_restore_test.go new file mode 100644 index 000000000..e64f140ad --- /dev/null +++ b/openfeature-provider/go/confidence/internal/flag_logger/counter_restore_test.go @@ -0,0 +1,109 @@ +package flag_logger + +import ( + "sync/atomic" + "testing" + + resolverv1 "github.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/resolverinternal" +) + +// TestCounterRestoreOnFailure verifies that after N consecutive failures +// followed by 1 success, the backend sees flush_failed=N (not 1). +func TestCounterRestoreOnFailure(t *testing.T) { + // Simulate the counter drain → set on request → failure → restore cycle + // using raw atomics (same logic as GrpcFlagLogger/MultiDestinationFlagLogger). + + var flushSucceeded, flushFailed atomic.Int64 + var eventsPublished, eventBatchesSucceeded, eventBatchesFailed atomic.Int64 + + drain := func() *resolverv1.TelemetryData { + fs := uint32(flushSucceeded.Swap(0)) + ff := uint32(flushFailed.Swap(0)) + ep := uint32(eventsPublished.Swap(0)) + ebs := uint32(eventBatchesSucceeded.Swap(0)) + ebf := uint32(eventBatchesFailed.Swap(0)) + td := &resolverv1.TelemetryData{} + if fs > 0 || ff > 0 { + td.Flush = &resolverv1.TelemetryData_FlushTelemetry{Succeeded: fs, Failed: ff} + } + if ep > 0 || ebs > 0 || ebf > 0 { + td.Events = &resolverv1.TelemetryData_EventsTelemetry{ + Published: ep, BatchesSucceeded: ebs, BatchesFailed: ebf, + } + } + return td + } + + restoreOnFailure := func(td *resolverv1.TelemetryData) { + flushFailed.Add(1) // record this failure + if td.Flush != nil { + flushSucceeded.Add(int64(td.Flush.Succeeded)) + flushFailed.Add(int64(td.Flush.Failed)) + } + if td.Events != nil { + eventsPublished.Add(int64(td.Events.Published)) + eventBatchesSucceeded.Add(int64(td.Events.BatchesSucceeded)) + eventBatchesFailed.Add(int64(td.Events.BatchesFailed)) + } + } + + recordSuccess := func() { + flushSucceeded.Add(1) + } + + // Simulate some event activity before flushes start + eventsPublished.Add(500) + eventBatchesSucceeded.Add(3) + eventBatchesFailed.Add(1) + + // Flush 1: drain → FAIL + td1 := drain() + restoreOnFailure(td1) + // After: flushFailed=1, eventsPublished=500, eventBatchesSucceeded=3, eventBatchesFailed=1 + + // Flush 2: drain → FAIL + td2 := drain() + restoreOnFailure(td2) + // After: flushFailed=2 (1 restored + 1 new), events restored + + // Flush 3: drain → FAIL + td3 := drain() + restoreOnFailure(td3) + // After: flushFailed=3 + + // Flush 4: drain → SUCCESS + td4 := drain() + recordSuccess() + + // Verify: the backend (td4) should see flush_failed=3 and all event counters + if td4.Flush == nil { + t.Fatal("flush telemetry should be present") + } + if td4.Flush.Failed != 3 { + t.Errorf("flush.failed: got %d, want 3", td4.Flush.Failed) + } + if td4.Flush.Succeeded != 0 { + t.Errorf("flush.succeeded: got %d, want 0", td4.Flush.Succeeded) + } + + if td4.Events == nil { + t.Fatal("events telemetry should be present") + } + if td4.Events.Published != 500 { + t.Errorf("events.published: got %d, want 500", td4.Events.Published) + } + if td4.Events.BatchesSucceeded != 3 { + t.Errorf("events.batches_succeeded: got %d, want 3", td4.Events.BatchesSucceeded) + } + if td4.Events.BatchesFailed != 1 { + t.Errorf("events.batches_failed: got %d, want 1", td4.Events.BatchesFailed) + } + + // After success: only the new success should be in the atomics + if v := flushSucceeded.Load(); v != 1 { + t.Errorf("post-success flushSucceeded: got %d, want 1", v) + } + if v := flushFailed.Load(); v != 0 { + t.Errorf("post-success flushFailed: got %d, want 0", v) + } +} diff --git a/openfeature-provider/go/confidence/internal/flag_logger/grpc.go b/openfeature-provider/go/confidence/internal/flag_logger/grpc.go index b2949a148..c43c19fc5 100644 --- a/openfeature-provider/go/confidence/internal/flag_logger/grpc.go +++ b/openfeature-provider/go/confidence/internal/flag_logger/grpc.go @@ -19,6 +19,7 @@ type GrpcFlagLogger struct { wg sync.WaitGroup attempts atomic.Int64 failures atomic.Int64 + TelemetryCounters } func NewGrpcWasmFlagLogger(stub resolverv1.InternalFlagLoggerServiceClient, clientSecret string, logger *slog.Logger) *GrpcFlagLogger { @@ -57,6 +58,7 @@ func (g *GrpcFlagLogger) Write(request *resolverv1.WriteFlagLogsRequest) { "client_resolve_info", clientResolveCount, "flag_resolve_info", flagResolveCount) + g.DrainAndStamp(request) g.sendAsync(request) } @@ -75,7 +77,9 @@ func (g *GrpcFlagLogger) sendAsync(request *resolverv1.WriteFlagLogsRequest) { if _, err := g.stub.ClientWriteFlagLogs(rpcCtx, request); err != nil { g.failures.Add(1) + g.RestoreOnFailure(request) } else { + g.FlushSucceeded.Add(1) g.logger.Debug("Successfully sent flag log", "flag_assigned", len(request.FlagAssigned), "client_resolve_info", len(request.ClientResolveInfo), @@ -109,3 +113,7 @@ func (n *NoOpWasmFlagLogger) Write(request *resolverv1.WriteFlagLogsRequest) { func (n *NoOpWasmFlagLogger) Shutdown() { // Nothing to shut down } + +func (n *NoOpWasmFlagLogger) RecordEventBatch(eventCount int, succeeded bool) { + // No-op +} diff --git a/openfeature-provider/go/confidence/internal/flag_logger/multi.go b/openfeature-provider/go/confidence/internal/flag_logger/multi.go index 27afb18ca..5f1ce9b7d 100644 --- a/openfeature-provider/go/confidence/internal/flag_logger/multi.go +++ b/openfeature-provider/go/confidence/internal/flag_logger/multi.go @@ -26,6 +26,7 @@ type MultiDestinationFlagLogger struct { wg sync.WaitGroup attempts atomic.Int64 failures atomic.Int64 + TelemetryCounters } // NewMultiDestinationFlagLogger creates a flag logger that routes to multiple @@ -83,6 +84,8 @@ func (m *MultiDestinationFlagLogger) Write(request *resolverv1.WriteFlagLogsRequ "client_resolve_info", clientResolveCount, "flag_resolve_info", flagResolveCount) + m.DrainAndStamp(request) + m.wg.Add(1) go func() { defer m.wg.Done() @@ -122,6 +125,9 @@ func (m *MultiDestinationFlagLogger) Write(request *resolverv1.WriteFlagLogsRequ if lastErr != nil { m.failures.Add(1) + m.RestoreOnFailure(request) + } else { + m.FlushSucceeded.Add(1) } if m.attempts.Add(1)%10 == 0 { diff --git a/openfeature-provider/go/confidence/internal/flag_logger/telemetry_counters.go b/openfeature-provider/go/confidence/internal/flag_logger/telemetry_counters.go new file mode 100644 index 000000000..23c851566 --- /dev/null +++ b/openfeature-provider/go/confidence/internal/flag_logger/telemetry_counters.go @@ -0,0 +1,74 @@ +package flag_logger + +import ( + "sync/atomic" + + resolverv1 "github.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/resolverinternal" +) + +// TelemetryCounters holds atomic counters for flush and event delivery +// telemetry. Embedded by both GrpcFlagLogger and MultiDestinationFlagLogger +// to avoid duplicating drain/restore/record logic. +type TelemetryCounters struct { + FlushSucceeded atomic.Int64 + FlushFailed atomic.Int64 + EventsPublished atomic.Int64 + EventBatchesSucceeded atomic.Int64 + EventBatchesFailed atomic.Int64 +} + +// DrainAndStamp atomically drains all counters and stamps them onto the +// request's TelemetryData. Call before sending. +func (tc *TelemetryCounters) DrainAndStamp(request *resolverv1.WriteFlagLogsRequest) { + succeeded := uint32(tc.FlushSucceeded.Swap(0)) + failed := uint32(tc.FlushFailed.Swap(0)) + evPub := uint32(tc.EventsPublished.Swap(0)) + evOk := uint32(tc.EventBatchesSucceeded.Swap(0)) + evFail := uint32(tc.EventBatchesFailed.Swap(0)) + if succeeded > 0 || failed > 0 || evPub > 0 || evOk > 0 || evFail > 0 { + if request.TelemetryData == nil { + request.TelemetryData = &resolverv1.TelemetryData{} + } + if succeeded > 0 || failed > 0 { + request.TelemetryData.Flush = &resolverv1.TelemetryData_FlushTelemetry{ + Succeeded: succeeded, + Failed: failed, + } + } + if evPub > 0 || evOk > 0 || evFail > 0 { + request.TelemetryData.Events = &resolverv1.TelemetryData_EventsTelemetry{ + Published: evPub, + BatchesSucceeded: evOk, + BatchesFailed: evFail, + } + } + } +} + +// RestoreOnFailure adds the drained counters from the failed request back to +// the atomics so they are retried in the next flush. Also records the current +// failure (+1 to FlushFailed). +func (tc *TelemetryCounters) RestoreOnFailure(request *resolverv1.WriteFlagLogsRequest) { + tc.FlushFailed.Add(1) + if td := request.TelemetryData; td != nil { + if td.Flush != nil { + tc.FlushSucceeded.Add(int64(td.Flush.Succeeded)) + tc.FlushFailed.Add(int64(td.Flush.Failed)) + } + if td.Events != nil { + tc.EventsPublished.Add(int64(td.Events.Published)) + tc.EventBatchesSucceeded.Add(int64(td.Events.BatchesSucceeded)) + tc.EventBatchesFailed.Add(int64(td.Events.BatchesFailed)) + } + } +} + +// RecordEventBatch records an event batch delivery outcome. +func (tc *TelemetryCounters) RecordEventBatch(eventCount int, succeeded bool) { + if succeeded { + tc.EventsPublished.Add(int64(eventCount)) + tc.EventBatchesSucceeded.Add(1) + } else { + tc.EventBatchesFailed.Add(1) + } +} diff --git a/openfeature-provider/go/confidence/internal/local_resolver/assets/confidence_resolver.wasm b/openfeature-provider/go/confidence/internal/local_resolver/assets/confidence_resolver.wasm index 4b70bf192..a451100eb 100755 Binary files a/openfeature-provider/go/confidence/internal/local_resolver/assets/confidence_resolver.wasm and b/openfeature-provider/go/confidence/internal/local_resolver/assets/confidence_resolver.wasm differ diff --git a/openfeature-provider/go/confidence/internal/proto/resolverinternal/internal_api.pb.go b/openfeature-provider/go/confidence/internal/proto/resolverinternal/internal_api.pb.go index 0050357cb..d7921b887 100644 --- a/openfeature-provider/go/confidence/internal/proto/resolverinternal/internal_api.pb.go +++ b/openfeature-provider/go/confidence/internal/proto/resolverinternal/internal_api.pb.go @@ -244,10 +244,19 @@ type TelemetryData struct { state protoimpl.MessageState `protogen:"open.v1"` // Information about the SDK/provider Sdk *resolver.Sdk `protobuf:"bytes,2,opt,name=sdk,proto3" json:"sdk,omitempty"` + // WASM-produced fields. Must be present so decode→encode round-trips + // in host SDKs preserve them (ts-proto/protobuf-js drop unknown fields). + ResolveLatency *TelemetryData_ResolveLatency `protobuf:"bytes,4,opt,name=resolve_latency,json=resolveLatency,proto3" json:"resolve_latency,omitempty"` + ResolveRate []*TelemetryData_ResolveRate `protobuf:"bytes,5,rep,name=resolve_rate,json=resolveRate,proto3" json:"resolve_rate,omitempty"` + StateAge *TelemetryData_StateAge `protobuf:"bytes,6,opt,name=state_age,json=stateAge,proto3" json:"state_age,omitempty"` + MemoryBytes uint64 `protobuf:"varint,7,opt,name=memory_bytes,json=memoryBytes,proto3" json:"memory_bytes,omitempty"` // Version of the embedded resolver (e.g. "0.14.0"). // This must be preserved when providers decode and re-encode telemetry. - ResolverVersion string `protobuf:"bytes,8,opt,name=resolver_version,json=resolverVersion,proto3" json:"resolver_version,omitempty"` - ProviderInitRate []*TelemetryData_ProviderInitRate `protobuf:"bytes,9,rep,name=provider_init_rate,json=providerInitRate,proto3" json:"provider_init_rate,omitempty"` + ResolverVersion string `protobuf:"bytes,8,opt,name=resolver_version,json=resolverVersion,proto3" json:"resolver_version,omitempty"` + ProviderInitRate []*TelemetryData_ProviderInitRate `protobuf:"bytes,9,rep,name=provider_init_rate,json=providerInitRate,proto3" json:"provider_init_rate,omitempty"` + ApplyDedup *TelemetryData_ApplyDedupTelemetry `protobuf:"bytes,10,opt,name=apply_dedup,json=applyDedup,proto3" json:"apply_dedup,omitempty"` + Flush *TelemetryData_FlushTelemetry `protobuf:"bytes,11,opt,name=flush,proto3" json:"flush,omitempty"` + Events *TelemetryData_EventsTelemetry `protobuf:"bytes,12,opt,name=events,proto3" json:"events,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -289,6 +298,34 @@ func (x *TelemetryData) GetSdk() *resolver.Sdk { return nil } +func (x *TelemetryData) GetResolveLatency() *TelemetryData_ResolveLatency { + if x != nil { + return x.ResolveLatency + } + return nil +} + +func (x *TelemetryData) GetResolveRate() []*TelemetryData_ResolveRate { + if x != nil { + return x.ResolveRate + } + return nil +} + +func (x *TelemetryData) GetStateAge() *TelemetryData_StateAge { + if x != nil { + return x.StateAge + } + return nil +} + +func (x *TelemetryData) GetMemoryBytes() uint64 { + if x != nil { + return x.MemoryBytes + } + return 0 +} + func (x *TelemetryData) GetResolverVersion() string { if x != nil { return x.ResolverVersion @@ -303,6 +340,27 @@ func (x *TelemetryData) GetProviderInitRate() []*TelemetryData_ProviderInitRate return nil } +func (x *TelemetryData) GetApplyDedup() *TelemetryData_ApplyDedupTelemetry { + if x != nil { + return x.ApplyDedup + } + return nil +} + +func (x *TelemetryData) GetFlush() *TelemetryData_FlushTelemetry { + if x != nil { + return x.Flush + } + return nil +} + +func (x *TelemetryData) GetEvents() *TelemetryData_EventsTelemetry { + if x != nil { + return x.Events + } + return nil +} + type ClientInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Client string `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` @@ -1181,6 +1239,418 @@ func (x *ReadOperationsResult) GetResults() []*ReadResult { return nil } +type TelemetryData_FlushTelemetry struct { + state protoimpl.MessageState `protogen:"open.v1"` + Succeeded uint32 `protobuf:"varint,1,opt,name=succeeded,proto3" json:"succeeded,omitempty"` + Failed uint32 `protobuf:"varint,2,opt,name=failed,proto3" json:"failed,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TelemetryData_FlushTelemetry) Reset() { + *x = TelemetryData_FlushTelemetry{} + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TelemetryData_FlushTelemetry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryData_FlushTelemetry) ProtoMessage() {} + +func (x *TelemetryData_FlushTelemetry) ProtoReflect() protoreflect.Message { + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryData_FlushTelemetry.ProtoReflect.Descriptor instead. +func (*TelemetryData_FlushTelemetry) Descriptor() ([]byte, []int) { + return file_confidence_flags_resolver_v1_internal_api_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *TelemetryData_FlushTelemetry) GetSucceeded() uint32 { + if x != nil { + return x.Succeeded + } + return 0 +} + +func (x *TelemetryData_FlushTelemetry) GetFailed() uint32 { + if x != nil { + return x.Failed + } + return 0 +} + +type TelemetryData_EventsTelemetry struct { + state protoimpl.MessageState `protogen:"open.v1"` + Published uint32 `protobuf:"varint,1,opt,name=published,proto3" json:"published,omitempty"` + BatchesSucceeded uint32 `protobuf:"varint,2,opt,name=batches_succeeded,json=batchesSucceeded,proto3" json:"batches_succeeded,omitempty"` + BatchesFailed uint32 `protobuf:"varint,3,opt,name=batches_failed,json=batchesFailed,proto3" json:"batches_failed,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TelemetryData_EventsTelemetry) Reset() { + *x = TelemetryData_EventsTelemetry{} + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TelemetryData_EventsTelemetry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryData_EventsTelemetry) ProtoMessage() {} + +func (x *TelemetryData_EventsTelemetry) ProtoReflect() protoreflect.Message { + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryData_EventsTelemetry.ProtoReflect.Descriptor instead. +func (*TelemetryData_EventsTelemetry) Descriptor() ([]byte, []int) { + return file_confidence_flags_resolver_v1_internal_api_proto_rawDescGZIP(), []int{3, 1} +} + +func (x *TelemetryData_EventsTelemetry) GetPublished() uint32 { + if x != nil { + return x.Published + } + return 0 +} + +func (x *TelemetryData_EventsTelemetry) GetBatchesSucceeded() uint32 { + if x != nil { + return x.BatchesSucceeded + } + return 0 +} + +func (x *TelemetryData_EventsTelemetry) GetBatchesFailed() uint32 { + if x != nil { + return x.BatchesFailed + } + return 0 +} + +type TelemetryData_ResolveLatency struct { + state protoimpl.MessageState `protogen:"open.v1"` + Sum uint32 `protobuf:"varint,1,opt,name=sum,proto3" json:"sum,omitempty"` + Count uint32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` + Buckets []*TelemetryData_BucketSpan `protobuf:"bytes,3,rep,name=buckets,proto3" json:"buckets,omitempty"` + LnRatio float64 `protobuf:"fixed64,4,opt,name=ln_ratio,json=lnRatio,proto3" json:"ln_ratio,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TelemetryData_ResolveLatency) Reset() { + *x = TelemetryData_ResolveLatency{} + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TelemetryData_ResolveLatency) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryData_ResolveLatency) ProtoMessage() {} + +func (x *TelemetryData_ResolveLatency) ProtoReflect() protoreflect.Message { + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryData_ResolveLatency.ProtoReflect.Descriptor instead. +func (*TelemetryData_ResolveLatency) Descriptor() ([]byte, []int) { + return file_confidence_flags_resolver_v1_internal_api_proto_rawDescGZIP(), []int{3, 2} +} + +func (x *TelemetryData_ResolveLatency) GetSum() uint32 { + if x != nil { + return x.Sum + } + return 0 +} + +func (x *TelemetryData_ResolveLatency) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *TelemetryData_ResolveLatency) GetBuckets() []*TelemetryData_BucketSpan { + if x != nil { + return x.Buckets + } + return nil +} + +func (x *TelemetryData_ResolveLatency) GetLnRatio() float64 { + if x != nil { + return x.LnRatio + } + return 0 +} + +type TelemetryData_ResolveRate struct { + state protoimpl.MessageState `protogen:"open.v1"` + Count uint32 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"` + Reason uint32 `protobuf:"varint,2,opt,name=reason,proto3" json:"reason,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TelemetryData_ResolveRate) Reset() { + *x = TelemetryData_ResolveRate{} + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TelemetryData_ResolveRate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryData_ResolveRate) ProtoMessage() {} + +func (x *TelemetryData_ResolveRate) ProtoReflect() protoreflect.Message { + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryData_ResolveRate.ProtoReflect.Descriptor instead. +func (*TelemetryData_ResolveRate) Descriptor() ([]byte, []int) { + return file_confidence_flags_resolver_v1_internal_api_proto_rawDescGZIP(), []int{3, 3} +} + +func (x *TelemetryData_ResolveRate) GetCount() uint32 { + if x != nil { + return x.Count + } + return 0 +} + +func (x *TelemetryData_ResolveRate) GetReason() uint32 { + if x != nil { + return x.Reason + } + return 0 +} + +type TelemetryData_StateAge struct { + state protoimpl.MessageState `protogen:"open.v1"` + LastStateUpdate uint64 `protobuf:"varint,1,opt,name=last_state_update,json=lastStateUpdate,proto3" json:"last_state_update,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TelemetryData_StateAge) Reset() { + *x = TelemetryData_StateAge{} + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TelemetryData_StateAge) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryData_StateAge) ProtoMessage() {} + +func (x *TelemetryData_StateAge) ProtoReflect() protoreflect.Message { + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryData_StateAge.ProtoReflect.Descriptor instead. +func (*TelemetryData_StateAge) Descriptor() ([]byte, []int) { + return file_confidence_flags_resolver_v1_internal_api_proto_rawDescGZIP(), []int{3, 4} +} + +func (x *TelemetryData_StateAge) GetLastStateUpdate() uint64 { + if x != nil { + return x.LastStateUpdate + } + return 0 +} + +type TelemetryData_BucketSpan struct { + state protoimpl.MessageState `protogen:"open.v1"` + Offset int32 `protobuf:"varint,1,opt,name=offset,proto3" json:"offset,omitempty"` + Counts []uint32 `protobuf:"varint,2,rep,packed,name=counts,proto3" json:"counts,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TelemetryData_BucketSpan) Reset() { + *x = TelemetryData_BucketSpan{} + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TelemetryData_BucketSpan) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryData_BucketSpan) ProtoMessage() {} + +func (x *TelemetryData_BucketSpan) ProtoReflect() protoreflect.Message { + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[24] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryData_BucketSpan.ProtoReflect.Descriptor instead. +func (*TelemetryData_BucketSpan) Descriptor() ([]byte, []int) { + return file_confidence_flags_resolver_v1_internal_api_proto_rawDescGZIP(), []int{3, 5} +} + +func (x *TelemetryData_BucketSpan) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *TelemetryData_BucketSpan) GetCounts() []uint32 { + if x != nil { + return x.Counts + } + return nil +} + +type TelemetryData_ApplyDedupTelemetry struct { + state protoimpl.MessageState `protogen:"open.v1"` + AppliesTotal uint32 `protobuf:"varint,1,opt,name=applies_total,json=appliesTotal,proto3" json:"applies_total,omitempty"` + AppliesDeduped uint32 `protobuf:"varint,2,opt,name=applies_deduped,json=appliesDeduped,proto3" json:"applies_deduped,omitempty"` + AppliesNotCached uint32 `protobuf:"varint,3,opt,name=applies_not_cached,json=appliesNotCached,proto3" json:"applies_not_cached,omitempty"` + Sweeps uint32 `protobuf:"varint,4,opt,name=sweeps,proto3" json:"sweeps,omitempty"` + MapSize uint32 `protobuf:"varint,5,opt,name=map_size,json=mapSize,proto3" json:"map_size,omitempty"` + MapCapacity uint32 `protobuf:"varint,6,opt,name=map_capacity,json=mapCapacity,proto3" json:"map_capacity,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TelemetryData_ApplyDedupTelemetry) Reset() { + *x = TelemetryData_ApplyDedupTelemetry{} + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TelemetryData_ApplyDedupTelemetry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TelemetryData_ApplyDedupTelemetry) ProtoMessage() {} + +func (x *TelemetryData_ApplyDedupTelemetry) ProtoReflect() protoreflect.Message { + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[25] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TelemetryData_ApplyDedupTelemetry.ProtoReflect.Descriptor instead. +func (*TelemetryData_ApplyDedupTelemetry) Descriptor() ([]byte, []int) { + return file_confidence_flags_resolver_v1_internal_api_proto_rawDescGZIP(), []int{3, 6} +} + +func (x *TelemetryData_ApplyDedupTelemetry) GetAppliesTotal() uint32 { + if x != nil { + return x.AppliesTotal + } + return 0 +} + +func (x *TelemetryData_ApplyDedupTelemetry) GetAppliesDeduped() uint32 { + if x != nil { + return x.AppliesDeduped + } + return 0 +} + +func (x *TelemetryData_ApplyDedupTelemetry) GetAppliesNotCached() uint32 { + if x != nil { + return x.AppliesNotCached + } + return 0 +} + +func (x *TelemetryData_ApplyDedupTelemetry) GetSweeps() uint32 { + if x != nil { + return x.Sweeps + } + return 0 +} + +func (x *TelemetryData_ApplyDedupTelemetry) GetMapSize() uint32 { + if x != nil { + return x.MapSize + } + return 0 +} + +func (x *TelemetryData_ApplyDedupTelemetry) GetMapCapacity() uint32 { + if x != nil { + return x.MapCapacity + } + return 0 +} + type TelemetryData_ProviderInitRate struct { state protoimpl.MessageState `protogen:"open.v1"` Count uint32 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"` @@ -1191,7 +1661,7 @@ type TelemetryData_ProviderInitRate struct { func (x *TelemetryData_ProviderInitRate) Reset() { *x = TelemetryData_ProviderInitRate{} - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[19] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1203,7 +1673,7 @@ func (x *TelemetryData_ProviderInitRate) String() string { func (*TelemetryData_ProviderInitRate) ProtoMessage() {} func (x *TelemetryData_ProviderInitRate) ProtoReflect() protoreflect.Message { - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[19] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1216,7 +1686,7 @@ func (x *TelemetryData_ProviderInitRate) ProtoReflect() protoreflect.Message { // Deprecated: Use TelemetryData_ProviderInitRate.ProtoReflect.Descriptor instead. func (*TelemetryData_ProviderInitRate) Descriptor() ([]byte, []int) { - return file_confidence_flags_resolver_v1_internal_api_proto_rawDescGZIP(), []int{3, 0} + return file_confidence_flags_resolver_v1_internal_api_proto_rawDescGZIP(), []int{3, 7} } func (x *TelemetryData_ProviderInitRate) GetCount() uint32 { @@ -1253,7 +1723,7 @@ type FlagAssigned_AppliedFlag struct { func (x *FlagAssigned_AppliedFlag) Reset() { *x = FlagAssigned_AppliedFlag{} - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[21] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1265,7 +1735,7 @@ func (x *FlagAssigned_AppliedFlag) String() string { func (*FlagAssigned_AppliedFlag) ProtoMessage() {} func (x *FlagAssigned_AppliedFlag) ProtoReflect() protoreflect.Message { - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[21] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1381,7 +1851,7 @@ type FlagAssigned_AssignmentInfo struct { func (x *FlagAssigned_AssignmentInfo) Reset() { *x = FlagAssigned_AssignmentInfo{} - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[22] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1393,7 +1863,7 @@ func (x *FlagAssigned_AssignmentInfo) String() string { func (*FlagAssigned_AssignmentInfo) ProtoMessage() {} func (x *FlagAssigned_AssignmentInfo) ProtoReflect() protoreflect.Message { - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[22] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1432,7 +1902,7 @@ type FlagAssigned_DefaultAssignment struct { func (x *FlagAssigned_DefaultAssignment) Reset() { *x = FlagAssigned_DefaultAssignment{} - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[23] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1444,7 +1914,7 @@ func (x *FlagAssigned_DefaultAssignment) String() string { func (*FlagAssigned_DefaultAssignment) ProtoMessage() {} func (x *FlagAssigned_DefaultAssignment) ProtoReflect() protoreflect.Message { - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[23] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1478,7 +1948,7 @@ type ClientResolveInfo_EvaluationContextSchemaInstance struct { func (x *ClientResolveInfo_EvaluationContextSchemaInstance) Reset() { *x = ClientResolveInfo_EvaluationContextSchemaInstance{} - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[24] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1490,7 +1960,7 @@ func (x *ClientResolveInfo_EvaluationContextSchemaInstance) String() string { func (*ClientResolveInfo_EvaluationContextSchemaInstance) ProtoMessage() {} func (x *ClientResolveInfo_EvaluationContextSchemaInstance) ProtoReflect() protoreflect.Message { - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[24] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1526,7 +1996,7 @@ type FlagResolveInfo_VariantResolveInfo struct { func (x *FlagResolveInfo_VariantResolveInfo) Reset() { *x = FlagResolveInfo_VariantResolveInfo{} - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[26] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1538,7 +2008,7 @@ func (x *FlagResolveInfo_VariantResolveInfo) String() string { func (*FlagResolveInfo_VariantResolveInfo) ProtoMessage() {} func (x *FlagResolveInfo_VariantResolveInfo) ProtoReflect() protoreflect.Message { - mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[26] + mi := &file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1582,11 +2052,48 @@ const file_confidence_flags_resolver_v1_internal_api_proto_rawDesc = "" + "\x15IngestFlagLogsRequest\x12\x1d\n" + "\n" + "account_id\x18\x01 \x01(\tR\taccountId\x12H\n" + - "\x05batch\x18\x02 \x01(\v22.confidence.flags.resolver.v1.WriteFlagLogsRequestR\x05batch\"\xa9\x03\n" + + "\x05batch\x18\x02 \x01(\v22.confidence.flags.resolver.v1.WriteFlagLogsRequestR\x05batch\"\xfc\r\n" + "\rTelemetryData\x123\n" + - "\x03sdk\x18\x02 \x01(\v2!.confidence.flags.resolver.v1.SdkR\x03sdk\x12)\n" + + "\x03sdk\x18\x02 \x01(\v2!.confidence.flags.resolver.v1.SdkR\x03sdk\x12c\n" + + "\x0fresolve_latency\x18\x04 \x01(\v2:.confidence.flags.resolver.v1.TelemetryData.ResolveLatencyR\x0eresolveLatency\x12Z\n" + + "\fresolve_rate\x18\x05 \x03(\v27.confidence.flags.resolver.v1.TelemetryData.ResolveRateR\vresolveRate\x12Q\n" + + "\tstate_age\x18\x06 \x01(\v24.confidence.flags.resolver.v1.TelemetryData.StateAgeR\bstateAge\x12!\n" + + "\fmemory_bytes\x18\a \x01(\x04R\vmemoryBytes\x12)\n" + "\x10resolver_version\x18\b \x01(\tR\x0fresolverVersion\x12j\n" + - "\x12provider_init_rate\x18\t \x03(\v2<.confidence.flags.resolver.v1.TelemetryData.ProviderInitRateR\x10providerInitRate\x1a\xcb\x01\n" + + "\x12provider_init_rate\x18\t \x03(\v2<.confidence.flags.resolver.v1.TelemetryData.ProviderInitRateR\x10providerInitRate\x12`\n" + + "\vapply_dedup\x18\n" + + " \x01(\v2?.confidence.flags.resolver.v1.TelemetryData.ApplyDedupTelemetryR\n" + + "applyDedup\x12P\n" + + "\x05flush\x18\v \x01(\v2:.confidence.flags.resolver.v1.TelemetryData.FlushTelemetryR\x05flush\x12S\n" + + "\x06events\x18\f \x01(\v2;.confidence.flags.resolver.v1.TelemetryData.EventsTelemetryR\x06events\x1aF\n" + + "\x0eFlushTelemetry\x12\x1c\n" + + "\tsucceeded\x18\x01 \x01(\rR\tsucceeded\x12\x16\n" + + "\x06failed\x18\x02 \x01(\rR\x06failed\x1a\x83\x01\n" + + "\x0fEventsTelemetry\x12\x1c\n" + + "\tpublished\x18\x01 \x01(\rR\tpublished\x12+\n" + + "\x11batches_succeeded\x18\x02 \x01(\rR\x10batchesSucceeded\x12%\n" + + "\x0ebatches_failed\x18\x03 \x01(\rR\rbatchesFailed\x1a\xa5\x01\n" + + "\x0eResolveLatency\x12\x10\n" + + "\x03sum\x18\x01 \x01(\rR\x03sum\x12\x14\n" + + "\x05count\x18\x02 \x01(\rR\x05count\x12P\n" + + "\abuckets\x18\x03 \x03(\v26.confidence.flags.resolver.v1.TelemetryData.BucketSpanR\abuckets\x12\x19\n" + + "\bln_ratio\x18\x04 \x01(\x01R\alnRatio\x1a;\n" + + "\vResolveRate\x12\x14\n" + + "\x05count\x18\x01 \x01(\rR\x05count\x12\x16\n" + + "\x06reason\x18\x02 \x01(\rR\x06reason\x1a6\n" + + "\bStateAge\x12*\n" + + "\x11last_state_update\x18\x01 \x01(\x04R\x0flastStateUpdate\x1a<\n" + + "\n" + + "BucketSpan\x12\x16\n" + + "\x06offset\x18\x01 \x01(\x05R\x06offset\x12\x16\n" + + "\x06counts\x18\x02 \x03(\rR\x06counts\x1a\xe7\x01\n" + + "\x13ApplyDedupTelemetry\x12#\n" + + "\rapplies_total\x18\x01 \x01(\rR\fappliesTotal\x12'\n" + + "\x0fapplies_deduped\x18\x02 \x01(\rR\x0eappliesDeduped\x12,\n" + + "\x12applies_not_cached\x18\x03 \x01(\rR\x10appliesNotCached\x12\x16\n" + + "\x06sweeps\x18\x04 \x01(\rR\x06sweeps\x12\x19\n" + + "\bmap_size\x18\x05 \x01(\rR\amapSize\x12!\n" + + "\fmap_capacity\x18\x06 \x01(\rR\vmapCapacity\x1a\xcb\x01\n" + "\x10ProviderInitRate\x12\x14\n" + "\x05count\x18\x01 \x01(\rR\x05count\x12`\n" + "\x06labels\x18\x03 \x03(\v2H.confidence.flags.resolver.v1.TelemetryData.ProviderInitRate.LabelsEntryR\x06labels\x1a9\n" + @@ -1700,7 +2207,7 @@ func file_confidence_flags_resolver_v1_internal_api_proto_rawDescGZIP() []byte { } var file_confidence_flags_resolver_v1_internal_api_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_confidence_flags_resolver_v1_internal_api_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_confidence_flags_resolver_v1_internal_api_proto_msgTypes = make([]protoimpl.MessageInfo, 34) var file_confidence_flags_resolver_v1_internal_api_proto_goTypes = []any{ (FlagAssigned_DefaultAssignment_DefaultAssignmentReason)(0), // 0: confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment.DefaultAssignmentReason (*WriteFlagLogsRequest)(nil), // 1: confidence.flags.resolver.v1.WriteFlagLogsRequest @@ -1722,16 +2229,23 @@ var file_confidence_flags_resolver_v1_internal_api_proto_goTypes = []any{ (*InclusionData)(nil), // 17: confidence.flags.resolver.v1.InclusionData (*ReadResult)(nil), // 18: confidence.flags.resolver.v1.ReadResult (*ReadOperationsResult)(nil), // 19: confidence.flags.resolver.v1.ReadOperationsResult - (*TelemetryData_ProviderInitRate)(nil), // 20: confidence.flags.resolver.v1.TelemetryData.ProviderInitRate - nil, // 21: confidence.flags.resolver.v1.TelemetryData.ProviderInitRate.LabelsEntry - (*FlagAssigned_AppliedFlag)(nil), // 22: confidence.flags.resolver.v1.FlagAssigned.AppliedFlag - (*FlagAssigned_AssignmentInfo)(nil), // 23: confidence.flags.resolver.v1.FlagAssigned.AssignmentInfo - (*FlagAssigned_DefaultAssignment)(nil), // 24: confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment - (*ClientResolveInfo_EvaluationContextSchemaInstance)(nil), // 25: confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance - nil, // 26: confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance.SchemaEntry - (*FlagResolveInfo_VariantResolveInfo)(nil), // 27: confidence.flags.resolver.v1.FlagResolveInfo.VariantResolveInfo - (*resolver.Sdk)(nil), // 28: confidence.flags.resolver.v1.Sdk - (*timestamppb.Timestamp)(nil), // 29: google.protobuf.Timestamp + (*TelemetryData_FlushTelemetry)(nil), // 20: confidence.flags.resolver.v1.TelemetryData.FlushTelemetry + (*TelemetryData_EventsTelemetry)(nil), // 21: confidence.flags.resolver.v1.TelemetryData.EventsTelemetry + (*TelemetryData_ResolveLatency)(nil), // 22: confidence.flags.resolver.v1.TelemetryData.ResolveLatency + (*TelemetryData_ResolveRate)(nil), // 23: confidence.flags.resolver.v1.TelemetryData.ResolveRate + (*TelemetryData_StateAge)(nil), // 24: confidence.flags.resolver.v1.TelemetryData.StateAge + (*TelemetryData_BucketSpan)(nil), // 25: confidence.flags.resolver.v1.TelemetryData.BucketSpan + (*TelemetryData_ApplyDedupTelemetry)(nil), // 26: confidence.flags.resolver.v1.TelemetryData.ApplyDedupTelemetry + (*TelemetryData_ProviderInitRate)(nil), // 27: confidence.flags.resolver.v1.TelemetryData.ProviderInitRate + nil, // 28: confidence.flags.resolver.v1.TelemetryData.ProviderInitRate.LabelsEntry + (*FlagAssigned_AppliedFlag)(nil), // 29: confidence.flags.resolver.v1.FlagAssigned.AppliedFlag + (*FlagAssigned_AssignmentInfo)(nil), // 30: confidence.flags.resolver.v1.FlagAssigned.AssignmentInfo + (*FlagAssigned_DefaultAssignment)(nil), // 31: confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment + (*ClientResolveInfo_EvaluationContextSchemaInstance)(nil), // 32: confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance + nil, // 33: confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance.SchemaEntry + (*FlagResolveInfo_VariantResolveInfo)(nil), // 34: confidence.flags.resolver.v1.FlagResolveInfo.VariantResolveInfo + (*resolver.Sdk)(nil), // 35: confidence.flags.resolver.v1.Sdk + (*timestamppb.Timestamp)(nil), // 36: google.protobuf.Timestamp } var file_confidence_flags_resolver_v1_internal_api_proto_depIdxs = []int32{ 6, // 0: confidence.flags.resolver.v1.WriteFlagLogsRequest.flag_assigned:type_name -> confidence.flags.resolver.v1.FlagAssigned @@ -1739,38 +2253,45 @@ var file_confidence_flags_resolver_v1_internal_api_proto_depIdxs = []int32{ 8, // 2: confidence.flags.resolver.v1.WriteFlagLogsRequest.client_resolve_info:type_name -> confidence.flags.resolver.v1.ClientResolveInfo 9, // 3: confidence.flags.resolver.v1.WriteFlagLogsRequest.flag_resolve_info:type_name -> confidence.flags.resolver.v1.FlagResolveInfo 1, // 4: confidence.flags.resolver.v1.IngestFlagLogsRequest.batch:type_name -> confidence.flags.resolver.v1.WriteFlagLogsRequest - 28, // 5: confidence.flags.resolver.v1.TelemetryData.sdk:type_name -> confidence.flags.resolver.v1.Sdk - 20, // 6: confidence.flags.resolver.v1.TelemetryData.provider_init_rate:type_name -> confidence.flags.resolver.v1.TelemetryData.ProviderInitRate - 28, // 7: confidence.flags.resolver.v1.ClientInfo.sdk:type_name -> confidence.flags.resolver.v1.Sdk - 5, // 8: confidence.flags.resolver.v1.FlagAssigned.client_info:type_name -> confidence.flags.resolver.v1.ClientInfo - 22, // 9: confidence.flags.resolver.v1.FlagAssigned.flags:type_name -> confidence.flags.resolver.v1.FlagAssigned.AppliedFlag - 25, // 10: confidence.flags.resolver.v1.ClientResolveInfo.schema:type_name -> confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance - 27, // 11: confidence.flags.resolver.v1.FlagResolveInfo.variant_resolve_info:type_name -> confidence.flags.resolver.v1.FlagResolveInfo.VariantResolveInfo - 16, // 12: confidence.flags.resolver.v1.WriteOperationsRequest.store_variant_op:type_name -> confidence.flags.resolver.v1.VariantData - 12, // 13: confidence.flags.resolver.v1.ReadOp.variant_read_op:type_name -> confidence.flags.resolver.v1.VariantReadOp - 13, // 14: confidence.flags.resolver.v1.ReadOp.inclusion_read_op:type_name -> confidence.flags.resolver.v1.InclusionReadOp - 14, // 15: confidence.flags.resolver.v1.ReadOperationsRequest.ops:type_name -> confidence.flags.resolver.v1.ReadOp - 16, // 16: confidence.flags.resolver.v1.ReadResult.variant_result:type_name -> confidence.flags.resolver.v1.VariantData - 17, // 17: confidence.flags.resolver.v1.ReadResult.inclusion_result:type_name -> confidence.flags.resolver.v1.InclusionData - 18, // 18: confidence.flags.resolver.v1.ReadOperationsResult.results:type_name -> confidence.flags.resolver.v1.ReadResult - 21, // 19: confidence.flags.resolver.v1.TelemetryData.ProviderInitRate.labels:type_name -> confidence.flags.resolver.v1.TelemetryData.ProviderInitRate.LabelsEntry - 23, // 20: confidence.flags.resolver.v1.FlagAssigned.AppliedFlag.assignment_info:type_name -> confidence.flags.resolver.v1.FlagAssigned.AssignmentInfo - 24, // 21: confidence.flags.resolver.v1.FlagAssigned.AppliedFlag.default_assignment:type_name -> confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment - 7, // 22: confidence.flags.resolver.v1.FlagAssigned.AppliedFlag.fallthrough_assignments:type_name -> confidence.flags.resolver.v1.FallthroughAssignment - 29, // 23: confidence.flags.resolver.v1.FlagAssigned.AppliedFlag.apply_time:type_name -> google.protobuf.Timestamp - 0, // 24: confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment.reason:type_name -> confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment.DefaultAssignmentReason - 26, // 25: confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance.schema:type_name -> confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance.SchemaEntry - 1, // 26: confidence.flags.resolver.v1.InternalFlagLoggerService.ClientWriteFlagLogs:input_type -> confidence.flags.resolver.v1.WriteFlagLogsRequest - 10, // 27: confidence.flags.resolver.v1.InternalFlagLoggerService.WriteMaterializedOperations:input_type -> confidence.flags.resolver.v1.WriteOperationsRequest - 15, // 28: confidence.flags.resolver.v1.InternalFlagLoggerService.ReadMaterializedOperations:input_type -> confidence.flags.resolver.v1.ReadOperationsRequest - 2, // 29: confidence.flags.resolver.v1.InternalFlagLoggerService.ClientWriteFlagLogs:output_type -> confidence.flags.resolver.v1.WriteFlagLogsResponse - 11, // 30: confidence.flags.resolver.v1.InternalFlagLoggerService.WriteMaterializedOperations:output_type -> confidence.flags.resolver.v1.WriteOperationsResult - 19, // 31: confidence.flags.resolver.v1.InternalFlagLoggerService.ReadMaterializedOperations:output_type -> confidence.flags.resolver.v1.ReadOperationsResult - 29, // [29:32] is the sub-list for method output_type - 26, // [26:29] is the sub-list for method input_type - 26, // [26:26] is the sub-list for extension type_name - 26, // [26:26] is the sub-list for extension extendee - 0, // [0:26] is the sub-list for field type_name + 35, // 5: confidence.flags.resolver.v1.TelemetryData.sdk:type_name -> confidence.flags.resolver.v1.Sdk + 22, // 6: confidence.flags.resolver.v1.TelemetryData.resolve_latency:type_name -> confidence.flags.resolver.v1.TelemetryData.ResolveLatency + 23, // 7: confidence.flags.resolver.v1.TelemetryData.resolve_rate:type_name -> confidence.flags.resolver.v1.TelemetryData.ResolveRate + 24, // 8: confidence.flags.resolver.v1.TelemetryData.state_age:type_name -> confidence.flags.resolver.v1.TelemetryData.StateAge + 27, // 9: confidence.flags.resolver.v1.TelemetryData.provider_init_rate:type_name -> confidence.flags.resolver.v1.TelemetryData.ProviderInitRate + 26, // 10: confidence.flags.resolver.v1.TelemetryData.apply_dedup:type_name -> confidence.flags.resolver.v1.TelemetryData.ApplyDedupTelemetry + 20, // 11: confidence.flags.resolver.v1.TelemetryData.flush:type_name -> confidence.flags.resolver.v1.TelemetryData.FlushTelemetry + 21, // 12: confidence.flags.resolver.v1.TelemetryData.events:type_name -> confidence.flags.resolver.v1.TelemetryData.EventsTelemetry + 35, // 13: confidence.flags.resolver.v1.ClientInfo.sdk:type_name -> confidence.flags.resolver.v1.Sdk + 5, // 14: confidence.flags.resolver.v1.FlagAssigned.client_info:type_name -> confidence.flags.resolver.v1.ClientInfo + 29, // 15: confidence.flags.resolver.v1.FlagAssigned.flags:type_name -> confidence.flags.resolver.v1.FlagAssigned.AppliedFlag + 32, // 16: confidence.flags.resolver.v1.ClientResolveInfo.schema:type_name -> confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance + 34, // 17: confidence.flags.resolver.v1.FlagResolveInfo.variant_resolve_info:type_name -> confidence.flags.resolver.v1.FlagResolveInfo.VariantResolveInfo + 16, // 18: confidence.flags.resolver.v1.WriteOperationsRequest.store_variant_op:type_name -> confidence.flags.resolver.v1.VariantData + 12, // 19: confidence.flags.resolver.v1.ReadOp.variant_read_op:type_name -> confidence.flags.resolver.v1.VariantReadOp + 13, // 20: confidence.flags.resolver.v1.ReadOp.inclusion_read_op:type_name -> confidence.flags.resolver.v1.InclusionReadOp + 14, // 21: confidence.flags.resolver.v1.ReadOperationsRequest.ops:type_name -> confidence.flags.resolver.v1.ReadOp + 16, // 22: confidence.flags.resolver.v1.ReadResult.variant_result:type_name -> confidence.flags.resolver.v1.VariantData + 17, // 23: confidence.flags.resolver.v1.ReadResult.inclusion_result:type_name -> confidence.flags.resolver.v1.InclusionData + 18, // 24: confidence.flags.resolver.v1.ReadOperationsResult.results:type_name -> confidence.flags.resolver.v1.ReadResult + 25, // 25: confidence.flags.resolver.v1.TelemetryData.ResolveLatency.buckets:type_name -> confidence.flags.resolver.v1.TelemetryData.BucketSpan + 28, // 26: confidence.flags.resolver.v1.TelemetryData.ProviderInitRate.labels:type_name -> confidence.flags.resolver.v1.TelemetryData.ProviderInitRate.LabelsEntry + 30, // 27: confidence.flags.resolver.v1.FlagAssigned.AppliedFlag.assignment_info:type_name -> confidence.flags.resolver.v1.FlagAssigned.AssignmentInfo + 31, // 28: confidence.flags.resolver.v1.FlagAssigned.AppliedFlag.default_assignment:type_name -> confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment + 7, // 29: confidence.flags.resolver.v1.FlagAssigned.AppliedFlag.fallthrough_assignments:type_name -> confidence.flags.resolver.v1.FallthroughAssignment + 36, // 30: confidence.flags.resolver.v1.FlagAssigned.AppliedFlag.apply_time:type_name -> google.protobuf.Timestamp + 0, // 31: confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment.reason:type_name -> confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment.DefaultAssignmentReason + 33, // 32: confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance.schema:type_name -> confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance.SchemaEntry + 1, // 33: confidence.flags.resolver.v1.InternalFlagLoggerService.ClientWriteFlagLogs:input_type -> confidence.flags.resolver.v1.WriteFlagLogsRequest + 10, // 34: confidence.flags.resolver.v1.InternalFlagLoggerService.WriteMaterializedOperations:input_type -> confidence.flags.resolver.v1.WriteOperationsRequest + 15, // 35: confidence.flags.resolver.v1.InternalFlagLoggerService.ReadMaterializedOperations:input_type -> confidence.flags.resolver.v1.ReadOperationsRequest + 2, // 36: confidence.flags.resolver.v1.InternalFlagLoggerService.ClientWriteFlagLogs:output_type -> confidence.flags.resolver.v1.WriteFlagLogsResponse + 11, // 37: confidence.flags.resolver.v1.InternalFlagLoggerService.WriteMaterializedOperations:output_type -> confidence.flags.resolver.v1.WriteOperationsResult + 19, // 38: confidence.flags.resolver.v1.InternalFlagLoggerService.ReadMaterializedOperations:output_type -> confidence.flags.resolver.v1.ReadOperationsResult + 36, // [36:39] is the sub-list for method output_type + 33, // [33:36] is the sub-list for method input_type + 33, // [33:33] is the sub-list for extension type_name + 33, // [33:33] is the sub-list for extension extendee + 0, // [0:33] is the sub-list for field type_name } func init() { file_confidence_flags_resolver_v1_internal_api_proto_init() } @@ -1786,7 +2307,7 @@ func file_confidence_flags_resolver_v1_internal_api_proto_init() { (*ReadResult_VariantResult)(nil), (*ReadResult_InclusionResult)(nil), } - file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[21].OneofWrappers = []any{ + file_confidence_flags_resolver_v1_internal_api_proto_msgTypes[28].OneofWrappers = []any{ (*FlagAssigned_AppliedFlag_AssignmentInfo)(nil), (*FlagAssigned_AppliedFlag_DefaultAssignment)(nil), } @@ -1796,7 +2317,7 @@ func file_confidence_flags_resolver_v1_internal_api_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_confidence_flags_resolver_v1_internal_api_proto_rawDesc), len(file_confidence_flags_resolver_v1_internal_api_proto_rawDesc)), NumEnums: 1, - NumMessages: 27, + NumMessages: 34, NumExtensions: 0, NumServices: 1, }, diff --git a/openfeature-provider/go/confidence/internal/testutil/helpers.go b/openfeature-provider/go/confidence/internal/testutil/helpers.go index 53de8c80f..30de97038 100644 --- a/openfeature-provider/go/confidence/internal/testutil/helpers.go +++ b/openfeature-provider/go/confidence/internal/testutil/helpers.go @@ -52,6 +52,8 @@ func (m *MockFlagLogger) Write(request *resolverv1.WriteFlagLogsRequest) { m.writeFunc(request) } +func (m *MockFlagLogger) RecordEventBatch(eventCount int, succeeded bool) {} + type StateProviderMock struct { AccountID string State []byte diff --git a/openfeature-provider/go/confidence/provider.go b/openfeature-provider/go/confidence/provider.go index 44dd855ad..99057ffdf 100644 --- a/openfeature-provider/go/confidence/provider.go +++ b/openfeature-provider/go/confidence/provider.go @@ -632,9 +632,16 @@ func (p *LocalResolverProvider) flushAndPublishEvents(ctx context.Context) int { return 0 } - if err := p.publishEvents(ctx, batch); err != nil { + eventCount := len(batch.Events) + rejected, err := p.publishEvents(ctx, batch) + if err != nil { p.eventPublishFailures.Add(1) p.logger.Debug("Failed to publish events", "error", err) + if recorder, ok := p.flagLogger.(interface{ RecordEventBatch(int, bool) }); ok { + recorder.RecordEventBatch(eventCount, false) + } + } else if recorder, ok := p.flagLogger.(interface{ RecordEventBatch(int, bool) }); ok { + recorder.RecordEventBatch(eventCount-rejected, true) } if p.eventPublishAttempts.Add(1)%eventPublishLogWindow == 0 { @@ -671,9 +678,9 @@ func (p *LocalResolverProvider) drainEvents(ctx context.Context) { // publishEvents wraps a flushed batch in a PublishEventsRequest and sends it to // the Confidence events service. -func (p *LocalResolverProvider) publishEvents(ctx context.Context, batch *eventswasm.FlushEventsResponse) error { +func (p *LocalResolverProvider) publishEvents(ctx context.Context, batch *eventswasm.FlushEventsResponse) (int, error) { if p.eventsClient == nil { - return errors.New("events service client is not configured") + return 0, errors.New("events service client is not configured") } rpcCtx, cancel := context.WithTimeout(ctx, eventsPublishTimeout) @@ -688,9 +695,10 @@ func (p *LocalResolverProvider) publishEvents(ctx context.Context, batch *events response, err := p.eventsClient.PublishEvents(rpcCtx, request) if err != nil { - return fmt.Errorf("failed to publish events: %w", err) + return 0, fmt.Errorf("failed to publish events: %w", err) } + rejected := len(response.GetErrors()) for _, e := range response.GetErrors() { p.logger.Error("Event publish error", "index", e.GetIndex(), @@ -698,7 +706,7 @@ func (p *LocalResolverProvider) publishEvents(ctx context.Context, batch *events "message", e.GetMessage()) } - return nil + return rejected, nil } // Resolve resolves multiple flags for the given context. If flagNames is empty, diff --git a/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/GrpcWasmFlagLogger.java b/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/GrpcWasmFlagLogger.java index ad7f7e342..14b190752 100644 --- a/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/GrpcWasmFlagLogger.java +++ b/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/GrpcWasmFlagLogger.java @@ -6,6 +6,7 @@ import com.spotify.confidence.sdk.flags.admin.v1.LogDestination; import com.spotify.confidence.sdk.flags.resolver.v1.IngestFlagLogsRequest; import com.spotify.confidence.sdk.flags.resolver.v1.InternalFlagLoggerServiceGrpc; +import com.spotify.confidence.sdk.flags.resolver.v1.TelemetryData; import com.spotify.confidence.sdk.flags.resolver.v1.WriteFlagLogsRequest; import io.grpc.*; import java.io.OutputStream; @@ -38,6 +39,11 @@ public class GrpcWasmFlagLogger implements WasmFlagLogger { private final Duration shutdownTimeout; private final AtomicLong attempts = new AtomicLong(); private final AtomicLong failures = new AtomicLong(); + private final AtomicLong telemetryFlushSucceeded = new AtomicLong(); + private final AtomicLong telemetryFlushFailed = new AtomicLong(); + private final AtomicLong restoredEventsPublished = new AtomicLong(); + private final AtomicLong restoredEventBatchesSucceeded = new AtomicLong(); + private final AtomicLong restoredEventBatchesFailed = new AtomicLong(); private final String clientSecret; private final HttpClientFactory httpClientFactory; private final AtomicReference> logDestinations = @@ -127,13 +133,22 @@ private void sendWithFailover(WriteFlagLogsRequest request) { // Default to Edge when no destinations configured if (destinations.isEmpty()) { - sendToEdge(request); + try { + sendToEdge(request); + telemetryFlushSucceeded.incrementAndGet(); + } catch (Exception e) { + failures.incrementAndGet(); + telemetryFlushFailed.incrementAndGet(); + restoreDrainedCounters(request); + logger.debug("Edge flag log delivery failed", e); + } return; } final LogDestination primary = destinations.get(0); try { sendToDestination(primary, request); + telemetryFlushSucceeded.incrementAndGet(); logger.debug( "Successfully sent flag log via {} with {} assigned, {} client_resolve_info, {} flag_resolve_info", primary, @@ -146,6 +161,7 @@ private void sendWithFailover(WriteFlagLogsRequest request) { logger.warn("Primary destination {} failed, trying fallback {}", primary, fallback); try { sendToDestination(fallback, request); + telemetryFlushSucceeded.incrementAndGet(); logger.debug( "Successfully sent flag log via fallback {} with {} assigned, {} client_resolve_info, {} flag_resolve_info", fallback, @@ -154,14 +170,31 @@ private void sendWithFailover(WriteFlagLogsRequest request) { request.getFlagResolveInfoCount()); } catch (Exception fallbackEx) { failures.incrementAndGet(); + telemetryFlushFailed.incrementAndGet(); + restoreDrainedCounters(request); logger.warn("Fallback destination {} also failed", fallback, fallbackEx); } } else { failures.incrementAndGet(); + telemetryFlushFailed.incrementAndGet(); + restoreDrainedCounters(request); } } } + private void restoreDrainedCounters(WriteFlagLogsRequest request) { + final TelemetryData td = request.getTelemetryData(); + if (td.hasFlush()) { + telemetryFlushSucceeded.addAndGet(td.getFlush().getSucceeded()); + telemetryFlushFailed.addAndGet(td.getFlush().getFailed()); + } + if (td.hasEvents()) { + restoredEventsPublished.addAndGet(td.getEvents().getPublished()); + restoredEventBatchesSucceeded.addAndGet(td.getEvents().getBatchesSucceeded()); + restoredEventBatchesFailed.addAndGet(td.getEvents().getBatchesFailed()); + } + } + private void sendToDestination(LogDestination destination, WriteFlagLogsRequest request) { switch (destination) { case LOG_DESTINATION_CLOUDFLARE: @@ -207,6 +240,20 @@ private void sendToCloudflare(WriteFlagLogsRequest request) { } } + @Override + public long[] drainFlushCounters() { + return new long[] {telemetryFlushSucceeded.getAndSet(0), telemetryFlushFailed.getAndSet(0)}; + } + + @Override + public long[] drainRestoredEventCounters() { + return new long[] { + restoredEventsPublished.getAndSet(0), + restoredEventBatchesSucceeded.getAndSet(0), + restoredEventBatchesFailed.getAndSet(0) + }; + } + /** * Shutdown the executor service and wait for pending async writes to complete. This method will * block for up to the configured shutdown timeout (default 10 seconds) waiting for pending log diff --git a/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/OpenFeatureLocalResolveProvider.java b/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/OpenFeatureLocalResolveProvider.java index 4b80c0929..7144ef650 100644 --- a/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/OpenFeatureLocalResolveProvider.java +++ b/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/OpenFeatureLocalResolveProvider.java @@ -83,6 +83,9 @@ public class OpenFeatureLocalResolveProvider implements FeatureProvider { private final AtomicLong eventPublishAttempts = new AtomicLong(); private final AtomicLong eventPublishFailures = new AtomicLong(); + private final AtomicLong telemetryEventsPublished = new AtomicLong(); + private final AtomicLong telemetryEventBatchesSucceeded = new AtomicLong(); + private final AtomicLong telemetryEventBatchesFailed = new AtomicLong(); private final ScheduledExecutorService flagsFetcherExecutor = newFlagsFetcherExecutor(); private final ScheduledExecutorService assignLogExecutor = Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder().setDaemon(true).build()); @@ -208,6 +211,9 @@ public OpenFeatureLocalResolveProvider( final LocalResolver telemetryResolver = new ProviderTelemetryResolver( flagLogger::write, + wasmFlagLogger::drainFlushCounters, + this::drainEventCounters, + wasmFlagLogger::drainRestoredEventCounters, SDK, initLabels, providerLogSink -> @@ -282,6 +288,9 @@ public OpenFeatureLocalResolveProvider( final LocalResolver telemetryResolver = new ProviderTelemetryResolver( wasmFlagLogger::write, + wasmFlagLogger::drainFlushCounters, + this::drainEventCounters, + wasmFlagLogger::drainRestoredEventCounters, SDK, Map.of(), providerLogSink -> @@ -775,6 +784,9 @@ private void sendEvents(FlushEventsResponse batch) { .build(); try { final PublishEventsResponse response = eventsStub.publishEvents(request); + final int rejected = response.getErrorsCount(); + telemetryEventsPublished.addAndGet(batch.getEventsCount() - rejected); + telemetryEventBatchesSucceeded.incrementAndGet(); for (final EventError error : response.getErrorsList()) { log.error( "Failed to publish event at index {}: {} {}", @@ -784,6 +796,7 @@ private void sendEvents(FlushEventsResponse batch) { } } catch (StatusRuntimeException e) { eventPublishFailures.incrementAndGet(); + telemetryEventBatchesFailed.incrementAndGet(); log.warn("Failed to send events", e); } if (eventPublishAttempts.incrementAndGet() % EVENT_STATS_WINDOW == 0) { @@ -794,6 +807,14 @@ private void sendEvents(FlushEventsResponse batch) { } } + long[] drainEventCounters() { + return new long[] { + telemetryEventsPublished.getAndSet(0), + telemetryEventBatchesSucceeded.getAndSet(0), + telemetryEventBatchesFailed.getAndSet(0) + }; + } + private void doRegisterResolve(ResolveReason reason, long startNanos) { long latencyUs = (System.nanoTime() - startNanos) / 1000; try { diff --git a/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/ProviderTelemetryResolver.java b/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/ProviderTelemetryResolver.java index cf9dbc80d..f4f2a32ef 100644 --- a/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/ProviderTelemetryResolver.java +++ b/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/ProviderTelemetryResolver.java @@ -11,10 +11,14 @@ import java.util.concurrent.CompletionStage; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; /** Owns provider-scoped telemetry above the resolver pool and recovery layers. */ final class ProviderTelemetryResolver implements LocalResolver { private final Consumer logSink; + private final Supplier flushCounterDrain; + private final Supplier eventCounterDrain; + private final Supplier restoredEventCounterDrain; private final Sdk sdk; private final Map labels; private final LocalResolver delegate; @@ -25,18 +29,99 @@ final class ProviderTelemetryResolver implements LocalResolver { Sdk sdk, Map labels, Function, LocalResolver> innerFactory) { + this(logSink, () -> new long[] {0, 0}, () -> new long[] {0, 0, 0}, sdk, labels, innerFactory); + } + + ProviderTelemetryResolver( + Consumer logSink, + Supplier flushCounterDrain, + Sdk sdk, + Map labels, + Function, LocalResolver> innerFactory) { + this(logSink, flushCounterDrain, () -> new long[] {0, 0, 0}, sdk, labels, innerFactory); + } + + ProviderTelemetryResolver( + Consumer logSink, + Supplier flushCounterDrain, + Supplier eventCounterDrain, + Sdk sdk, + Map labels, + Function, LocalResolver> innerFactory) { + this( + logSink, + flushCounterDrain, + eventCounterDrain, + () -> new long[] {0, 0, 0}, + sdk, + labels, + innerFactory); + } + + ProviderTelemetryResolver( + Consumer logSink, + Supplier flushCounterDrain, + Supplier eventCounterDrain, + Supplier restoredEventCounterDrain, + Sdk sdk, + Map labels, + Function, LocalResolver> innerFactory) { this.logSink = logSink; + this.flushCounterDrain = flushCounterDrain; + this.eventCounterDrain = eventCounterDrain; + this.restoredEventCounterDrain = restoredEventCounterDrain; this.sdk = sdk; this.labels = Map.copyOf(labels); this.delegate = innerFactory.apply(this::writeLogs); } private synchronized void writeLogs(WriteFlagLogsRequest request) { - final WriteFlagLogsRequest outgoing = initSent ? request : addInitTelemetry(request); + WriteFlagLogsRequest outgoing = initSent ? request : addInitTelemetry(request); + outgoing = addFlushCounters(outgoing); + outgoing = addEventCounters(outgoing); logSink.accept(outgoing); initSent = true; } + private WriteFlagLogsRequest addFlushCounters(WriteFlagLogsRequest request) { + final long[] counters = flushCounterDrain.get(); + if (counters[0] == 0 && counters[1] == 0) { + return request; + } + return request.toBuilder() + .setTelemetryData( + request.getTelemetryData().toBuilder() + .setFlush( + TelemetryData.FlushTelemetry.newBuilder() + .setSucceeded((int) counters[0]) + .setFailed((int) counters[1]) + .build()) + .build()) + .build(); + } + + private WriteFlagLogsRequest addEventCounters(WriteFlagLogsRequest request) { + final long[] counters = eventCounterDrain.get(); + final long[] restored = restoredEventCounterDrain.get(); + final long published = counters[0] + restored[0]; + final long succeeded = counters[1] + restored[1]; + final long failed = counters[2] + restored[2]; + if (published == 0 && succeeded == 0 && failed == 0) { + return request; + } + return request.toBuilder() + .setTelemetryData( + request.getTelemetryData().toBuilder() + .setEvents( + TelemetryData.EventsTelemetry.newBuilder() + .setPublished((int) published) + .setBatchesSucceeded((int) succeeded) + .setBatchesFailed((int) failed) + .build()) + .build()) + .build(); + } + private synchronized void emitInitIfPending() { if (initSent) { return; diff --git a/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/WasmFlagLogger.java b/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/WasmFlagLogger.java index 14c5960cc..dcfff5f90 100644 --- a/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/WasmFlagLogger.java +++ b/openfeature-provider/java/src/main/java/com/spotify/confidence/sdk/WasmFlagLogger.java @@ -18,4 +18,20 @@ interface WasmFlagLogger { default void updateLogRouting(List destinations, String accountId) { // no-op by default for test implementations } + + /** + * Atomically reads and resets the flush delivery counters. Returns [succeeded, failed]. Called by + * the telemetry layer to merge delivery stats into the next TelemetryData. + */ + default long[] drainFlushCounters() { + return new long[] {0, 0}; + } + + /** + * Drains event counters restored from failed sends. Returns [published, succeeded, failed]. These + * are merged with the provider's own event counters before the next send. + */ + default long[] drainRestoredEventCounters() { + return new long[] {0, 0, 0}; + } } diff --git a/openfeature-provider/java/wrangler-test.toml b/openfeature-provider/java/wrangler-test.toml new file mode 100644 index 000000000..326065a08 --- /dev/null +++ b/openfeature-provider/java/wrangler-test.toml @@ -0,0 +1,32 @@ +name = "confidence-telemetry-test" +main = "build/worker/shim.mjs" +compatibility_date = "2025-01-28" + +[observability] +enabled = true + +[[queues.consumers]] +queue = "telemetry-test-flag-logs" +max_batch_size = 100 +max_batch_timeout = 10 + +[[queues.producers]] +queue = "telemetry-test-flag-logs" +binding = "flag_logs_queue" + +[[queues.consumers]] +queue = "telemetry-test-events" +max_batch_size = 100 +max_batch_timeout = 10 + +[[queues.producers]] +queue = "telemetry-test-events" +binding = "events_queue" + +[[kv_namespaces]] +binding = "CONFIDENCE_METRICS_KV" +id = "placeholder" + +[vars] +CONFIDENCE_CLIENT_SECRET = "mkjJruAATQWjeY7foFIWfVAcBWnci2YF" +ENABLE_APPLY_DEDUP = "true" diff --git a/openfeature-provider/js/package-lock.json b/openfeature-provider/js/package-lock.json new file mode 100644 index 000000000..e3048124b --- /dev/null +++ b/openfeature-provider/js/package-lock.json @@ -0,0 +1,3221 @@ +{ + "name": "@spotify-confidence/openfeature-server-provider-local", + "version": "0.21.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@spotify-confidence/openfeature-server-provider-local", + "version": "0.21.0", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "^2.9.0" + }, + "devDependencies": { + "@openfeature/core": "^1.9.0", + "@openfeature/server-sdk": "^1.19.0", + "@spotify/prettier-config": "^15.0.0", + "@testing-library/dom": "^10.4.1", + "@testing-library/react": "^16.3.2", + "@types/debug": "^4", + "@types/node": "^24.0.1", + "@types/react": "^19", + "@types/react-dom": "^19", + "@vitest/coverage-v8": "^4.1.7", + "debug": "^4.4.3", + "dotenv": "^17.2.2", + "happy-dom": "^20.3.4", + "next": "^16.0.0", + "prettier": "^2.8.8", + "react": "^19", + "react-dom": "^19.2.3", + "rolldown": "1.0.0-beta.38", + "ts-proto": "^2.7.3", + "tsdown": "latest", + "typescript": "^5.9.3", + "vitest": "^4.1.7" + }, + "peerDependencies": { + "@openfeature/core": "^1.0.0", + "debug": "^4.4.3", + "next": ">=13.0.0", + "react": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@openfeature/core": { + "optional": true + }, + "debug": { + "optional": true + }, + "next": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "dev": true, + "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/helper-string-parser": { + "version": "7.29.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.9.0", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, + "node_modules/@next/env": { + "version": "16.2.12", + "dev": true, + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.2.12", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@openfeature/core": { + "version": "1.9.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@openfeature/server-sdk": { + "version": "1.19.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@openfeature/core": "^1.9.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.89.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@quansync/fs": { + "version": "1.0.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@quansync/fs/-/fs-1.0.0.tgz", + "integrity": "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.6.tgz", + "integrity": "sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.6.tgz", + "integrity": "sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-beta.38", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.6.tgz", + "integrity": "sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.6.tgz", + "integrity": "sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.6.tgz", + "integrity": "sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.6.tgz", + "integrity": "sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.6.tgz", + "integrity": "sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.6.tgz", + "integrity": "sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.6.tgz", + "integrity": "sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.6.tgz", + "integrity": "sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.6.tgz", + "integrity": "sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.6.tgz", + "integrity": "sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.38.tgz", + "integrity": "sha512-fnLtHyjwEsG4/aNV3Uv3Qd1ZbdH+CopwJNoV0RgBqrcQB8V6/Qdikd5JKvnO23kb3QvIpP+dAMGZMv1c2PJMzw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.6.tgz", + "integrity": "sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-ia32-msvc": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.38.tgz", + "integrity": "sha512-HcICm4YzFJZV+fI0O0bFLVVlsWvRNo/AB9EfUXvNYbtAxakCnQZ15oq22deFdz6sfi9Y4/SagH2kPU723dhCFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.6.tgz", + "integrity": "sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.38", + "dev": true, + "license": "MIT" + }, + "node_modules/@spotify/prettier-config": { + "version": "15.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "prettier": "2.x" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.12.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.9", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/whatwg-mimetype": { + "version": "3.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws/node_modules/@types/node": { + "version": "25.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/ws/node_modules/undici-types": { + "version": "7.16.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.8", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.8", + "vitest": "4.1.8" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.8", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.8", + "@vitest/utils": "4.1.8", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.8", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.8", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@yuku-codegen/binding-android-arm64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-android-arm64/-/binding-android-arm64-0.8.7.tgz", + "integrity": "sha512-C/0zV5IhgVdYhGJTwrY0v8dknxlhiKwtVJkMUaexu9/QvRmzlV4vfU3hZlUSgqc2BxQHntL1mCVbDq8j0FRFDw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@yuku-codegen/binding-darwin-arm64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-darwin-arm64/-/binding-darwin-arm64-0.8.7.tgz", + "integrity": "sha512-/u+REDMI4a0/lsJXTM4c53/w31OGZLOReZIyg62uhgLs0kc8NHsj/nOcxTdlQjq5gi0zhdkccD9LaLTXcdzPvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-codegen/binding-darwin-x64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-darwin-x64/-/binding-darwin-x64-0.8.7.tgz", + "integrity": "sha512-sMMzFOwCo4WXR+/6zIBThOocSC50iIIZZdfiIDbaLvj0Ax/rWt/iavyfEAqajyvzydLyCqR/ZItdLWSRlu1umw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-codegen/binding-freebsd-x64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-freebsd-x64/-/binding-freebsd-x64-0.8.7.tgz", + "integrity": "sha512-MpdpKXix9P+Y1rKgjvcNeNtGjXeL1CmttNhYINrWls8kRpm4xM/oBGTmn6w7to8lAlwj5jm8q03dQPl5mRv4Qw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm-gnu": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.8.7.tgz", + "integrity": "sha512-rr1srFLlPAmC1vtxfc9C1YLDe3iH09YjfSeeIidBqKhzx1MATjOAq4mjlRUOnhr/L27MotWIYOFKwVsd5JZFOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm-musl": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-linux-arm-musl/-/binding-linux-arm-musl-0.8.7.tgz", + "integrity": "sha512-eAufXh8qBRpiSO6ueaMDL+yyoXIGLhpUce72YbcACtZU2qhExwBIJyEtQf5kH2Ki0X2aqkSjSfog4OPtKXan3Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm64-gnu": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.8.7.tgz", + "integrity": "sha512-gw4w6wPoHObBrdIC4duVWLmJOvpdE25j5D7yrM5mACNlK4klRz/lv8hK+ssQk9EJHBgZjSaqZIJVFgqNYbfv7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm64-musl": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.8.7.tgz", + "integrity": "sha512-L68N6Y4XkqcIaKo3Ra88JEvBEH4AHff44A4INcrxeVWZ8CZtu2tCpfVxe3hR8qQQMcvBSQlDn24KpkCKEhVvfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-x64-gnu": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.8.7.tgz", + "integrity": "sha512-dzyAbltJmf3Cqlb8HcFuYIf5Yn0fl1vTr3XJ9HiVNNvOlhqPSArOqtw9vI1p6/VTXTjrMLXlU+s+/kNHiIy/Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-x64-musl": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-linux-x64-musl/-/binding-linux-x64-musl-0.8.7.tgz", + "integrity": "sha512-Otw4MH3404q0Bbvl+YTdW9aoUV5vXmUw8260bWvt1XlaoIX/ceSgI4ygheRDMfBPoHt6FDayQaO9OLVUZAgkFA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-win32-arm64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-win32-arm64/-/binding-win32-arm64-0.8.7.tgz", + "integrity": "sha512-qo/jyrzryiBuKEsFiuWaBCBe3tRMynQ0qFWFgOEjcCMQeZfBm+wKiVEUEFXXLc7bh8YezguAWp0Mtnhq4ARNyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-codegen/binding-win32-x64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-codegen/binding-win32-x64/-/binding-win32-x64-0.8.7.tgz", + "integrity": "sha512-D5lDsVDx6m00E6bWySlWdH72Ca4TPSaphDqB6QjU6MpuNLIJqoGoatYyq2rOmBE8Zv/kunot/o58KGL03P3eiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-parser/binding-android-arm64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-android-arm64/-/binding-android-arm64-0.8.7.tgz", + "integrity": "sha512-eGKYiUDX7Y0V7tDTmg+JTVnXnjMqfXXsorZ+EDf5kxwchQ3Or1HS14MzI2fw+jFhHR85fCWt+mtX33Yao73hIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@yuku-parser/binding-darwin-arm64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.8.7.tgz", + "integrity": "sha512-Re0RHelKLnjEURulY2/KxW+Ngb8zuNA4BRZuMwgGQNzVumT6u4U2N2hc01oeYVNVof0i7GrXE4UCNBgbpRRnjQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-parser/binding-darwin-x64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-darwin-x64/-/binding-darwin-x64-0.8.7.tgz", + "integrity": "sha512-Hn8DROtQkjlA1ACbPgj4a7eP9IuVOI504oiTwpkWPbpaDWD9KdmnVYCqW+1LfenNK/g7O9NhWGpXEdaCNX7lIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-parser/binding-freebsd-x64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.8.7.tgz", + "integrity": "sha512-bAP2OV8wRuzplX/jYxv9+vvqQT8JxyNphI8fLfXGL054Xs+4/J5u33cIm3y4rxY8rdoLmmdiJs2Tq7r7lrDRfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm-gnu": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.8.7.tgz", + "integrity": "sha512-kTYwJQQgmZeAWdDIWabiReIZMpmfLueIj1tCmjStUtFGhR1Z0qwxonKVfUC4N7h/VhGGzLZ//7O1kgt1QKqgCg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm-musl": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-linux-arm-musl/-/binding-linux-arm-musl-0.8.7.tgz", + "integrity": "sha512-uL4jE8HPT2BLlxAXyD10LqgPuXa9eDa0BKpCdSANmzIJghq/2eZo3/gQNtaxPZMupWoxjYzSad9IXrwu7aYPXQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm64-gnu": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.8.7.tgz", + "integrity": "sha512-3gVN4pWSKZmXiNX7cU164dR9MPvesCHnlH6nPfpK+yQsCuYphjKKplcb4SnZBrhiqmXbgb2HR0c2TS0IZUPhgA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm64-musl": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.8.7.tgz", + "integrity": "sha512-S0mwfEjoLpxzXeZw802Wa4RaELsQiPtWqG6INcy8j4GtvNFtl4LCX3eGO1XLn9pyLAISLzTRyU3zUCBUPin8lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-x64-gnu": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.8.7.tgz", + "integrity": "sha512-lnbWdPmerE5D1uH1G4IEZKnPzCrWCStRGrtgpSIe1RibAo5bZIjDbbbPYXmMHCEh4F+x/JaJpElh26a3r+BPbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-x64-musl": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.8.7.tgz", + "integrity": "sha512-769uwndMvMzUvATWbAcEvyLHKA+DzhHSCl/obBUrRdYfRo26yxui6S8y3z7uJ+Naup7UKrDxrpK7OnQkxkl9KQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-win32-arm64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-win32-arm64/-/binding-win32-arm64-0.8.7.tgz", + "integrity": "sha512-mEB/9PlaAkisJ6KWGz0zvywXoU6+80dTlR2LwS7s/jcXXoU6fm2+sitBZXtqu3+Q4DcDgPxM45uWMCzPs0TSRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-parser/binding-win32-x64": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-parser/binding-win32-x64/-/binding-win32-x64-0.8.7.tgz", + "integrity": "sha512-8vNB2DP0ou61nGb8tc/qfi41gfyDXz1MHr2zqL3nR+cJ6CEbiuWV/l/a/vv151gCgiZLLAyGkQGENpozdg716w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-toolchain/types": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@yuku-toolchain/types/-/types-0.8.7.tgz", + "integrity": "sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.3.1", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/ansis/-/ansis-4.3.1.tgz", + "integrity": "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.27", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/cac": { + "version": "7.0.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/cac/-/cac-7.0.0.tgz", + "integrity": "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001791", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/case-anything": { + "version": "2.1.13", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "dev": true, + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "17.2.2", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dprint-node": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3" + } + }, + "node_modules/dprint-node/node_modules/detect-libc": { + "version": "1.0.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dts-resolver": { + "version": "3.0.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/dts-resolver/-/dts-resolver-3.0.0.tgz", + "integrity": "sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "oxc-resolver": ">=11.0.0" + }, + "peerDependenciesMeta": { + "oxc-resolver": { + "optional": true + } + } + }, + "node_modules/empathic": { + "version": "2.0.1", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/empathic/-/empathic-2.0.1.tgz", + "integrity": "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.5", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/get-tsconfig/-/get-tsconfig-5.0.0-beta.5.tgz", + "integrity": "sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/happy-dom": { + "version": "20.8.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": ">=20.0.0", + "@types/whatwg-mimetype": "^3.0.2", + "@types/ws": "^8.18.1", + "entities": "^7.0.1", + "whatwg-mimetype": "^3.0.0", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/happy-dom/node_modules/@types/node": { + "version": "25.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/happy-dom/node_modules/undici-types": { + "version": "7.16.0", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hookable": { + "version": "6.1.1", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/hookable/-/hookable-6.1.1.tgz", + "integrity": "sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/import-without-cache": { + "version": "0.4.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/import-without-cache/-/import-without-cache-0.4.0.tgz", + "integrity": "sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/magicast/node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/magicast/node_modules/@babel/parser": { + "version": "7.29.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/magicast/node_modules/@babel/types": { + "version": "7.29.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "16.2.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/env": "16.2.12", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.2.12", + "@next/swc-darwin-x64": "16.2.12", + "@next/swc-linux-arm64-gnu": "16.2.12", + "@next/swc-linux-arm64-musl": "16.2.12", + "@next/swc-linux-x64-gnu": "16.2.12", + "@next/swc-linux-x64-musl": "16.2.12", + "@next/swc-win32-arm64-msvc": "16.2.12", + "@next/swc-win32-x64-msvc": "16.2.12", + "sharp": "^0.34.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.3" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-beta.38", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.89.0", + "@rolldown/pluginutils": "1.0.0-beta.38", + "ansis": "^4.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-beta.38", + "@rolldown/binding-darwin-arm64": "1.0.0-beta.38", + "@rolldown/binding-darwin-x64": "1.0.0-beta.38", + "@rolldown/binding-freebsd-x64": "1.0.0-beta.38", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.38", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.38", + "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.38", + "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.38", + "@rolldown/binding-linux-x64-musl": "1.0.0-beta.38", + "@rolldown/binding-openharmony-arm64": "1.0.0-beta.38", + "@rolldown/binding-wasm32-wasi": "1.0.0-beta.38", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.38", + "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.38", + "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.38" + } + }, + "node_modules/rolldown-plugin-dts": { + "version": "0.27.14", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/rolldown-plugin-dts/-/rolldown-plugin-dts-0.27.14.tgz", + "integrity": "sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dts-resolver": "^3.0.0", + "get-tsconfig": "5.0.0-beta.5", + "obug": "^2.1.4", + "yuku-ast": "^0.8.0", + "yuku-codegen": "^0.8.0", + "yuku-parser": "^0.8.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@typescript/native-preview": "*", + "@volar/typescript": "~2.4.0", + "rolldown": "^1.0.0", + "typescript": "^5.0.0 || ^6.0.0 || ~7.0.0", + "vue-tsc": "~3.2.0 || ~3.3.0" + }, + "peerDependenciesMeta": { + "@typescript/native-preview": { + "optional": true + }, + "@volar/typescript": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.38.tgz", + "integrity": "sha512-AE3HFQrjWCKLFZD1Vpiy+qsqTRwwoil1oM5WsKPSmfQ5fif/A+ZtOZetF32erZdsR7qyvns6qHEteEsF6g6rsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.38.tgz", + "integrity": "sha512-Ymojqc2U35iUc8NFU2XX1WQPfBRRHN6xHcrxAf9WS8BFFBn8pDrH5QPvH1tYs3lDkw6UGGbanr1RGzARqdUp1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.38.tgz", + "integrity": "sha512-0ermTQ//WzSI0nOL3z/LUWMNiE9xeM5cLGxjewPFEexqxV/0uM8/lNp9QageQ8jfc/VO1OURsGw34HYO5PaL8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.38.tgz", + "integrity": "sha512-GADxzVUTCTp6EWI52831A29Tt7PukFe94nhg/SUsfkI33oTiNQtPxyLIT/3oRegizGuPSZSlrdBurkjDwxyEUQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.38.tgz", + "integrity": "sha512-SKO7Exl5Yem/OSNoA5uLHzyrptUQ8Hg70kHDxuwEaH0+GUg+SQe9/7PWmc4hFKBMrJGdQtii8WZ0uIz9Dofg5Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.38.tgz", + "integrity": "sha512-SOo6+WqhXPBaShLxLT0eCgH17d3Yu1lMAe4mFP0M9Bvr/kfMSOPQXuLxBcbBU9IFM9w3N6qP9xWOHO+oUJvi8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.38.tgz", + "integrity": "sha512-yvsQ3CyrodOX+lcoi+lejZGCOvJZa9xTsNB8OzpMDmHeZq3QzJfpYjXSAS6vie70fOkLVJb77UqYO193Cl8XBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.38.tgz", + "integrity": "sha512-84qzKMwUwikfYeOuJ4Kxm/3z15rt0nFGGQArHYIQQNSTiQdxGHxOkqXtzPFqrVfBJUdxBAf+jYzR1pttFJuWyg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.38.tgz", + "integrity": "sha512-QrNiWlce01DYH0rL8K3yUBu+lNzY+B0DyCbIc2Atan6/S6flxOL0ow5DLQvMamOI/oKhrJ4xG+9MkMb9dDHbLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.38.tgz", + "integrity": "sha512-19cTfnGedem+RY+znA9J6ARBOCEFD4YSjnx0p5jiTm9tR6pHafRfFIfKlTXhun+NL0WWM/M0eb2IfPPYUa8+wg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-beta.38", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.38.tgz", + "integrity": "sha512-4Qx6cgEPXLb0XsCyLoQcUgYBpfL0sjugftob+zhUH0EOk/NVCAIT+h0NJhY+jn7pFpeKxhNMqhvTNx3AesxIAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.4", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-poet": { + "version": "6.12.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dprint-node": "^1.0.8" + } + }, + "node_modules/ts-proto": { + "version": "2.7.7", + "dev": true, + "license": "ISC", + "dependencies": { + "@bufbuild/protobuf": "^2.0.0", + "case-anything": "^2.1.13", + "ts-poet": "^6.12.0", + "ts-proto-descriptors": "2.0.0" + }, + "bin": { + "protoc-gen-ts_proto": "protoc-gen-ts_proto" + } + }, + "node_modules/ts-proto-descriptors": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@bufbuild/protobuf": "^2.0.0" + } + }, + "node_modules/ts-proto-descriptors/node_modules/@bufbuild/protobuf": { + "version": "2.8.0", + "dev": true, + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/ts-proto/node_modules/@bufbuild/protobuf": { + "version": "2.8.0", + "dev": true, + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/tsdown": { + "version": "0.22.14", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/tsdown/-/tsdown-0.22.14.tgz", + "integrity": "sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.3.1", + "cac": "^7.0.0", + "defu": "^6.1.7", + "empathic": "^2.0.1", + "hookable": "^6.1.1", + "import-without-cache": "^0.4.0", + "obug": "^2.1.4", + "picomatch": "^4.0.5", + "rolldown": "~1.2.0", + "rolldown-plugin-dts": "^0.27.13", + "tinyexec": "^1.2.4", + "tinyglobby": "^0.2.17", + "tree-kill": "^1.2.2", + "unconfig-core": "^7.5.0", + "verkit": "^0.3.0" + }, + "bin": { + "tsdown": "dist/run.mjs" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@arethetypeswrong/core": "^0.18.1", + "@tsdown/css": "0.22.14", + "@tsdown/exe": "0.22.14", + "@vitejs/devtools": "*", + "publint": "^0.3.8", + "tsx": "*", + "typescript": "^5.0.0 || ^6.0.0 || ^7.0.0", + "unplugin-unused": "^0.5.0", + "unrun": "*" + }, + "peerDependenciesMeta": { + "@arethetypeswrong/core": { + "optional": true + }, + "@tsdown/css": { + "optional": true + }, + "@tsdown/exe": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "publint": { + "optional": true + }, + "tsx": { + "optional": true + }, + "typescript": { + "optional": true + }, + "unplugin-unused": { + "optional": true + }, + "unrun": { + "optional": true + } + } + }, + "node_modules/tsdown/node_modules/@oxc-project/types": { + "version": "0.147.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@oxc-project/types/-/types-0.147.0.tgz", + "integrity": "sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/tsdown/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.6.tgz", + "integrity": "sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/tsdown/node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tsdown/node_modules/rolldown": { + "version": "1.2.6", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/rolldown/-/rolldown-1.2.6.tgz", + "integrity": "sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.147.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.6", + "@rolldown/binding-android-arm64": "1.2.6", + "@rolldown/binding-darwin-arm64": "1.2.6", + "@rolldown/binding-darwin-x64": "1.2.6", + "@rolldown/binding-freebsd-x64": "1.2.6", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.6", + "@rolldown/binding-linux-arm64-gnu": "1.2.6", + "@rolldown/binding-linux-arm64-musl": "1.2.6", + "@rolldown/binding-linux-ppc64-gnu": "1.2.6", + "@rolldown/binding-linux-s390x-gnu": "1.2.6", + "@rolldown/binding-linux-x64-gnu": "1.2.6", + "@rolldown/binding-linux-x64-musl": "1.2.6", + "@rolldown/binding-openharmony-arm64": "1.2.6", + "@rolldown/binding-win32-arm64-msvc": "1.2.6", + "@rolldown/binding-win32-x64-msvc": "1.2.6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unconfig-core": { + "version": "7.5.0", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/unconfig-core/-/unconfig-core-7.5.0.tgz", + "integrity": "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/undici-types": { + "version": "7.12.0", + "dev": true, + "license": "MIT" + }, + "node_modules/verkit": { + "version": "0.3.2", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/verkit/-/verkit-0.3.2.tgz", + "integrity": "sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/vite": { + "version": "8.0.16", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@oxc-project/types": { + "version": "0.133.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.3", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.3", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.5.15", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/vite/node_modules/rolldown": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.133.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" + } + }, + "node_modules/vitest": { + "version": "4.1.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.8", + "@vitest/mocker": "4.1.8", + "@vitest/pretty-format": "4.1.8", + "@vitest/runner": "4.1.8", + "@vitest/snapshot": "4.1.8", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.8", + "@vitest/browser-preview": "4.1.8", + "@vitest/browser-webdriverio": "4.1.8", + "@vitest/coverage-istanbul": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "@vitest/ui": "4.1.8", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yuku-ast": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/yuku-ast/-/yuku-ast-0.8.7.tgz", + "integrity": "sha512-h6+4bDfyootiMB9vckk5uKo5r5j0GHrkr17FQTDNfEsFT3DWlN9uu1HJwQwc64pgmLCI945fWM3lbTIqxjT3GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "^0.8.7" + } + }, + "node_modules/yuku-codegen": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/yuku-codegen/-/yuku-codegen-0.8.7.tgz", + "integrity": "sha512-adwDZSh8oVDzhE6Du9PwVWxcOxeV0e2EVhUuMKWfhSY4wkrDq9eqixlxFF3l/XGUV1E7UFzhpz9393MUumkyNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "^0.8.7" + }, + "optionalDependencies": { + "@yuku-codegen/binding-android-arm64": "0.8.7", + "@yuku-codegen/binding-darwin-arm64": "0.8.7", + "@yuku-codegen/binding-darwin-x64": "0.8.7", + "@yuku-codegen/binding-freebsd-x64": "0.8.7", + "@yuku-codegen/binding-linux-arm-gnu": "0.8.7", + "@yuku-codegen/binding-linux-arm-musl": "0.8.7", + "@yuku-codegen/binding-linux-arm64-gnu": "0.8.7", + "@yuku-codegen/binding-linux-arm64-musl": "0.8.7", + "@yuku-codegen/binding-linux-x64-gnu": "0.8.7", + "@yuku-codegen/binding-linux-x64-musl": "0.8.7", + "@yuku-codegen/binding-win32-arm64": "0.8.7", + "@yuku-codegen/binding-win32-x64": "0.8.7" + } + }, + "node_modules/yuku-parser": { + "version": "0.8.7", + "resolved": "https://artifactory.spotify.net/artifactory/api/npm/virtual-npm/yuku-parser/-/yuku-parser-0.8.7.tgz", + "integrity": "sha512-vRD9nwt4L3aYpxNqeSC4WqLv58xrXef0Ong1Mc45CTXTIpvLafx7JO05sczmQZwdLEZvywrLOGdNC5+Rp5N1BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "^0.8.7", + "yuku-ast": "^0.8.7" + }, + "optionalDependencies": { + "@yuku-parser/binding-android-arm64": "0.8.7", + "@yuku-parser/binding-darwin-arm64": "0.8.7", + "@yuku-parser/binding-darwin-x64": "0.8.7", + "@yuku-parser/binding-freebsd-x64": "0.8.7", + "@yuku-parser/binding-linux-arm-gnu": "0.8.7", + "@yuku-parser/binding-linux-arm-musl": "0.8.7", + "@yuku-parser/binding-linux-arm64-gnu": "0.8.7", + "@yuku-parser/binding-linux-arm64-musl": "0.8.7", + "@yuku-parser/binding-linux-x64-gnu": "0.8.7", + "@yuku-parser/binding-linux-x64-musl": "0.8.7", + "@yuku-parser/binding-win32-arm64": "0.8.7", + "@yuku-parser/binding-win32-x64": "0.8.7" + } + } + } +} diff --git a/openfeature-provider/js/src/ConfidenceServerProviderLocal.ts b/openfeature-provider/js/src/ConfidenceServerProviderLocal.ts index beff1f68a..8618e8862 100644 --- a/openfeature-provider/js/src/ConfidenceServerProviderLocal.ts +++ b/openfeature-provider/js/src/ConfidenceServerProviderLocal.ts @@ -93,6 +93,11 @@ export class ConfidenceServerProviderLocal implements Provider { private readonly materializationStore: MaterializationStore | null; private readonly initLabels: Record; private initTelemetryState: 'pending' | 'sending' | 'sent' = 'pending'; + private flushSucceeded = 0; + private flushFailed = 0; + private eventsPublished = 0; + private eventBatchesSucceeded = 0; + private eventBatchesFailed = 0; private resolverInstance: LocalResolver | null = null; private eventTracker: EventTracker | null = null; private stateEtag: string | null = null; @@ -239,7 +244,7 @@ export class ConfidenceServerProviderLocal implements Provider { } if (this.initTelemetryState !== 'sent') { try { - const request = this.addProviderInitTelemetry(new Uint8Array()); + const request = this.enrichTelemetry(new Uint8Array(), true); await this.sendFlagLogs(request, signal); this.initTelemetryState = 'sent'; } catch { @@ -315,16 +320,20 @@ export class ConfidenceServerProviderLocal implements Provider { body: body as Uint8Array, }); if (!response.ok) { + this.eventBatchesFailed++; logger.error(`Failed to send events: ${response.status} ${response.statusText}`); return; } + this.eventBatchesSucceeded++; const { errors } = PublishEventsResponse.decode(new Uint8Array(await response.arrayBuffer())); + this.eventsPublished += (batch.events?.length ?? 0) - errors.length; for (const error of errors) { logger.error( `Failed to publish event at index ${error.index}: ${EventError_Reason[error.reason]} ${error.message}`, ); } } catch (err) { + this.eventBatchesFailed++; logger.warn('Failed to send events:', err); } } @@ -510,14 +519,17 @@ export class ConfidenceServerProviderLocal implements Provider { const includeInit = this.initTelemetryState === 'pending'; if (includeInit) { this.initTelemetryState = 'sending'; - writeFlagLogRequest = this.addProviderInitTelemetry(writeFlagLogRequest); } + writeFlagLogRequest = this.enrichTelemetry(writeFlagLogRequest, includeInit); try { await this.sendFlagLogs(writeFlagLogRequest, signal); + this.flushSucceeded++; if (includeInit) { this.initTelemetryState = 'sent'; } } catch (error) { + this.flushFailed++; + this.restoreDrainedCounters(writeFlagLogRequest); if (includeInit) { this.initTelemetryState = 'pending'; } @@ -557,18 +569,64 @@ export class ConfidenceServerProviderLocal implements Provider { throw err; } } + // All destinations returned non-OK responses without throwing + throw new Error('All flag log destinations returned error responses'); } - private addProviderInitTelemetry(encodedWriteFlagLogRequest: Uint8Array): Uint8Array { + private restoreDrainedCounters(encodedWriteFlagLogRequest: Uint8Array): void { + try { + const request = WriteFlagLogsRequest.decode(encodedWriteFlagLogRequest); + const td = request.telemetryData; + if (td?.flush) { + this.flushSucceeded += td.flush.succeeded; + this.flushFailed += td.flush.failed; + } + if (td?.events) { + this.eventsPublished += td.events.published; + this.eventBatchesSucceeded += td.events.batchesSucceeded; + this.eventBatchesFailed += td.events.batchesFailed; + } + } catch { + // Best-effort restore — don't mask the original send error + } + } + + /** Single decode→enrich→encode pass for init + delivery telemetry. */ + private enrichTelemetry(encodedWriteFlagLogRequest: Uint8Array, includeInit: boolean): Uint8Array { + const hasFlush = this.flushSucceeded > 0 || this.flushFailed > 0; + const hasEvents = this.eventsPublished > 0 || this.eventBatchesSucceeded > 0 || this.eventBatchesFailed > 0; + if (!includeInit && !hasFlush && !hasEvents) { + return encodedWriteFlagLogRequest; + } const request = WriteFlagLogsRequest.decode(encodedWriteFlagLogRequest); if (!request.telemetryData) { - request.telemetryData = { resolverVersion: '', providerInitRate: [] }; + request.telemetryData = { + resolverVersion: '', + providerInitRate: [], + resolveRate: [], + memoryBytes: 0, + }; } - request.telemetryData.sdk = { - id: SdkId.SDK_ID_JS_LOCAL_SERVER_PROVIDER, - version: VERSION, - }; - request.telemetryData.providerInitRate.push({ count: 1, labels: this.initLabels }); + const td = request.telemetryData!; + if (includeInit) { + td.sdk = { id: SdkId.SDK_ID_JS_LOCAL_SERVER_PROVIDER, version: VERSION }; + td.providerInitRate.push({ count: 1, labels: this.initLabels }); + } + if (hasFlush) { + td.flush = { succeeded: this.flushSucceeded, failed: this.flushFailed }; + } + if (hasEvents) { + td.events = { + published: this.eventsPublished, + batchesSucceeded: this.eventBatchesSucceeded, + batchesFailed: this.eventBatchesFailed, + }; + } + this.flushSucceeded = 0; + this.flushFailed = 0; + this.eventsPublished = 0; + this.eventBatchesSucceeded = 0; + this.eventBatchesFailed = 0; return WriteFlagLogsRequest.encode(request).finish(); } diff --git a/openfeature-provider/proto/confidence/flags/resolver/v1/internal_api.proto b/openfeature-provider/proto/confidence/flags/resolver/v1/internal_api.proto index 8ba7bc835..fe68446c0 100644 --- a/openfeature-provider/proto/confidence/flags/resolver/v1/internal_api.proto +++ b/openfeature-provider/proto/confidence/flags/resolver/v1/internal_api.proto @@ -51,12 +51,65 @@ message TelemetryData { // Information about the SDK/provider Sdk sdk = 2; + // WASM-produced fields. Must be present so decode→encode round-trips + // in host SDKs preserve them (ts-proto/protobuf-js drop unknown fields). + ResolveLatency resolve_latency = 4; + repeated ResolveRate resolve_rate = 5; + StateAge state_age = 6; + uint64 memory_bytes = 7; + // Version of the embedded resolver (e.g. "0.14.0"). // This must be preserved when providers decode and re-encode telemetry. string resolver_version = 8; repeated ProviderInitRate provider_init_rate = 9; + ApplyDedupTelemetry apply_dedup = 10; + + FlushTelemetry flush = 11; + EventsTelemetry events = 12; + + message FlushTelemetry { + uint32 succeeded = 1; + uint32 failed = 2; + } + + message EventsTelemetry { + uint32 published = 1; + uint32 batches_succeeded = 2; + uint32 batches_failed = 3; + } + + message ResolveLatency { + uint32 sum = 1; + uint32 count = 2; + repeated BucketSpan buckets = 3; + double ln_ratio = 4; + } + + message ResolveRate { + uint32 count = 1; + uint32 reason = 2; + } + + message StateAge { + uint64 last_state_update = 1; + } + + message BucketSpan { + int32 offset = 1; + repeated uint32 counts = 2; + } + + message ApplyDedupTelemetry { + uint32 applies_total = 1; + uint32 applies_deduped = 2; + uint32 apply_dedup_overflow = 3; + uint32 sweeps = 4; + uint32 map_size = 5; + uint32 map_capacity = 6; + } + message ProviderInitRate { uint32 count = 1; reserved 2; // status — tbd diff --git a/openfeature-provider/python/src/confidence/proto/confidence/events/v1/api_pb2.py b/openfeature-provider/python/src/confidence/proto/confidence/events/v1/api_pb2.py index ffe93a137..6802f2d65 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/events/v1/api_pb2.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/events/v1/api_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: confidence/events/v1/api.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,9 +11,9 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, + 5, + 29, + 0, '', 'confidence/events/v1/api.proto' ) @@ -23,7 +23,7 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -from . import types_pb2 as confidence_dot_events_dot_v1_dot_types__pb2 +from confidence.events.v1 import types_pb2 as confidence_dot_events_dot_v1_dot_types__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1e\x63onfidence/events/v1/api.proto\x12\x14\x63onfidence.events.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a confidence/events/v1/types.proto\"\xb1\x01\n\x14PublishEventsRequest\x12\x15\n\rclient_secret\x18\x01 \x01(\t\x12+\n\x06\x65vents\x18\x02 \x03(\x0b\x32\x1b.confidence.events.v1.Event\x12-\n\tsend_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12&\n\x03sdk\x18\x04 \x01(\x0b\x32\x19.confidence.events.v1.Sdk\"I\n\x15PublishEventsResponse\x12\x30\n\x06\x65rrors\x18\x01 \x03(\x0b\x32 .confidence.events.v1.EventError2y\n\rEventsService\x12h\n\rPublishEvents\x12*.confidence.events.v1.PublishEventsRequest\x1a+.confidence.events.v1.PublishEventsResponseB\x93\x01\n$com.spotify.confidence.sdk.events.v1B\x08\x41piProtoP\x01Z_github.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/eventsb\x06proto3') diff --git a/openfeature-provider/python/src/confidence/proto/confidence/events/v1/api_pb2_grpc.py b/openfeature-provider/python/src/confidence/proto/confidence/events/v1/api_pb2_grpc.py index 046ab81e5..72231f94a 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/events/v1/api_pb2_grpc.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/events/v1/api_pb2_grpc.py @@ -3,7 +3,7 @@ import grpc import warnings -from . import api_pb2 as confidence_dot_events_dot_v1_dot_api__pb2 +from confidence.events.v1 import api_pb2 as confidence_dot_events_dot_v1_dot_api__pb2 GRPC_GENERATED_VERSION = '1.70.0' GRPC_VERSION = grpc.__version__ @@ -18,7 +18,7 @@ if _version_not_supported: raise RuntimeError( f'The grpc package installed is at version {GRPC_VERSION},' - + ' but the generated code in confidence/events/v1/api_pb2_grpc.py depends on' + + f' but the generated code in confidence/events/v1/api_pb2_grpc.py depends on' + f' grpcio>={GRPC_GENERATED_VERSION}.' + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' diff --git a/openfeature-provider/python/src/confidence/proto/confidence/events/v1/types_pb2.py b/openfeature-provider/python/src/confidence/proto/confidence/events/v1/types_pb2.py index 8ff279925..75328d2e6 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/events/v1/types_pb2.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/events/v1/types_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: confidence/events/v1/types.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,9 +11,9 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, + 5, + 29, + 0, '', 'confidence/events/v1/types.proto' ) diff --git a/openfeature-provider/python/src/confidence/proto/confidence/events/v1/types_pb2_grpc.py b/openfeature-provider/python/src/confidence/proto/confidence/events/v1/types_pb2_grpc.py index a314adab8..e5c206b55 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/events/v1/types_pb2_grpc.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/events/v1/types_pb2_grpc.py @@ -4,7 +4,7 @@ import warnings -GRPC_GENERATED_VERSION = '1.78.0' +GRPC_GENERATED_VERSION = '1.70.0' GRPC_VERSION = grpc.__version__ _version_not_supported = False @@ -17,7 +17,7 @@ if _version_not_supported: raise RuntimeError( f'The grpc package installed is at version {GRPC_VERSION},' - + ' but the generated code in confidence/events/v1/types_pb2_grpc.py depends on' + + f' but the generated code in confidence/events/v1/types_pb2_grpc.py depends on' + f' grpcio>={GRPC_GENERATED_VERSION}.' + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' diff --git a/openfeature-provider/python/src/confidence/proto/confidence/events/wasm/v1/wasm_api_pb2.py b/openfeature-provider/python/src/confidence/proto/confidence/events/wasm/v1/wasm_api_pb2.py index 0ca4c4968..25c4e086e 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/events/wasm/v1/wasm_api_pb2.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/events/wasm/v1/wasm_api_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: confidence/events/wasm/v1/wasm_api.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,9 +11,9 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, + 5, + 29, + 0, '', 'confidence/events/wasm/v1/wasm_api.proto' ) @@ -24,7 +24,7 @@ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -from ...v1 import types_pb2 as confidence_dot_events_dot_v1_dot_types__pb2 +from confidence.events.v1 import types_pb2 as confidence_dot_events_dot_v1_dot_types__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(confidence/events/wasm/v1/wasm_api.proto\x12\x19\x63onfidence.events.wasm.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a confidence/events/v1/types.proto\"\x06\n\x04Void\"\xc6\x01\n\x11TrackEventRequest\x12\x12\n\nevent_name\x18\x01 \x01(\t\x12.\n\nevent_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x12\n\x05value\x18\x03 \x01(\x01H\x00\x88\x01\x01\x12(\n\x07\x63ontext\x18\x04 \x01(\x0b\x32\x17.google.protobuf.Struct\x12%\n\x04\x64\x61ta\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructB\x08\n\x06_value\"B\n\x13\x46lushEventsResponse\x12+\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x1b.confidence.events.v1.EventB\xa0\x01\n)com.spotify.confidence.sdk.events.wasm.v1B\x0cWasmApiProtoP\x01Zcgithub.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/eventswasmb\x06proto3') diff --git a/openfeature-provider/python/src/confidence/proto/confidence/events/wasm/v1/wasm_api_pb2_grpc.py b/openfeature-provider/python/src/confidence/proto/confidence/events/wasm/v1/wasm_api_pb2_grpc.py index 9fd4079a1..8f9f167c7 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/events/wasm/v1/wasm_api_pb2_grpc.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/events/wasm/v1/wasm_api_pb2_grpc.py @@ -4,7 +4,7 @@ import warnings -GRPC_GENERATED_VERSION = '1.78.0' +GRPC_GENERATED_VERSION = '1.70.0' GRPC_VERSION = grpc.__version__ _version_not_supported = False @@ -17,7 +17,7 @@ if _version_not_supported: raise RuntimeError( f'The grpc package installed is at version {GRPC_VERSION},' - + ' but the generated code in confidence/events/wasm/v1/wasm_api_pb2_grpc.py depends on' + + f' but the generated code in confidence/events/wasm/v1/wasm_api_pb2_grpc.py depends on' + f' grpcio>={GRPC_GENERATED_VERSION}.' + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' diff --git a/openfeature-provider/python/src/confidence/proto/confidence/flags/admin/v1/resolver_pb2.py b/openfeature-provider/python/src/confidence/proto/confidence/flags/admin/v1/resolver_pb2.py index c08df5c76..9afdc52f4 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/flags/admin/v1/resolver_pb2.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/flags/admin/v1/resolver_pb2.py @@ -23,8 +23,8 @@ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -from ...types.v1 import types_pb2 as confidence_dot_flags_dot_types_dot_v1_dot_types__pb2 -from ...types.v1 import target_pb2 as confidence_dot_flags_dot_types_dot_v1_dot_target__pb2 +from confidence.flags.types.v1 import types_pb2 as confidence_dot_flags_dot_types_dot_v1_dot_types__pb2 +from confidence.flags.types.v1 import target_pb2 as confidence_dot_flags_dot_types_dot_v1_dot_target__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(confidence/flags/admin/v1/resolver.proto\x12\x19\x63onfidence.flags.admin.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a%confidence/flags/types/v1/types.proto\x1a&confidence/flags/types/v1/target.proto\"z\n\x13\x43lientResolverState\x12\r\n\x05state\x18\x01 \x01(\x0c\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\t\x12\x43\n\x10log_destinations\x18\x04 \x03(\x0e\x32).confidence.flags.admin.v1.LogDestination\"\xa2\x04\n\rResolverState\x12.\n\x05\x66lags\x18\x01 \x03(\x0b\x32\x1f.confidence.flags.admin.v1.Flag\x12?\n\x13segments_no_bitsets\x18\x02 \x03(\x0b\x32\".confidence.flags.admin.v1.Segment\x12\x46\n\x07\x62itsets\x18\x05 \x03(\x0b\x32\x35.confidence.flags.admin.v1.ResolverState.PackedBitset\x12\x32\n\x07\x63lients\x18\x06 \x03(\x0b\x32!.confidence.flags.admin.v1.Client\x12G\n\x12\x63lient_credentials\x18\x07 \x03(\x0b\x32+.confidence.flags.admin.v1.ClientCredential\x12?\n\x06region\x18\x08 \x01(\x0e\x32/.confidence.flags.admin.v1.ResolverState.Region\x1aZ\n\x0cPackedBitset\x12\x0f\n\x07segment\x18\x01 \x01(\t\x12\x18\n\x0egzipped_bitset\x18\x02 \x01(\x0cH\x00\x12\x15\n\x0b\x66ull_bitset\x18\x03 \x01(\x08H\x00\x42\x08\n\x06\x62itset\">\n\x06Region\x12\x16\n\x12REGION_UNSPECIFIED\x10\x00\x12\r\n\tREGION_EU\x10\x01\x12\r\n\tREGION_US\x10\x02\"\xc9\x0c\n\x04\x46lag\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\r \x01(\t\x12\x46\n\x06schema\x18\x02 \x01(\x0b\x32\x36.confidence.flags.types.v1.FlagSchema.StructFlagSchema\x12\x39\n\x08variants\x18\x03 \x03(\x0b\x32\'.confidence.flags.admin.v1.Flag.Variant\x12\x34\n\x05state\x18\x04 \x01(\x0e\x32%.confidence.flags.admin.v1.Flag.State\x12\x33\n\x05rules\x18\x05 \x03(\x0b\x32$.confidence.flags.admin.v1.Flag.Rule\x12\x0f\n\x07\x63lients\x18\x0c \x03(\t\x1a?\n\x07Variant\x12\x0c\n\x04name\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x1a\xa3\t\n\x04Rule\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07segment\x18\x02 \x01(\t\x12L\n\x0f\x61ssignment_spec\x18\x0c \x01(\x0b\x32\x33.confidence.flags.admin.v1.Flag.Rule.AssignmentSpec\x12\x1e\n\x16targeting_key_selector\x18\x05 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x0b \x01(\x08\x12V\n\x14materialization_spec\x18\r \x01(\x0b\x32\x38.confidence.flags.admin.v1.Flag.Rule.MaterializationSpec\x1a\x9b\x02\n\x13MaterializationSpec\x12\x1d\n\x15write_materialization\x18\x01 \x01(\t\x12\x1c\n\x14read_materialization\x18\x02 \x01(\t\x12^\n\x04mode\x18\x03 \x01(\x0b\x32P.confidence.flags.admin.v1.Flag.Rule.MaterializationSpec.MaterializationReadMode\x1ag\n\x17MaterializationReadMode\x12\"\n\x1amaterialization_must_match\x18\x01 \x01(\x08\x12(\n segment_targeting_can_be_ignored\x18\x02 \x01(\x08\x1al\n\x0e\x41ssignmentSpec\x12\x14\n\x0c\x62ucket_count\x18\x01 \x01(\x05\x12\x44\n\x0b\x61ssignments\x18\x02 \x03(\x0b\x32/.confidence.flags.admin.v1.Flag.Rule.Assignment\x1a\xeb\x03\n\nAssignment\x12\x15\n\rassignment_id\x18\x01 \x01(\t\x12T\n\x07variant\x18\x02 \x01(\x0b\x32\x41.confidence.flags.admin.v1.Flag.Rule.Assignment.VariantAssignmentH\x00\x12\\\n\x0b\x66\x61llthrough\x18\x03 \x01(\x0b\x32\x45.confidence.flags.admin.v1.Flag.Rule.Assignment.FallthroughAssignmentH\x00\x12\x61\n\x0e\x63lient_default\x18\x04 \x01(\x0b\x32G.confidence.flags.admin.v1.Flag.Rule.Assignment.ClientDefaultAssignmentH\x00\x12G\n\rbucket_ranges\x18\x05 \x03(\x0b\x32\x30.confidence.flags.admin.v1.Flag.Rule.BucketRange\x1a$\n\x11VariantAssignment\x12\x0f\n\x07variant\x18\x01 \x01(\t\x1a\x17\n\x15\x46\x61llthroughAssignment\x1a\x19\n\x17\x43lientDefaultAssignmentB\x0c\n\nassignment\x1a+\n\x0b\x42ucketRange\x12\r\n\x05lower\x18\x01 \x01(\x05\x12\r\n\x05upper\x18\x02 \x01(\x05\"8\n\x05State\x12\x15\n\x11STATE_UNSPECIFIED\x10\x00\x12\n\n\x06\x41\x43TIVE\x10\x01\x12\x0c\n\x08\x41RCHIVED\x10\x02\"\xef\x02\n\x07Segment\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x0c \x01(\t\x12\x37\n\ttargeting\x18\x02 \x01(\x0b\x32$.confidence.flags.types.v1.Targeting\x12\x41\n\nallocation\x18\x03 \x01(\x0b\x32-.confidence.flags.admin.v1.Segment.Allocation\x12N\n\x11\x62itset_allocation\x18\x06 \x01(\x0b\x32\x33.confidence.flags.admin.v1.Segment.BitsetAllocation\x1aP\n\nAllocation\x12\x12\n\nproportion\x18\x01 \x01(\t\x12\x18\n\x10\x65xclusivity_tags\x18\x03 \x03(\t\x12\x14\n\x0c\x65xclusive_to\x18\x04 \x03(\t\x1a\"\n\x10\x42itsetAllocation\x12\x0e\n\x06\x62itset\x18\x01 \x01(\x0c\"\x90\x0b\n\tCondition\x12\x43\n\x0etrue_condition\x18\x01 \x01(\x0b\x32).confidence.flags.admin.v1.Condition.TrueH\x00\x12\x45\n\x0f\x66\x61lse_condition\x18\x02 \x01(\x0b\x32*.confidence.flags.admin.v1.Condition.FalseH\x00\x12\x37\n\x03\x61nd\x18\x03 \x01(\x0b\x32(.confidence.flags.admin.v1.Condition.AndH\x00\x12\x35\n\x02or\x18\x04 \x01(\x0b\x32\'.confidence.flags.admin.v1.Condition.OrH\x00\x12\x37\n\x03not\x18\x05 \x01(\x0b\x32(.confidence.flags.admin.v1.Condition.NotH\x00\x12\x45\n\ncomparison\x18\x06 \x01(\x0b\x32/.confidence.flags.admin.v1.Condition.ComparisonH\x00\x12_\n\x18\x63ontext_field_comparison\x18\x07 \x01(\x0b\x32;.confidence.flags.admin.v1.Condition.ContextFieldComparisonH\x00\x1a\x06\n\x04True\x1a\x07\n\x05\x46\x61lse\x1a?\n\x03\x41nd\x12\x38\n\nconditions\x18\x01 \x03(\x0b\x32$.confidence.flags.admin.v1.Condition\x1a>\n\x02Or\x12\x38\n\nconditions\x18\x01 \x03(\x0b\x32$.confidence.flags.admin.v1.Condition\x1a>\n\x03Not\x12\x37\n\tcondition\x18\x01 \x01(\x0b\x32$.confidence.flags.admin.v1.Condition\x1a\x98\x03\n\nComparison\x12\r\n\x05\x66ield\x18\x01 \x01(\t\x12J\n\x08operator\x18\x02 \x01(\x0e\x32\x38.confidence.flags.admin.v1.Condition.Comparison.Operator\x12%\n\x05value\x18\x03 \x01(\x0b\x32\x16.google.protobuf.Value\"\x87\x02\n\x08Operator\x12\x18\n\x14OPERATOR_UNSPECIFIED\x10\x00\x12\n\n\x06\x45QUALS\x10\x01\x12\x0e\n\nNOT_EQUALS\x10\x02\x12\r\n\tLESS_THAN\x10\x03\x12\x16\n\x12LESS_THAN_OR_EQUAL\x10\x04\x12\x10\n\x0cGREATER_THAN\x10\x05\x12\x19\n\x15GREATER_THAN_OR_EQUAL\x10\x06\x12\x0c\n\x08\x43ONTAINS\x10\x07\x12\x0f\n\x0bSTARTS_WITH\x10\x08\x12\r\n\tENDS_WITH\x10\t\x12\x0b\n\x07MATCHES\x10\n\x12\x06\n\x02IN\x10\x0b\x12\n\n\x06NOT_IN\x10\x0c\x12\x10\n\x0c\x43ONTAINS_ANY\x10\r\x12\x10\n\x0c\x43ONTAINS_ALL\x10\x0e\x1a\xab\x02\n\x16\x43ontextFieldComparison\x12\r\n\x05\x66ield\x18\x01 \x01(\t\x12V\n\x08operator\x18\x02 \x01(\x0e\x32\x44.confidence.flags.admin.v1.Condition.ContextFieldComparison.Operator\x12\x13\n\x0bother_field\x18\x03 \x01(\t\"\x94\x01\n\x08Operator\x12\x18\n\x14OPERATOR_UNSPECIFIED\x10\x00\x12\n\n\x06\x45QUALS\x10\x01\x12\x0e\n\nNOT_EQUALS\x10\x02\x12\r\n\tLESS_THAN\x10\x03\x12\x16\n\x12LESS_THAN_OR_EQUAL\x10\x04\x12\x10\n\x0cGREATER_THAN\x10\x05\x12\x19\n\x15GREATER_THAN_OR_EQUAL\x10\x06\x42\x0b\n\tcondition\",\n\x06\x43lient\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\"\xa1\x01\n\x10\x43lientCredential\x12\x0c\n\x04name\x18\x01 \x01(\t\x12Q\n\rclient_secret\x18\x03 \x01(\x0b\x32\x38.confidence.flags.admin.v1.ClientCredential.ClientSecretH\x00\x1a\x1e\n\x0c\x43lientSecret\x12\x0e\n\x06secret\x18\x01 \x01(\tB\x0c\n\ncredential*s\n\x0eLogDestination\x12\x1f\n\x1bLOG_DESTINATION_UNSPECIFIED\x10\x00\x12 \n\x1cLOG_DESTINATION_SPOTIFY_EDGE\x10\x01\x12\x1e\n\x1aLOG_DESTINATION_CLOUDFLARE\x10\x02\x42\x9c\x01\n)com.spotify.confidence.sdk.flags.admin.v1B\rResolverProtoP\x01Z^github.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/adminb\x06proto3') diff --git a/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/api_pb2.py b/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/api_pb2.py index 31c44efd2..3ebc26fd8 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/api_pb2.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/api_pb2.py @@ -24,8 +24,8 @@ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -from . import types_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_types__pb2 -from ...types.v1 import types_pb2 as confidence_dot_flags_dot_types_dot_v1_dot_types__pb2 +from confidence.flags.resolver.v1 import types_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_types__pb2 +from confidence.flags.types.v1 import types_pb2 as confidence_dot_flags_dot_types_dot_v1_dot_types__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&confidence/flags/resolver/v1/api.proto\x12\x1c\x63onfidence.flags.resolver.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a(confidence/flags/resolver/v1/types.proto\x1a%confidence/flags/types/v1/types.proto\"\xaf\x01\n\x13ResolveFlagsRequest\x12\r\n\x05\x66lags\x18\x01 \x03(\t\x12\x33\n\x12\x65valuation_context\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x15\n\rclient_secret\x18\x03 \x01(\t\x12\r\n\x05\x61pply\x18\x04 \x01(\x08\x12.\n\x03sdk\x18\x05 \x01(\x0b\x32!.confidence.flags.resolver.v1.Sdk\"\x85\x01\n\x14ResolveFlagsResponse\x12\x42\n\x0eresolved_flags\x18\x01 \x03(\x0b\x32*.confidence.flags.resolver.v1.ResolvedFlag\x12\x15\n\rresolve_token\x18\x02 \x01(\x0c\x12\x12\n\nresolve_id\x18\x03 \x01(\t\"\xda\x01\n\x11\x41pplyFlagsRequest\x12\x38\n\x05\x66lags\x18\x01 \x03(\x0b\x32).confidence.flags.resolver.v1.AppliedFlag\x12\x15\n\rclient_secret\x18\x02 \x01(\t\x12\x15\n\rresolve_token\x18\x03 \x01(\x0c\x12-\n\tsend_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\x03sdk\x18\x05 \x01(\x0b\x32!.confidence.flags.resolver.v1.Sdk\"\x14\n\x12\x41pplyFlagsResponse\"K\n\x0b\x41ppliedFlag\x12\x0c\n\x04\x66lag\x18\x01 \x01(\t\x12.\n\napply_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\x90\x02\n\x0cResolvedFlag\x12\x0c\n\x04\x66lag\x18\x01 \x01(\t\x12\x0f\n\x07variant\x18\x02 \x01(\t\x12&\n\x05value\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12K\n\x0b\x66lag_schema\x18\x04 \x01(\x0b\x32\x36.confidence.flags.types.v1.FlagSchema.StructFlagSchema\x12;\n\x06reason\x18\x05 \x01(\x0e\x32+.confidence.flags.resolver.v1.ResolveReason\x12\x14\n\x0cshould_apply\x18\x06 \x01(\x08\x12\x19\n\x11\x61ssignment_origin\x18\x07 \x01(\t2\x8e\x01\n\x13\x46lagResolverService\x12w\n\x0cResolveFlags\x12\x31.confidence.flags.resolver.v1.ResolveFlagsRequest\x1a\x32.confidence.flags.resolver.v1.ResolveFlagsResponse\"\x00\x42\x9d\x01\n,com.spotify.confidence.sdk.flags.resolver.v1B\x08\x41piProtoP\x01Zagithub.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/resolverb\x06proto3') diff --git a/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/api_pb2_grpc.py b/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/api_pb2_grpc.py index 6f4d114cd..1c1cfe7fd 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/api_pb2_grpc.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/api_pb2_grpc.py @@ -3,7 +3,7 @@ import grpc import warnings -from . import api_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_api__pb2 +from confidence.flags.resolver.v1 import api_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_api__pb2 GRPC_GENERATED_VERSION = '1.70.0' GRPC_VERSION = grpc.__version__ diff --git a/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/internal_api_pb2.py b/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/internal_api_pb2.py index a835c476f..d3e0a0a64 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/internal_api_pb2.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/internal_api_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: confidence/flags/resolver/v1/internal_api.proto -# Protobuf Python Version: 5.29.0 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -13,7 +13,7 @@ _runtime_version.Domain.PUBLIC, 5, 29, - 0, + 3, '', 'confidence/flags/resolver/v1/internal_api.proto' ) @@ -23,10 +23,10 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -from . import types_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_types__pb2 +from confidence.flags.resolver.v1 import types_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_types__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/confidence/flags/resolver/v1/internal_api.proto\x12\x1c\x63onfidence.flags.resolver.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a(confidence/flags/resolver/v1/types.proto\"\xb6\x02\n\x14WriteFlagLogsRequest\x12\x41\n\rflag_assigned\x18\x01 \x03(\x0b\x32*.confidence.flags.resolver.v1.FlagAssigned\x12\x43\n\x0etelemetry_data\x18\x02 \x01(\x0b\x32+.confidence.flags.resolver.v1.TelemetryData\x12L\n\x13\x63lient_resolve_info\x18\x03 \x03(\x0b\x32/.confidence.flags.resolver.v1.ClientResolveInfo\x12H\n\x11\x66lag_resolve_info\x18\x04 \x03(\x0b\x32-.confidence.flags.resolver.v1.FlagResolveInfo\"\x17\n\x15WriteFlagLogsResponse\"n\n\x15IngestFlagLogsRequest\x12\x12\n\naccount_id\x18\x01 \x01(\t\x12\x41\n\x05\x62\x61tch\x18\x02 \x01(\x0b\x32\x32.confidence.flags.resolver.v1.WriteFlagLogsRequest\"\xe6\x02\n\rTelemetryData\x12.\n\x03sdk\x18\x02 \x01(\x0b\x32!.confidence.flags.resolver.v1.Sdk\x12\x18\n\x10resolver_version\x18\x08 \x01(\t\x12X\n\x12provider_init_rate\x18\t \x03(\x0b\x32<.confidence.flags.resolver.v1.TelemetryData.ProviderInitRate\x1a\xb0\x01\n\x10ProviderInitRate\x12\r\n\x05\x63ount\x18\x01 \x01(\r\x12X\n\x06labels\x18\x03 \x03(\x0b\x32H.confidence.flags.resolver.v1.TelemetryData.ProviderInitRate.LabelsEntry\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01J\x04\x08\x02\x10\x03\"g\n\nClientInfo\x12\x0e\n\x06\x63lient\x18\x01 \x01(\t\x12\x19\n\x11\x63lient_credential\x18\x02 \x01(\t\x12.\n\x03sdk\x18\x03 \x01(\x0b\x32!.confidence.flags.resolver.v1.Sdk\"\xa4\x07\n\x0c\x46lagAssigned\x12\x12\n\nresolve_id\x18\n \x01(\t\x12=\n\x0b\x63lient_info\x18\x03 \x01(\x0b\x32(.confidence.flags.resolver.v1.ClientInfo\x12\x45\n\x05\x66lags\x18\x0f \x03(\x0b\x32\x36.confidence.flags.resolver.v1.FlagAssigned.AppliedFlag\x1a\xbd\x03\n\x0b\x41ppliedFlag\x12\x0c\n\x04\x66lag\x18\x01 \x01(\t\x12\x15\n\rtargeting_key\x18\x02 \x01(\t\x12\x1e\n\x16targeting_key_selector\x18\x03 \x01(\t\x12T\n\x0f\x61ssignment_info\x18\x04 \x01(\x0b\x32\x39.confidence.flags.resolver.v1.FlagAssigned.AssignmentInfoH\x00\x12Z\n\x12\x64\x65\x66\x61ult_assignment\x18\x05 \x01(\x0b\x32<.confidence.flags.resolver.v1.FlagAssigned.DefaultAssignmentH\x00\x12\x15\n\rassignment_id\x18\x06 \x01(\t\x12\x0c\n\x04rule\x18\x07 \x01(\t\x12T\n\x17\x66\x61llthrough_assignments\x18\x08 \x03(\x0b\x32\x33.confidence.flags.resolver.v1.FallthroughAssignment\x12.\n\napply_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x0c\n\nassignment\x1a\x32\n\x0e\x41ssignmentInfo\x12\x0f\n\x07segment\x18\x01 \x01(\t\x12\x0f\n\x07variant\x18\x02 \x01(\t\x1a\x85\x02\n\x11\x44\x65\x66\x61ultAssignment\x12\x64\n\x06reason\x18\x01 \x01(\x0e\x32T.confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment.DefaultAssignmentReason\"\x89\x01\n\x17\x44\x65\x66\x61ultAssignmentReason\x12)\n%DEFAULT_ASSIGNMENT_REASON_UNSPECIFIED\x10\x00\x12\x14\n\x10NO_SEGMENT_MATCH\x10\x01\x12\x1a\n\x12NO_TREATMENT_MATCH\x10\x02\x1a\x02\x08\x01\x12\x11\n\rFLAG_ARCHIVED\x10\x03\"s\n\x15\x46\x61llthroughAssignment\x12\x0c\n\x04rule\x18\x01 \x01(\t\x12\x15\n\rassignment_id\x18\x02 \x01(\t\x12\x15\n\rtargeting_key\x18\x03 \x01(\t\x12\x1e\n\x16targeting_key_selector\x18\x04 \x01(\t\"\xdf\x02\n\x11\x43lientResolveInfo\x12\x0e\n\x06\x63lient\x18\x01 \x01(\t\x12\x19\n\x11\x63lient_credential\x18\x02 \x01(\t\x12_\n\x06schema\x18\x03 \x03(\x0b\x32O.confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance\x1a\xbd\x01\n\x1f\x45valuationContextSchemaInstance\x12k\n\x06schema\x18\x01 \x03(\x0b\x32[.confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance.SchemaEntry\x1a-\n\x0bSchemaEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"\xb5\x01\n\x0f\x46lagResolveInfo\x12\x0c\n\x04\x66lag\x18\x01 \x01(\t\x12^\n\x14variant_resolve_info\x18\x02 \x03(\x0b\x32@.confidence.flags.resolver.v1.FlagResolveInfo.VariantResolveInfo\x1a\x34\n\x12VariantResolveInfo\x12\x0f\n\x07variant\x18\x01 \x01(\t\x12\r\n\x05\x63ount\x18\x03 \x01(\x03\"]\n\x16WriteOperationsRequest\x12\x43\n\x10store_variant_op\x18\x01 \x03(\x0b\x32).confidence.flags.resolver.v1.VariantData\"\x17\n\x15WriteOperationsResult\"D\n\rVariantReadOp\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\x17\n\x0fmaterialization\x18\x02 \x01(\t\x12\x0c\n\x04rule\x18\x03 \x01(\t\"8\n\x0fInclusionReadOp\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\x17\n\x0fmaterialization\x18\x02 \x01(\t\"\xa2\x01\n\x06ReadOp\x12\x46\n\x0fvariant_read_op\x18\x01 \x01(\x0b\x32+.confidence.flags.resolver.v1.VariantReadOpH\x00\x12J\n\x11inclusion_read_op\x18\x02 \x01(\x0b\x32-.confidence.flags.resolver.v1.InclusionReadOpH\x00\x42\x04\n\x02op\"J\n\x15ReadOperationsRequest\x12\x31\n\x03ops\x18\x03 \x03(\x0b\x32$.confidence.flags.resolver.v1.ReadOp\"S\n\x0bVariantData\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\x17\n\x0fmaterialization\x18\x02 \x01(\t\x12\x0c\n\x04rule\x18\x03 \x01(\t\x12\x0f\n\x07variant\x18\x04 \x01(\t\"K\n\rInclusionData\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\x17\n\x0fmaterialization\x18\x02 \x01(\t\x12\x13\n\x0bis_included\x18\x03 \x01(\x08\"\xa4\x01\n\nReadResult\x12\x43\n\x0evariant_result\x18\x01 \x01(\x0b\x32).confidence.flags.resolver.v1.VariantDataH\x00\x12G\n\x10inclusion_result\x18\x02 \x01(\x0b\x32+.confidence.flags.resolver.v1.InclusionDataH\x00\x42\x08\n\x06result\"Q\n\x14ReadOperationsResult\x12\x39\n\x07results\x18\x01 \x03(\x0b\x32(.confidence.flags.resolver.v1.ReadResult2\xb2\x03\n\x19InternalFlagLoggerService\x12~\n\x13\x43lientWriteFlagLogs\x12\x32.confidence.flags.resolver.v1.WriteFlagLogsRequest\x1a\x33.confidence.flags.resolver.v1.WriteFlagLogsResponse\x12\x8a\x01\n\x1bWriteMaterializedOperations\x12\x34.confidence.flags.resolver.v1.WriteOperationsRequest\x1a\x33.confidence.flags.resolver.v1.WriteOperationsResult\"\x00\x12\x87\x01\n\x1aReadMaterializedOperations\x12\x33.confidence.flags.resolver.v1.ReadOperationsRequest\x1a\x32.confidence.flags.resolver.v1.ReadOperationsResult\"\x00\x42\xad\x01\n,com.spotify.confidence.sdk.flags.resolver.v1B\x10InternalApiProtoP\x01Zigithub.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/resolverinternalb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/confidence/flags/resolver/v1/internal_api.proto\x12\x1c\x63onfidence.flags.resolver.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a(confidence/flags/resolver/v1/types.proto\"\xb6\x02\n\x14WriteFlagLogsRequest\x12\x41\n\rflag_assigned\x18\x01 \x03(\x0b\x32*.confidence.flags.resolver.v1.FlagAssigned\x12\x43\n\x0etelemetry_data\x18\x02 \x01(\x0b\x32+.confidence.flags.resolver.v1.TelemetryData\x12L\n\x13\x63lient_resolve_info\x18\x03 \x03(\x0b\x32/.confidence.flags.resolver.v1.ClientResolveInfo\x12H\n\x11\x66lag_resolve_info\x18\x04 \x03(\x0b\x32-.confidence.flags.resolver.v1.FlagResolveInfo\"\x17\n\x15WriteFlagLogsResponse\"n\n\x15IngestFlagLogsRequest\x12\x12\n\naccount_id\x18\x01 \x01(\t\x12\x41\n\x05\x62\x61tch\x18\x02 \x01(\x0b\x32\x32.confidence.flags.resolver.v1.WriteFlagLogsRequest\"\x8e\x0b\n\rTelemetryData\x12.\n\x03sdk\x18\x02 \x01(\x0b\x32!.confidence.flags.resolver.v1.Sdk\x12S\n\x0fresolve_latency\x18\x04 \x01(\x0b\x32:.confidence.flags.resolver.v1.TelemetryData.ResolveLatency\x12M\n\x0cresolve_rate\x18\x05 \x03(\x0b\x32\x37.confidence.flags.resolver.v1.TelemetryData.ResolveRate\x12G\n\tstate_age\x18\x06 \x01(\x0b\x32\x34.confidence.flags.resolver.v1.TelemetryData.StateAge\x12\x14\n\x0cmemory_bytes\x18\x07 \x01(\x04\x12\x18\n\x10resolver_version\x18\x08 \x01(\t\x12X\n\x12provider_init_rate\x18\t \x03(\x0b\x32<.confidence.flags.resolver.v1.TelemetryData.ProviderInitRate\x12T\n\x0b\x61pply_dedup\x18\n \x01(\x0b\x32?.confidence.flags.resolver.v1.TelemetryData.ApplyDedupTelemetry\x12I\n\x05\x66lush\x18\x0b \x01(\x0b\x32:.confidence.flags.resolver.v1.TelemetryData.FlushTelemetry\x12K\n\x06\x65vents\x18\x0c \x01(\x0b\x32;.confidence.flags.resolver.v1.TelemetryData.EventsTelemetry\x1a\x33\n\x0e\x46lushTelemetry\x12\x11\n\tsucceeded\x18\x01 \x01(\r\x12\x0e\n\x06\x66\x61iled\x18\x02 \x01(\r\x1aW\n\x0f\x45ventsTelemetry\x12\x11\n\tpublished\x18\x01 \x01(\r\x12\x19\n\x11\x62\x61tches_succeeded\x18\x02 \x01(\r\x12\x16\n\x0e\x62\x61tches_failed\x18\x03 \x01(\r\x1a\x87\x01\n\x0eResolveLatency\x12\x0b\n\x03sum\x18\x01 \x01(\r\x12\r\n\x05\x63ount\x18\x02 \x01(\r\x12G\n\x07\x62uckets\x18\x03 \x03(\x0b\x32\x36.confidence.flags.resolver.v1.TelemetryData.BucketSpan\x12\x10\n\x08ln_ratio\x18\x04 \x01(\x01\x1a,\n\x0bResolveRate\x12\r\n\x05\x63ount\x18\x01 \x01(\r\x12\x0e\n\x06reason\x18\x02 \x01(\r\x1a%\n\x08StateAge\x12\x19\n\x11last_state_update\x18\x01 \x01(\x04\x1a,\n\nBucketSpan\x12\x0e\n\x06offset\x18\x01 \x01(\x05\x12\x0e\n\x06\x63ounts\x18\x02 \x03(\r\x1a\x99\x01\n\x13\x41pplyDedupTelemetry\x12\x15\n\rapplies_total\x18\x01 \x01(\r\x12\x17\n\x0f\x61pplies_deduped\x18\x02 \x01(\r\x12\x1a\n\x12\x61pplies_not_cached\x18\x03 \x01(\r\x12\x0e\n\x06sweeps\x18\x04 \x01(\r\x12\x10\n\x08map_size\x18\x05 \x01(\r\x12\x14\n\x0cmap_capacity\x18\x06 \x01(\r\x1a\xb0\x01\n\x10ProviderInitRate\x12\r\n\x05\x63ount\x18\x01 \x01(\r\x12X\n\x06labels\x18\x03 \x03(\x0b\x32H.confidence.flags.resolver.v1.TelemetryData.ProviderInitRate.LabelsEntry\x1a-\n\x0bLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01J\x04\x08\x02\x10\x03\"g\n\nClientInfo\x12\x0e\n\x06\x63lient\x18\x01 \x01(\t\x12\x19\n\x11\x63lient_credential\x18\x02 \x01(\t\x12.\n\x03sdk\x18\x03 \x01(\x0b\x32!.confidence.flags.resolver.v1.Sdk\"\xa4\x07\n\x0c\x46lagAssigned\x12\x12\n\nresolve_id\x18\n \x01(\t\x12=\n\x0b\x63lient_info\x18\x03 \x01(\x0b\x32(.confidence.flags.resolver.v1.ClientInfo\x12\x45\n\x05\x66lags\x18\x0f \x03(\x0b\x32\x36.confidence.flags.resolver.v1.FlagAssigned.AppliedFlag\x1a\xbd\x03\n\x0b\x41ppliedFlag\x12\x0c\n\x04\x66lag\x18\x01 \x01(\t\x12\x15\n\rtargeting_key\x18\x02 \x01(\t\x12\x1e\n\x16targeting_key_selector\x18\x03 \x01(\t\x12T\n\x0f\x61ssignment_info\x18\x04 \x01(\x0b\x32\x39.confidence.flags.resolver.v1.FlagAssigned.AssignmentInfoH\x00\x12Z\n\x12\x64\x65\x66\x61ult_assignment\x18\x05 \x01(\x0b\x32<.confidence.flags.resolver.v1.FlagAssigned.DefaultAssignmentH\x00\x12\x15\n\rassignment_id\x18\x06 \x01(\t\x12\x0c\n\x04rule\x18\x07 \x01(\t\x12T\n\x17\x66\x61llthrough_assignments\x18\x08 \x03(\x0b\x32\x33.confidence.flags.resolver.v1.FallthroughAssignment\x12.\n\napply_time\x18\t \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x0c\n\nassignment\x1a\x32\n\x0e\x41ssignmentInfo\x12\x0f\n\x07segment\x18\x01 \x01(\t\x12\x0f\n\x07variant\x18\x02 \x01(\t\x1a\x85\x02\n\x11\x44\x65\x66\x61ultAssignment\x12\x64\n\x06reason\x18\x01 \x01(\x0e\x32T.confidence.flags.resolver.v1.FlagAssigned.DefaultAssignment.DefaultAssignmentReason\"\x89\x01\n\x17\x44\x65\x66\x61ultAssignmentReason\x12)\n%DEFAULT_ASSIGNMENT_REASON_UNSPECIFIED\x10\x00\x12\x14\n\x10NO_SEGMENT_MATCH\x10\x01\x12\x1a\n\x12NO_TREATMENT_MATCH\x10\x02\x1a\x02\x08\x01\x12\x11\n\rFLAG_ARCHIVED\x10\x03\"s\n\x15\x46\x61llthroughAssignment\x12\x0c\n\x04rule\x18\x01 \x01(\t\x12\x15\n\rassignment_id\x18\x02 \x01(\t\x12\x15\n\rtargeting_key\x18\x03 \x01(\t\x12\x1e\n\x16targeting_key_selector\x18\x04 \x01(\t\"\xdf\x02\n\x11\x43lientResolveInfo\x12\x0e\n\x06\x63lient\x18\x01 \x01(\t\x12\x19\n\x11\x63lient_credential\x18\x02 \x01(\t\x12_\n\x06schema\x18\x03 \x03(\x0b\x32O.confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance\x1a\xbd\x01\n\x1f\x45valuationContextSchemaInstance\x12k\n\x06schema\x18\x01 \x03(\x0b\x32[.confidence.flags.resolver.v1.ClientResolveInfo.EvaluationContextSchemaInstance.SchemaEntry\x1a-\n\x0bSchemaEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"\xb5\x01\n\x0f\x46lagResolveInfo\x12\x0c\n\x04\x66lag\x18\x01 \x01(\t\x12^\n\x14variant_resolve_info\x18\x02 \x03(\x0b\x32@.confidence.flags.resolver.v1.FlagResolveInfo.VariantResolveInfo\x1a\x34\n\x12VariantResolveInfo\x12\x0f\n\x07variant\x18\x01 \x01(\t\x12\r\n\x05\x63ount\x18\x03 \x01(\x03\"]\n\x16WriteOperationsRequest\x12\x43\n\x10store_variant_op\x18\x01 \x03(\x0b\x32).confidence.flags.resolver.v1.VariantData\"\x17\n\x15WriteOperationsResult\"D\n\rVariantReadOp\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\x17\n\x0fmaterialization\x18\x02 \x01(\t\x12\x0c\n\x04rule\x18\x03 \x01(\t\"8\n\x0fInclusionReadOp\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\x17\n\x0fmaterialization\x18\x02 \x01(\t\"\xa2\x01\n\x06ReadOp\x12\x46\n\x0fvariant_read_op\x18\x01 \x01(\x0b\x32+.confidence.flags.resolver.v1.VariantReadOpH\x00\x12J\n\x11inclusion_read_op\x18\x02 \x01(\x0b\x32-.confidence.flags.resolver.v1.InclusionReadOpH\x00\x42\x04\n\x02op\"J\n\x15ReadOperationsRequest\x12\x31\n\x03ops\x18\x03 \x03(\x0b\x32$.confidence.flags.resolver.v1.ReadOp\"S\n\x0bVariantData\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\x17\n\x0fmaterialization\x18\x02 \x01(\t\x12\x0c\n\x04rule\x18\x03 \x01(\t\x12\x0f\n\x07variant\x18\x04 \x01(\t\"K\n\rInclusionData\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\x17\n\x0fmaterialization\x18\x02 \x01(\t\x12\x13\n\x0bis_included\x18\x03 \x01(\x08\"\xa4\x01\n\nReadResult\x12\x43\n\x0evariant_result\x18\x01 \x01(\x0b\x32).confidence.flags.resolver.v1.VariantDataH\x00\x12G\n\x10inclusion_result\x18\x02 \x01(\x0b\x32+.confidence.flags.resolver.v1.InclusionDataH\x00\x42\x08\n\x06result\"Q\n\x14ReadOperationsResult\x12\x39\n\x07results\x18\x01 \x03(\x0b\x32(.confidence.flags.resolver.v1.ReadResult2\xb2\x03\n\x19InternalFlagLoggerService\x12~\n\x13\x43lientWriteFlagLogs\x12\x32.confidence.flags.resolver.v1.WriteFlagLogsRequest\x1a\x33.confidence.flags.resolver.v1.WriteFlagLogsResponse\x12\x8a\x01\n\x1bWriteMaterializedOperations\x12\x34.confidence.flags.resolver.v1.WriteOperationsRequest\x1a\x33.confidence.flags.resolver.v1.WriteOperationsResult\"\x00\x12\x87\x01\n\x1aReadMaterializedOperations\x12\x33.confidence.flags.resolver.v1.ReadOperationsRequest\x1a\x32.confidence.flags.resolver.v1.ReadOperationsResult\"\x00\x42\xad\x01\n,com.spotify.confidence.sdk.flags.resolver.v1B\x10InternalApiProtoP\x01Zigithub.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/resolverinternalb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -47,55 +47,69 @@ _globals['_INGESTFLAGLOGSREQUEST']._serialized_start=494 _globals['_INGESTFLAGLOGSREQUEST']._serialized_end=604 _globals['_TELEMETRYDATA']._serialized_start=607 - _globals['_TELEMETRYDATA']._serialized_end=965 - _globals['_TELEMETRYDATA_PROVIDERINITRATE']._serialized_start=789 - _globals['_TELEMETRYDATA_PROVIDERINITRATE']._serialized_end=965 - _globals['_TELEMETRYDATA_PROVIDERINITRATE_LABELSENTRY']._serialized_start=914 - _globals['_TELEMETRYDATA_PROVIDERINITRATE_LABELSENTRY']._serialized_end=959 - _globals['_CLIENTINFO']._serialized_start=967 - _globals['_CLIENTINFO']._serialized_end=1070 - _globals['_FLAGASSIGNED']._serialized_start=1073 - _globals['_FLAGASSIGNED']._serialized_end=2005 - _globals['_FLAGASSIGNED_APPLIEDFLAG']._serialized_start=1244 - _globals['_FLAGASSIGNED_APPLIEDFLAG']._serialized_end=1689 - _globals['_FLAGASSIGNED_ASSIGNMENTINFO']._serialized_start=1691 - _globals['_FLAGASSIGNED_ASSIGNMENTINFO']._serialized_end=1741 - _globals['_FLAGASSIGNED_DEFAULTASSIGNMENT']._serialized_start=1744 - _globals['_FLAGASSIGNED_DEFAULTASSIGNMENT']._serialized_end=2005 - _globals['_FLAGASSIGNED_DEFAULTASSIGNMENT_DEFAULTASSIGNMENTREASON']._serialized_start=1868 - _globals['_FLAGASSIGNED_DEFAULTASSIGNMENT_DEFAULTASSIGNMENTREASON']._serialized_end=2005 - _globals['_FALLTHROUGHASSIGNMENT']._serialized_start=2007 - _globals['_FALLTHROUGHASSIGNMENT']._serialized_end=2122 - _globals['_CLIENTRESOLVEINFO']._serialized_start=2125 - _globals['_CLIENTRESOLVEINFO']._serialized_end=2476 - _globals['_CLIENTRESOLVEINFO_EVALUATIONCONTEXTSCHEMAINSTANCE']._serialized_start=2287 - _globals['_CLIENTRESOLVEINFO_EVALUATIONCONTEXTSCHEMAINSTANCE']._serialized_end=2476 - _globals['_CLIENTRESOLVEINFO_EVALUATIONCONTEXTSCHEMAINSTANCE_SCHEMAENTRY']._serialized_start=2431 - _globals['_CLIENTRESOLVEINFO_EVALUATIONCONTEXTSCHEMAINSTANCE_SCHEMAENTRY']._serialized_end=2476 - _globals['_FLAGRESOLVEINFO']._serialized_start=2479 - _globals['_FLAGRESOLVEINFO']._serialized_end=2660 - _globals['_FLAGRESOLVEINFO_VARIANTRESOLVEINFO']._serialized_start=2608 - _globals['_FLAGRESOLVEINFO_VARIANTRESOLVEINFO']._serialized_end=2660 - _globals['_WRITEOPERATIONSREQUEST']._serialized_start=2662 - _globals['_WRITEOPERATIONSREQUEST']._serialized_end=2755 - _globals['_WRITEOPERATIONSRESULT']._serialized_start=2757 - _globals['_WRITEOPERATIONSRESULT']._serialized_end=2780 - _globals['_VARIANTREADOP']._serialized_start=2782 - _globals['_VARIANTREADOP']._serialized_end=2850 - _globals['_INCLUSIONREADOP']._serialized_start=2852 - _globals['_INCLUSIONREADOP']._serialized_end=2908 - _globals['_READOP']._serialized_start=2911 - _globals['_READOP']._serialized_end=3073 - _globals['_READOPERATIONSREQUEST']._serialized_start=3075 - _globals['_READOPERATIONSREQUEST']._serialized_end=3149 - _globals['_VARIANTDATA']._serialized_start=3151 - _globals['_VARIANTDATA']._serialized_end=3234 - _globals['_INCLUSIONDATA']._serialized_start=3236 - _globals['_INCLUSIONDATA']._serialized_end=3311 - _globals['_READRESULT']._serialized_start=3314 - _globals['_READRESULT']._serialized_end=3478 - _globals['_READOPERATIONSRESULT']._serialized_start=3480 - _globals['_READOPERATIONSRESULT']._serialized_end=3561 - _globals['_INTERNALFLAGLOGGERSERVICE']._serialized_start=3564 - _globals['_INTERNALFLAGLOGGERSERVICE']._serialized_end=3998 + _globals['_TELEMETRYDATA']._serialized_end=2029 + _globals['_TELEMETRYDATA_FLUSHTELEMETRY']._serialized_start=1285 + _globals['_TELEMETRYDATA_FLUSHTELEMETRY']._serialized_end=1336 + _globals['_TELEMETRYDATA_EVENTSTELEMETRY']._serialized_start=1338 + _globals['_TELEMETRYDATA_EVENTSTELEMETRY']._serialized_end=1425 + _globals['_TELEMETRYDATA_RESOLVELATENCY']._serialized_start=1428 + _globals['_TELEMETRYDATA_RESOLVELATENCY']._serialized_end=1563 + _globals['_TELEMETRYDATA_RESOLVERATE']._serialized_start=1565 + _globals['_TELEMETRYDATA_RESOLVERATE']._serialized_end=1609 + _globals['_TELEMETRYDATA_STATEAGE']._serialized_start=1611 + _globals['_TELEMETRYDATA_STATEAGE']._serialized_end=1648 + _globals['_TELEMETRYDATA_BUCKETSPAN']._serialized_start=1650 + _globals['_TELEMETRYDATA_BUCKETSPAN']._serialized_end=1694 + _globals['_TELEMETRYDATA_APPLYDEDUPTELEMETRY']._serialized_start=1697 + _globals['_TELEMETRYDATA_APPLYDEDUPTELEMETRY']._serialized_end=1850 + _globals['_TELEMETRYDATA_PROVIDERINITRATE']._serialized_start=1853 + _globals['_TELEMETRYDATA_PROVIDERINITRATE']._serialized_end=2029 + _globals['_TELEMETRYDATA_PROVIDERINITRATE_LABELSENTRY']._serialized_start=1978 + _globals['_TELEMETRYDATA_PROVIDERINITRATE_LABELSENTRY']._serialized_end=2023 + _globals['_CLIENTINFO']._serialized_start=2031 + _globals['_CLIENTINFO']._serialized_end=2134 + _globals['_FLAGASSIGNED']._serialized_start=2137 + _globals['_FLAGASSIGNED']._serialized_end=3069 + _globals['_FLAGASSIGNED_APPLIEDFLAG']._serialized_start=2308 + _globals['_FLAGASSIGNED_APPLIEDFLAG']._serialized_end=2753 + _globals['_FLAGASSIGNED_ASSIGNMENTINFO']._serialized_start=2755 + _globals['_FLAGASSIGNED_ASSIGNMENTINFO']._serialized_end=2805 + _globals['_FLAGASSIGNED_DEFAULTASSIGNMENT']._serialized_start=2808 + _globals['_FLAGASSIGNED_DEFAULTASSIGNMENT']._serialized_end=3069 + _globals['_FLAGASSIGNED_DEFAULTASSIGNMENT_DEFAULTASSIGNMENTREASON']._serialized_start=2932 + _globals['_FLAGASSIGNED_DEFAULTASSIGNMENT_DEFAULTASSIGNMENTREASON']._serialized_end=3069 + _globals['_FALLTHROUGHASSIGNMENT']._serialized_start=3071 + _globals['_FALLTHROUGHASSIGNMENT']._serialized_end=3186 + _globals['_CLIENTRESOLVEINFO']._serialized_start=3189 + _globals['_CLIENTRESOLVEINFO']._serialized_end=3540 + _globals['_CLIENTRESOLVEINFO_EVALUATIONCONTEXTSCHEMAINSTANCE']._serialized_start=3351 + _globals['_CLIENTRESOLVEINFO_EVALUATIONCONTEXTSCHEMAINSTANCE']._serialized_end=3540 + _globals['_CLIENTRESOLVEINFO_EVALUATIONCONTEXTSCHEMAINSTANCE_SCHEMAENTRY']._serialized_start=3495 + _globals['_CLIENTRESOLVEINFO_EVALUATIONCONTEXTSCHEMAINSTANCE_SCHEMAENTRY']._serialized_end=3540 + _globals['_FLAGRESOLVEINFO']._serialized_start=3543 + _globals['_FLAGRESOLVEINFO']._serialized_end=3724 + _globals['_FLAGRESOLVEINFO_VARIANTRESOLVEINFO']._serialized_start=3672 + _globals['_FLAGRESOLVEINFO_VARIANTRESOLVEINFO']._serialized_end=3724 + _globals['_WRITEOPERATIONSREQUEST']._serialized_start=3726 + _globals['_WRITEOPERATIONSREQUEST']._serialized_end=3819 + _globals['_WRITEOPERATIONSRESULT']._serialized_start=3821 + _globals['_WRITEOPERATIONSRESULT']._serialized_end=3844 + _globals['_VARIANTREADOP']._serialized_start=3846 + _globals['_VARIANTREADOP']._serialized_end=3914 + _globals['_INCLUSIONREADOP']._serialized_start=3916 + _globals['_INCLUSIONREADOP']._serialized_end=3972 + _globals['_READOP']._serialized_start=3975 + _globals['_READOP']._serialized_end=4137 + _globals['_READOPERATIONSREQUEST']._serialized_start=4139 + _globals['_READOPERATIONSREQUEST']._serialized_end=4213 + _globals['_VARIANTDATA']._serialized_start=4215 + _globals['_VARIANTDATA']._serialized_end=4298 + _globals['_INCLUSIONDATA']._serialized_start=4300 + _globals['_INCLUSIONDATA']._serialized_end=4375 + _globals['_READRESULT']._serialized_start=4378 + _globals['_READRESULT']._serialized_end=4542 + _globals['_READOPERATIONSRESULT']._serialized_start=4544 + _globals['_READOPERATIONSRESULT']._serialized_end=4625 + _globals['_INTERNALFLAGLOGGERSERVICE']._serialized_start=4628 + _globals['_INTERNALFLAGLOGGERSERVICE']._serialized_end=5062 # @@protoc_insertion_point(module_scope) diff --git a/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/internal_api_pb2_grpc.py b/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/internal_api_pb2_grpc.py index dce37bdce..ea5ec5c7a 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/internal_api_pb2_grpc.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/flags/resolver/v1/internal_api_pb2_grpc.py @@ -3,7 +3,7 @@ import grpc import warnings -from . import internal_api_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_internal__api__pb2 +from confidence.flags.resolver.v1 import internal_api_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_internal__api__pb2 GRPC_GENERATED_VERSION = '1.70.0' GRPC_VERSION = grpc.__version__ diff --git a/openfeature-provider/python/src/confidence/proto/confidence/wasm/messages_pb2.py b/openfeature-provider/python/src/confidence/proto/confidence/wasm/messages_pb2.py index 5c1c39a64..c84e3ec39 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/wasm/messages_pb2.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/wasm/messages_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: confidence/wasm/messages.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -11,9 +11,9 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, + 5, + 29, + 0, '', 'confidence/wasm/messages.proto' ) @@ -22,10 +22,10 @@ _sym_db = _symbol_database.Default() -from ..flags.resolver.v1 import types_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_types__pb2 +from confidence.flags.resolver.v1 import types_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_types__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1econfidence/wasm/messages.proto\x12\x0fconfidence.wasm\x1a(confidence/flags/resolver/v1/types.proto"\x06\n\x04Void"\xad\x01\n\x17SetResolverStateRequest\x12\r\n\x05state\x18\x01 \x01(\x0c\x12\x12\n\naccount_id\x18\x02 \x01(\t\x12.\n\x03sdk\x18\x03 \x01(\x0b2!.confidence.flags.resolver.v1.Sdk\x12\x1a\n\x12enable_apply_dedup\x18\x04 \x01(\x08\x12#\n\x1bdisable_exposure_collection\x18\x05 \x01(\x08"\x17\n\x07Request\x12\x0c\n\x04data\x18\x01 \x01(\x0c"5\n\x08Response\x12\x0e\n\x04data\x18\x01 \x01(\x0cH\x00\x12\x0f\n\x05error\x18\x02 \x01(\tH\x00B\x08\n\x06result"^\n\x19PrometheusSnapshotRequest\x12\x10\n\x08instance\x18\x01 \x01(\t\x12\x1a\n\x12buckets_per_decade\x18\x02 \x01(\r\x12\x13\n\x0bopenmetrics\x18\x03 \x01(\x08"*\n\x1aPrometheusSnapshotResponse\x12\x0c\n\x04text\x18\x01 \x01(\tB\x8c\x01\n\x1fcom.spotify.confidence.sdk.wasmB\x08MessagesP\x00Z]github.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/wasmb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1e\x63onfidence/wasm/messages.proto\x12\x0f\x63onfidence.wasm\x1a(confidence/flags/resolver/v1/types.proto\"\x06\n\x04Void\"\xad\x01\n\x17SetResolverStateRequest\x12\r\n\x05state\x18\x01 \x01(\x0c\x12\x12\n\naccount_id\x18\x02 \x01(\t\x12.\n\x03sdk\x18\x03 \x01(\x0b\x32!.confidence.flags.resolver.v1.Sdk\x12\x1a\n\x12\x65nable_apply_dedup\x18\x04 \x01(\x08\x12#\n\x1b\x64isable_exposure_collection\x18\x05 \x01(\x08\"\x17\n\x07Request\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"5\n\x08Response\x12\x0e\n\x04\x64\x61ta\x18\x01 \x01(\x0cH\x00\x12\x0f\n\x05\x65rror\x18\x02 \x01(\tH\x00\x42\x08\n\x06result\"^\n\x19PrometheusSnapshotRequest\x12\x10\n\x08instance\x18\x01 \x01(\t\x12\x1a\n\x12\x62uckets_per_decade\x18\x02 \x01(\r\x12\x13\n\x0bopenmetrics\x18\x03 \x01(\x08\"*\n\x1aPrometheusSnapshotResponse\x12\x0c\n\x04text\x18\x01 \x01(\tB\x8c\x01\n\x1f\x63om.spotify.confidence.sdk.wasmB\x08MessagesP\x00Z]github.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/wasmb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) diff --git a/openfeature-provider/python/src/confidence/proto/confidence/wasm/messages_pb2_grpc.py b/openfeature-provider/python/src/confidence/proto/confidence/wasm/messages_pb2_grpc.py index f38527c39..1c4e8c13d 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/wasm/messages_pb2_grpc.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/wasm/messages_pb2_grpc.py @@ -4,7 +4,7 @@ import warnings -GRPC_GENERATED_VERSION = '1.78.0' +GRPC_GENERATED_VERSION = '1.70.0' GRPC_VERSION = grpc.__version__ _version_not_supported = False @@ -17,7 +17,7 @@ if _version_not_supported: raise RuntimeError( f'The grpc package installed is at version {GRPC_VERSION},' - + ' but the generated code in confidence/wasm/messages_pb2_grpc.py depends on' + + f' but the generated code in confidence/wasm/messages_pb2_grpc.py depends on' + f' grpcio>={GRPC_GENERATED_VERSION}.' + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' diff --git a/openfeature-provider/python/src/confidence/proto/confidence/wasm/wasm_api_pb2.py b/openfeature-provider/python/src/confidence/proto/confidence/wasm/wasm_api_pb2.py index ed651bfdd..b410ae531 100644 --- a/openfeature-provider/python/src/confidence/proto/confidence/wasm/wasm_api_pb2.py +++ b/openfeature-provider/python/src/confidence/proto/confidence/wasm/wasm_api_pb2.py @@ -22,8 +22,8 @@ _sym_db = _symbol_database.Default() -from ..flags.resolver.v1 import api_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_api__pb2 -from ..flags.resolver.v1 import types_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_types__pb2 +from confidence.flags.resolver.v1 import api_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_api__pb2 +from confidence.flags.resolver.v1 import types_pb2 as confidence_dot_flags_dot_resolver_dot_v1_dot_types__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1e\x63onfidence/wasm/wasm_api.proto\x12\x1c\x63onfidence.flags.resolver.v1\x1a&confidence/flags/resolver/v1/api.proto\x1a(confidence/flags/resolver/v1/types.proto\"\xac\x05\n\x15ResolveProcessRequest\x12V\n\x19\x64\x65\x66\x65rred_materializations\x18\x01 \x01(\x0b\x32\x31.confidence.flags.resolver.v1.ResolveFlagsRequestH\x00\x12m\n\x17static_materializations\x18\x02 \x01(\x0b\x32J.confidence.flags.resolver.v1.ResolveProcessRequest.StaticMaterializationsH\x00\x12U\n\x18without_materializations\x18\x03 \x01(\x0b\x32\x31.confidence.flags.resolver.v1.ResolveFlagsRequestH\x00\x12L\n\x06resume\x18\x04 \x01(\x0b\x32:.confidence.flags.resolver.v1.ResolveProcessRequest.ResumeH\x00\x1a\xb3\x01\n\x16StaticMaterializations\x12J\n\x0fresolve_request\x18\x01 \x01(\x0b\x32\x31.confidence.flags.resolver.v1.ResolveFlagsRequest\x12M\n\x10materializations\x18\x02 \x03(\x0b\x32\x33.confidence.flags.resolver.v1.MaterializationRecord\x1a\x66\n\x06Resume\x12M\n\x10materializations\x18\x01 \x03(\x0b\x32\x33.confidence.flags.resolver.v1.MaterializationRecord\x12\r\n\x05state\x18\x02 \x01(\x0c\x42\t\n\x07resolve\"\xe8\x03\n\x16ResolveProcessResponse\x12Q\n\x08resolved\x18\x01 \x01(\x0b\x32=.confidence.flags.resolver.v1.ResolveProcessResponse.ResolvedH\x00\x12S\n\tsuspended\x18\x02 \x01(\x0b\x32>.confidence.flags.resolver.v1.ResolveProcessResponse.SuspendedH\x00\x1a\xa8\x01\n\x08Resolved\x12\x44\n\x08response\x18\x01 \x01(\x0b\x32\x32.confidence.flags.resolver.v1.ResolveFlagsResponse\x12V\n\x19materializations_to_write\x18\x02 \x03(\x0b\x32\x33.confidence.flags.resolver.v1.MaterializationRecord\x1aq\n\tSuspended\x12U\n\x18materializations_to_read\x18\x01 \x03(\x0b\x32\x33.confidence.flags.resolver.v1.MaterializationRecord\x12\r\n\x05state\x18\x02 \x01(\x0c\x42\x08\n\x06result\"]\n\x15MaterializationRecord\x12\x0c\n\x04unit\x18\x01 \x01(\t\x12\x17\n\x0fmaterialization\x18\x02 \x01(\t\x12\x0c\n\x04rule\x18\x03 \x01(\t\x12\x0f\n\x07variant\x18\x04 \x01(\t\"\x1d\n\nLogMessage\x12\x0f\n\x07message\x18\x01 \x01(\t\"i\n\x16RegisterResolveRequest\x12;\n\x06reason\x18\x01 \x01(\x0e\x32+.confidence.flags.resolver.v1.ResolveReason\x12\x12\n\nlatency_us\x18\x02 \x01(\rB\x9d\x01\n,com.spotify.confidence.sdk.flags.resolver.v1B\x0cWasmApiProtoP\x01Z]github.com/spotify/confidence-resolver/openfeature-provider/go/confidence/internal/proto/wasmb\x06proto3') diff --git a/openfeature-provider/python/src/confidence/provider.py b/openfeature-provider/python/src/confidence/provider.py index f9a9db6b2..ce8525089 100644 --- a/openfeature-provider/python/src/confidence/provider.py +++ b/openfeature-provider/python/src/confidence/provider.py @@ -284,6 +284,12 @@ def __init__( self._event_publish_attempts = 0 self._event_publish_failures = 0 + self._flush_succeeded = 0 + self._flush_failed = 0 + self._event_telemetry_published = 0 + self._event_telemetry_succeeded = 0 + self._event_telemetry_failed = 0 + # State fetcher (injected or created) self._state_fetcher = state_fetcher @@ -913,28 +919,69 @@ def _write_logs(self, log_data: bytes) -> None: self._init_telemetry_state = "sending" include_init = True - if include_init: + with self._event_stats_lock: + has_flush = self._flush_succeeded > 0 or self._flush_failed > 0 + has_events = ( + self._event_telemetry_published > 0 + or self._event_telemetry_succeeded > 0 + or self._event_telemetry_failed > 0 + ) + need_rewrite = include_init or has_flush or has_events + + request = None + if need_rewrite: request = internal_api_pb2.WriteFlagLogsRequest.FromString(log_data) - request.telemetry_data.sdk.CopyFrom( - types_pb2.Sdk( - id=types_pb2.SdkId.SDK_ID_PYTHON_PROVIDER, - version=__version__, + if include_init: + request.telemetry_data.sdk.CopyFrom( + types_pb2.Sdk( + id=types_pb2.SdkId.SDK_ID_PYTHON_PROVIDER, + version=__version__, + ) ) - ) - init_rate = request.telemetry_data.provider_init_rate.add() - init_rate.count = 1 - for k, v in self._init_labels.items(): - init_rate.labels[k] = v + init_rate = request.telemetry_data.provider_init_rate.add() + init_rate.count = 1 + for k, v in self._init_labels.items(): + init_rate.labels[k] = v + with self._event_stats_lock: + if has_flush: + request.telemetry_data.flush.succeeded = self._flush_succeeded + request.telemetry_data.flush.failed = self._flush_failed + self._flush_succeeded = 0 + self._flush_failed = 0 + if has_events: + request.telemetry_data.events.published = ( + self._event_telemetry_published + ) + request.telemetry_data.events.batches_succeeded = ( + self._event_telemetry_succeeded + ) + request.telemetry_data.events.batches_failed = ( + self._event_telemetry_failed + ) + self._event_telemetry_published = 0 + self._event_telemetry_succeeded = 0 + self._event_telemetry_failed = 0 log_data = request.SerializeToString() try: self._flag_logger.write(log_data) except Exception: + with self._event_stats_lock: + self._flush_failed += 1 + if request is not None: + td = request.telemetry_data + self._flush_succeeded += td.flush.succeeded + self._flush_failed += td.flush.failed + self._event_telemetry_published += td.events.published + self._event_telemetry_succeeded += td.events.batches_succeeded + self._event_telemetry_failed += td.events.batches_failed if include_init: with self._init_telemetry_lock: self._init_telemetry_state = "pending" raise else: + with self._event_stats_lock: + self._flush_succeeded += 1 if include_init: with self._init_telemetry_lock: self._init_telemetry_state = "sent" @@ -1132,6 +1179,10 @@ def _send_events(self, batch: events_wasm_pb2.FlushEventsResponse) -> None: with self._event_stats_lock: if failed: self._event_publish_failures += 1 + self._event_telemetry_failed += 1 + else: + self._event_telemetry_published += len(batch.events) + self._event_telemetry_succeeded += 1 self._event_publish_attempts += 1 if self._event_publish_attempts % EVENTS_STATS_WINDOW == 0: if self._event_publish_failures > 0: diff --git a/wasm/rust-guest/src/lib.rs b/wasm/rust-guest/src/lib.rs index 8588b5fa2..903ab1093 100644 --- a/wasm/rust-guest/src/lib.rs +++ b/wasm/rust-guest/src/lib.rs @@ -5,7 +5,7 @@ use std::sync::Mutex; use arc_swap::{ArcSwap, ArcSwapOption}; use bytes::Bytes; -use confidence_resolver::apply_dedup::ApplyDedup; +use confidence_resolver::apply_dedup::{ApplyDedup, ApplyDedupSnapshot}; use confidence_resolver::assign_logger::AssignLogger; use confidence_resolver::telemetry::{Telemetry, TelemetrySnapshot}; use prost::Message; @@ -72,6 +72,8 @@ static TELEMETRY: LazyLock = LazyLock::new(|| { }); static LAST_FLUSHED: LazyLock> = LazyLock::new(|| ArcSwap::from_pointee(TelemetrySnapshot::default())); +static LAST_DEDUP_SNAPSHOT: LazyLock> = + LazyLock::new(|| Mutex::new(ApplyDedupSnapshot::default())); struct WasmHost; @@ -153,6 +155,13 @@ fn sweep_apply_dedup() { } } +fn dedup_snapshot() -> ApplyDedupSnapshot { + match APPLY_DEDUP.lock() { + Ok(dedup) => dedup.telemetry_snapshot(), + Err(poisoned) => poisoned.into_inner().telemetry_snapshot(), + } +} + /// Safely gets an owned handle to the current resolver state. fn get_resolver_state() -> Result, String> { let guard = RESOLVER_STATE.load(); @@ -233,6 +242,17 @@ wasm_msg_guest! { if let Some(state) = RESOLVER_STATE.load().as_ref() { td.sdk = state.sdk.clone(); } + if APPLY_DEDUP_ENABLED.load(Ordering::Relaxed) { + let current = dedup_snapshot(); + let prev = match LAST_DEDUP_SNAPSHOT.lock() { + Ok(mut guard) => std::mem::replace(&mut *guard, current.clone()), + Err(poisoned) => std::mem::replace(&mut *poisoned.into_inner(), current.clone()), + }; + let delta = current.to_proto_delta(&prev); + if delta.applies_total > 0 || current.map_size > 0 { + td.apply_dedup = Some(delta); + } + } req.telemetry_data = Some(td); ASSIGN_LOGGER.checkpoint_fill_with_limit(&mut req, LOG_TARGET_BYTES, false); Ok(req) @@ -248,7 +268,11 @@ wasm_msg_guest! { buckets_per_decade: request.buckets_per_decade, openmetrics: request.openmetrics, }; - let text = TELEMETRY.snapshot().to_prometheus(&request.instance, &config); + let mut snap = TELEMETRY.snapshot(); + if APPLY_DEDUP_ENABLED.load(Ordering::Relaxed) { + snap.apply_dedup = Some(dedup_snapshot()); + } + let text = snap.to_prometheus(&request.instance, &config); Ok(proto::PrometheusSnapshotResponse { text }) }