From 25c427718f687e5680c3224820cd6228e1590bc8 Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Thu, 4 Jun 2026 15:25:20 -0400 Subject: [PATCH 1/6] fix: keep root package as workspace member for sampo release PR #116 added a [workspace] table to the root Cargo.toml with only compliance/adapter in members. Sampo's package discovery enumerates workspace.members and no longer saw the root posthog-rs package, so sampo release failed with: Changeset error: Changeset references 'cargo/posthog-rs', but it was not found in the workspace. Listing "." explicitly keeps the root crate discoverable. Verified by reproducing the failure against 79673c3 with sampo 0.17.4 and confirming sampo release then plans posthog-rs 0.8.0 -> 0.9.0 and exits 0. Cargo accepts the redundant "." member without warnings. Co-Authored-By: Claude Opus 4.8 (1M context) --- Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 89f61f61..e2af844e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,4 +51,7 @@ capture-v1 = ["flate2", "brotli", "zstd"] test-harness = [] [workspace] -members = ["compliance/adapter"] +# Explicitly list "." so the root posthog-rs package stays a discoverable +# workspace member. Without it, sampo only sees `members` and fails to find +# `cargo/posthog-rs` when applying changesets during release. +members = [".", "compliance/adapter"] From ab7cd2faf437970bc8d82ed8f155b8ba09d1ef7b Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Thu, 4 Jun 2026 15:32:32 -0400 Subject: [PATCH 2/6] fix: drop comments, keep root as explicit workspace member Co-Authored-By: Claude Opus 4.8 (1M context) --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e2af844e..08854ffc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,4 @@ capture-v1 = ["flate2", "brotli", "zstd"] test-harness = [] [workspace] -# Explicitly list "." so the root posthog-rs package stays a discoverable -# workspace member. Without it, sampo only sees `members` and fails to find -# `cargo/posthog-rs` when applying changesets during release. members = [".", "compliance/adapter"] From e6d93b28a9f62822b076cc8953ec8fe5fe5d110c Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Thu, 2 Jul 2026 12:00:47 -0400 Subject: [PATCH 3/6] feat: add secret_key config, deprecate personal_api_key Add a canonical `secret_key` builder method/field for local flag evaluation and remote config, accepting either a Personal API Key (`phx_...`) or a Project Secret API Key (`phs_...`). Keep `personal_api_key` as a deprecated, non-breaking alias; `secret_key` wins when both are set. --- examples/advanced_config.rs | 2 +- examples/local_evaluation.rs | 2 +- src/client/async_client.rs | 4 +-- src/client/blocking.rs | 4 +-- src/client/mod.rs | 63 ++++++++++++++++++++++++++++++---- tests/test_local_evaluation.rs | 2 +- 6 files changed, 63 insertions(+), 14 deletions(-) diff --git a/examples/advanced_config.rs b/examples/advanced_config.rs index 22fc7e1d..96f56bfe 100644 --- a/examples/advanced_config.rs +++ b/examples/advanced_config.rs @@ -41,7 +41,7 @@ async fn main() -> Result<(), Box> { println!("5. High-performance with local evaluation:"); let performance_config = ClientOptionsBuilder::default() .api_key("phc_project_key".to_string()) - .personal_api_key("phx_personal_key") // Required for local eval + .secret_key("phx_personal_key") // Required for local eval .enable_local_evaluation(true) // Cache flags locally .poll_interval_seconds(30) // Update cache every 30s .feature_flags_request_timeout_seconds(3) diff --git a/examples/local_evaluation.rs b/examples/local_evaluation.rs index 1508f06b..6d3eb7ca 100644 --- a/examples/local_evaluation.rs +++ b/examples/local_evaluation.rs @@ -53,7 +53,7 @@ async fn main() { let local_client = { let options = ClientOptionsBuilder::default() .api_key(api_key.clone()) - .personal_api_key(personal_key) + .secret_key(personal_key) .enable_local_evaluation(true) .poll_interval_seconds(30) // Poll for updates every 30 seconds .build() diff --git a/src/client/async_client.rs b/src/client/async_client.rs index 35158d9c..57bcf7c0 100644 --- a/src/client/async_client.rs +++ b/src/client/async_client.rs @@ -237,11 +237,11 @@ pub async fn client>(options: C) -> Client { let (local_evaluator, flag_poller) = if options.enable_local_evaluation && !options.is_disabled() { - if let Some(ref personal_key) = options.personal_api_key { + if let Some(ref secret_key) = options.secret_key { let cache = FlagCache::new(); let config = LocalEvaluationConfig { - personal_api_key: personal_key.clone(), + personal_api_key: secret_key.clone(), project_api_key: options.api_key.clone(), api_host: options.endpoints().api_host(), poll_interval: Duration::from_secs(options.poll_interval_seconds), diff --git a/src/client/blocking.rs b/src/client/blocking.rs index d498ccb0..279e3222 100644 --- a/src/client/blocking.rs +++ b/src/client/blocking.rs @@ -221,11 +221,11 @@ pub fn client>(options: C) -> Client { let (local_evaluator, flag_poller) = if options.enable_local_evaluation && !options.is_disabled() { - if let Some(ref personal_key) = options.personal_api_key { + if let Some(ref secret_key) = options.secret_key { let cache = FlagCache::new(); let config = LocalEvaluationConfig { - personal_api_key: personal_key.clone(), + personal_api_key: secret_key.clone(), project_api_key: options.api_key.clone(), api_host: options.endpoints().api_host(), poll_interval: Duration::from_secs(options.poll_interval_seconds), diff --git a/src/client/mod.rs b/src/client/mod.rs index bf7d1299..954bc2ba 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -82,10 +82,12 @@ pub struct ClientOptions { #[builder(default = "30")] request_timeout_seconds: u64, - /// Personal API key for fetching flag definitions. Required when - /// `enable_local_evaluation` is `true`. + /// Secret key used for local feature flag evaluation and remote config. + /// + /// Accepts either a Personal API Key (`phx_...`) or a Project Secret API + /// Key (`phs_...`). Required when `enable_local_evaluation` is `true`. #[builder(setter(into, strip_option), default)] - personal_api_key: Option, + secret_key: Option, /// Enable local evaluation of feature flags using a background definitions /// poller. @@ -209,8 +211,8 @@ impl ClientOptions { } None => DEFAULT_HOST.to_string(), }); - self.personal_api_key = self.personal_api_key.and_then(|personal_api_key| { - let normalized = personal_api_key.trim().to_string(); + self.secret_key = self.secret_key.and_then(|secret_key| { + let normalized = secret_key.trim().to_string(); if normalized.is_empty() { None } else { @@ -240,6 +242,19 @@ impl ClientOptionsBuilder { pub fn build(&self) -> Result { Ok(self.build_unchecked()?.sanitize()) } + + /// Deprecated alias for [`secret_key`](Self::secret_key). + /// + /// Kept for backwards compatibility. When both are set, `secret_key` wins. + #[deprecated( + note = "use `secret_key` instead; it accepts a Personal API Key or a Project Secret API Key" + )] + pub fn personal_api_key(&mut self, value: impl Into) -> &mut Self { + if self.secret_key.is_none() { + self.secret_key = Some(Some(value.into())); + } + self + } } impl From<&str> for ClientOptions { @@ -273,16 +288,50 @@ mod tests { let options = ClientOptionsBuilder::default() .api_key(" \n test-api-key\t ".to_string()) .host(" \nhttps://eu.posthog.com/\t ") - .personal_api_key(" \n\t ") + .secret_key(" \n\t ") .build() .unwrap(); assert_eq!(options.api_key, "test-api-key"); assert_eq!(options.host.as_deref(), Some("https://eu.posthog.com/")); - assert_eq!(options.personal_api_key, None); + assert_eq!(options.secret_key, None); assert_eq!(options.endpoints().api_host(), EU_INGESTION_ENDPOINT); } + #[test] + #[allow(deprecated)] + fn resolves_secret_key_over_personal_api_key() { + let secret_only = ClientOptionsBuilder::default() + .api_key("test-api-key".to_string()) + .secret_key("phs_secret") + .build() + .unwrap(); + assert_eq!(secret_only.secret_key.as_deref(), Some("phs_secret")); + + let personal_only = ClientOptionsBuilder::default() + .api_key("test-api-key".to_string()) + .personal_api_key("phx_personal") + .build() + .unwrap(); + assert_eq!(personal_only.secret_key.as_deref(), Some("phx_personal")); + + let both = ClientOptionsBuilder::default() + .api_key("test-api-key".to_string()) + .personal_api_key("phx_personal") + .secret_key("phs_secret") + .build() + .unwrap(); + assert_eq!(both.secret_key.as_deref(), Some("phs_secret")); + + let both_reversed = ClientOptionsBuilder::default() + .api_key("test-api-key".to_string()) + .secret_key("phs_secret") + .personal_api_key("phx_personal") + .build() + .unwrap(); + assert_eq!(both_reversed.secret_key.as_deref(), Some("phs_secret")); + } + #[test] fn defaults_blank_host_after_trimming_whitespace() { let options = ClientOptionsBuilder::default() diff --git a/tests/test_local_evaluation.rs b/tests/test_local_evaluation.rs index 7b5c71cd..ea8f2506 100644 --- a/tests/test_local_evaluation.rs +++ b/tests/test_local_evaluation.rs @@ -204,7 +204,7 @@ async fn test_local_evaluation_with_mock_server() { let options = ClientOptionsBuilder::default() .host(server.base_url()) .api_key("test_project_key".to_string()) - .personal_api_key("test_personal_key".to_string()) + .secret_key("test_personal_key".to_string()) .enable_local_evaluation(true) .poll_interval_seconds(60) .build() From d863b966df88cf916835544df76d7fc70eab93f8 Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Thu, 2 Jul 2026 12:15:45 -0400 Subject: [PATCH 4/6] chore: update public API snapshot and parameterize secret_key test --- api/public-api.txt | 3 ++- src/client/mod.rs | 51 ++++++++++++++++++++-------------------------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/api/public-api.txt b/api/public-api.txt index 5ad25c66..d682ab3d 100644 --- a/api/public-api.txt +++ b/api/public-api.txt @@ -156,16 +156,17 @@ pub fn posthog_rs::ClientOptionsBuilder::local_evaluation_only(&mut self, bool) pub fn posthog_rs::ClientOptionsBuilder::max_batch_size(&mut self, usize) -> &mut Self pub fn posthog_rs::ClientOptionsBuilder::max_capture_attempts(&mut self, u32) -> &mut Self pub fn posthog_rs::ClientOptionsBuilder::max_queue_size(&mut self, usize) -> &mut Self -pub fn posthog_rs::ClientOptionsBuilder::personal_api_key>(&mut self, VALUE) -> &mut Self pub fn posthog_rs::ClientOptionsBuilder::poll_interval_seconds(&mut self, u64) -> &mut Self pub fn posthog_rs::ClientOptionsBuilder::request_timeout_seconds(&mut self, u64) -> &mut Self pub fn posthog_rs::ClientOptionsBuilder::retry_initial_backoff_ms(&mut self, u64) -> &mut Self pub fn posthog_rs::ClientOptionsBuilder::retry_max_backoff_ms(&mut self, u64) -> &mut Self +pub fn posthog_rs::ClientOptionsBuilder::secret_key>(&mut self, VALUE) -> &mut Self pub fn posthog_rs::ClientOptionsBuilder::shutdown_timeout_ms(&mut self, u64) -> &mut Self impl posthog_rs::ClientOptionsBuilder pub fn posthog_rs::ClientOptionsBuilder::before_send(&mut self, F) -> &mut Self where F: core::ops::function::FnMut(posthog_rs::Event) -> core::option::Option + core::marker::Send + 'static pub fn posthog_rs::ClientOptionsBuilder::build(&self) -> core::result::Result pub fn posthog_rs::ClientOptionsBuilder::on_error(&mut self, F) -> &mut Self where F: core::ops::function::Fn(&posthog_rs::PostHogError<'_>) + core::marker::Send + core::marker::Sync + 'static +pub fn posthog_rs::ClientOptionsBuilder::personal_api_key(&mut self, impl core::convert::Into) -> &mut Self impl core::default::Default for posthog_rs::ClientOptionsBuilder pub fn posthog_rs::ClientOptionsBuilder::default() -> Self pub struct posthog_rs::Cohort diff --git a/src/client/mod.rs b/src/client/mod.rs index ed1cd69e..2c00a7e7 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -459,35 +459,28 @@ mod tests { #[test] #[allow(deprecated)] fn resolves_secret_key_over_personal_api_key() { - let secret_only = ClientOptionsBuilder::default() - .api_key("test-api-key".to_string()) - .secret_key("phs_secret") - .build() - .unwrap(); - assert_eq!(secret_only.secret_key.as_deref(), Some("phs_secret")); - - let personal_only = ClientOptionsBuilder::default() - .api_key("test-api-key".to_string()) - .personal_api_key("phx_personal") - .build() - .unwrap(); - assert_eq!(personal_only.secret_key.as_deref(), Some("phx_personal")); - - let both = ClientOptionsBuilder::default() - .api_key("test-api-key".to_string()) - .personal_api_key("phx_personal") - .secret_key("phs_secret") - .build() - .unwrap(); - assert_eq!(both.secret_key.as_deref(), Some("phs_secret")); - - let both_reversed = ClientOptionsBuilder::default() - .api_key("test-api-key".to_string()) - .secret_key("phs_secret") - .personal_api_key("phx_personal") - .build() - .unwrap(); - assert_eq!(both_reversed.secret_key.as_deref(), Some("phs_secret")); + let cases: [(Option<&str>, Option<&str>, Option<&str>); 3] = [ + (Some("phs_secret"), None, Some("phs_secret")), + (None, Some("phx_personal"), Some("phx_personal")), + (Some("phs_secret"), Some("phx_personal"), Some("phs_secret")), + ]; + + for (secret, personal, expected) in cases { + let mut builder = ClientOptionsBuilder::default(); + builder.api_key("test-api-key".to_string()); + if let Some(personal) = personal { + builder.personal_api_key(personal); + } + if let Some(secret) = secret { + builder.secret_key(secret); + } + let options = builder.build().unwrap(); + assert_eq!( + options.secret_key.as_deref(), + expected, + "{secret:?} {personal:?}" + ); + } } #[test] From 41652f00ff6a95f366ae3ff6a7609c7c1fd72529 Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Tue, 7 Jul 2026 09:55:15 -0400 Subject: [PATCH 5/6] refactor: address review on deprecated personal_api_key alias - Restore the original > signature so the public API of personal_api_key is unchanged (only secret_key is added). - Drop the guard so the last builder call wins, matching how the alias behaved before (transparent forward into secret_key). - Update the local-eval fallback warning to reference secret_key (blocking + async). --- api/public-api.txt | 2 +- src/client/async_client.rs | 4 ++- src/client/blocking.rs | 4 ++- src/client/mod.rs | 55 +++++++++++++++++++++----------------- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/api/public-api.txt b/api/public-api.txt index d682ab3d..b80542a3 100644 --- a/api/public-api.txt +++ b/api/public-api.txt @@ -166,7 +166,7 @@ impl posthog_rs::ClientOptionsBuilder pub fn posthog_rs::ClientOptionsBuilder::before_send(&mut self, F) -> &mut Self where F: core::ops::function::FnMut(posthog_rs::Event) -> core::option::Option + core::marker::Send + 'static pub fn posthog_rs::ClientOptionsBuilder::build(&self) -> core::result::Result pub fn posthog_rs::ClientOptionsBuilder::on_error(&mut self, F) -> &mut Self where F: core::ops::function::Fn(&posthog_rs::PostHogError<'_>) + core::marker::Send + core::marker::Sync + 'static -pub fn posthog_rs::ClientOptionsBuilder::personal_api_key(&mut self, impl core::convert::Into) -> &mut Self +pub fn posthog_rs::ClientOptionsBuilder::personal_api_key>(&mut self, VALUE) -> &mut Self impl core::default::Default for posthog_rs::ClientOptionsBuilder pub fn posthog_rs::ClientOptionsBuilder::default() -> Self pub struct posthog_rs::Cohort diff --git a/src/client/async_client.rs b/src/client/async_client.rs index ec058e3d..c67123ef 100644 --- a/src/client/async_client.rs +++ b/src/client/async_client.rs @@ -150,7 +150,9 @@ pub async fn client>(options: C) -> Client { (Some(LocalEvaluator::new(cache)), Some(poller)) } else { - warn!("Local evaluation enabled but personal_api_key not set, falling back to API evaluation"); + warn!( + "Local evaluation enabled but secret_key not set, falling back to API evaluation" + ); (None, None) } } else { diff --git a/src/client/blocking.rs b/src/client/blocking.rs index 3e9f5953..873d4524 100644 --- a/src/client/blocking.rs +++ b/src/client/blocking.rs @@ -154,7 +154,9 @@ pub fn client>(options: C) -> Client { (Some(LocalEvaluator::new(cache)), Some(poller)) } else { - warn!("Local evaluation enabled but personal_api_key not set, falling back to API evaluation"); + warn!( + "Local evaluation enabled but secret_key not set, falling back to API evaluation" + ); (None, None) } } else { diff --git a/src/client/mod.rs b/src/client/mod.rs index 2c00a7e7..6db8b39b 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -403,14 +403,13 @@ impl ClientOptionsBuilder { /// Deprecated alias for [`secret_key`](Self::secret_key). /// - /// Kept for backwards compatibility. When both are set, `secret_key` wins. + /// Kept for backwards compatibility; forwards to `secret_key`. The last + /// builder call wins if both are set. #[deprecated( note = "use `secret_key` instead; it accepts a Personal API Key or a Project Secret API Key" )] - pub fn personal_api_key(&mut self, value: impl Into) -> &mut Self { - if self.secret_key.is_none() { - self.secret_key = Some(Some(value.into())); - } + pub fn personal_api_key>(&mut self, value: VALUE) -> &mut Self { + self.secret_key = Some(Some(value.into())); self } } @@ -458,29 +457,35 @@ mod tests { #[test] #[allow(deprecated)] - fn resolves_secret_key_over_personal_api_key() { - let cases: [(Option<&str>, Option<&str>, Option<&str>); 3] = [ - (Some("phs_secret"), None, Some("phs_secret")), - (None, Some("phx_personal"), Some("phx_personal")), - (Some("phs_secret"), Some("phx_personal"), Some("phs_secret")), - ]; - - for (secret, personal, expected) in cases { + fn personal_api_key_forwards_to_secret_key_last_call_wins() { + let resolve = |calls: &[(&str, &str)]| { let mut builder = ClientOptionsBuilder::default(); builder.api_key("test-api-key".to_string()); - if let Some(personal) = personal { - builder.personal_api_key(personal); - } - if let Some(secret) = secret { - builder.secret_key(secret); + for (which, val) in calls { + match *which { + "secret" => builder.secret_key(*val), + _ => builder.personal_api_key(*val), + }; } - let options = builder.build().unwrap(); - assert_eq!( - options.secret_key.as_deref(), - expected, - "{secret:?} {personal:?}" - ); - } + builder.build().unwrap().secret_key + }; + + assert_eq!( + resolve(&[("secret", "phs_secret")]).as_deref(), + Some("phs_secret") + ); + assert_eq!( + resolve(&[("personal", "phx_personal")]).as_deref(), + Some("phx_personal") + ); + assert_eq!( + resolve(&[("personal", "phx_personal"), ("secret", "phs_secret")]).as_deref(), + Some("phs_secret") + ); + assert_eq!( + resolve(&[("secret", "phs_secret"), ("personal", "phx_personal")]).as_deref(), + Some("phx_personal") + ); } #[test] From 65bf0392ee6caa14425bd2625dc3128043a6c2ac Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Tue, 7 Jul 2026 09:57:53 -0400 Subject: [PATCH 6/6] style: format with stable rustfmt (CI toolchain) --- src/client/async_client.rs | 49 +++++++++++++++++++------------------- src/client/blocking.rs | 49 +++++++++++++++++++------------------- 2 files changed, 48 insertions(+), 50 deletions(-) diff --git a/src/client/async_client.rs b/src/client/async_client.rs index c67123ef..e6483e4b 100644 --- a/src/client/async_client.rs +++ b/src/client/async_client.rs @@ -130,34 +130,33 @@ pub async fn client>(options: C) -> Client { .build() .unwrap(); // Unwrap here is as safe as `HttpClient::new` - let (local_evaluator, flag_poller) = if options.enable_local_evaluation - && !options.is_disabled() - { - if let Some(ref secret_key) = options.secret_key { - let cache = FlagCache::new(); - - let config = LocalEvaluationConfig { - personal_api_key: secret_key.clone(), - project_api_key: options.api_key.clone(), - api_host: options.endpoints().api_host(), - poll_interval: Duration::from_secs(options.poll_interval_seconds), - request_timeout: Duration::from_secs(options.request_timeout_seconds), - }; - - let mut poller = AsyncFlagPoller::new(config, cache.clone()); - poller.set_on_error(options.on_error.clone()); - poller.start().await; - - (Some(LocalEvaluator::new(cache)), Some(poller)) - } else { - warn!( + let (local_evaluator, flag_poller) = + if options.enable_local_evaluation && !options.is_disabled() { + if let Some(ref secret_key) = options.secret_key { + let cache = FlagCache::new(); + + let config = LocalEvaluationConfig { + personal_api_key: secret_key.clone(), + project_api_key: options.api_key.clone(), + api_host: options.endpoints().api_host(), + poll_interval: Duration::from_secs(options.poll_interval_seconds), + request_timeout: Duration::from_secs(options.request_timeout_seconds), + }; + + let mut poller = AsyncFlagPoller::new(config, cache.clone()); + poller.set_on_error(options.on_error.clone()); + poller.start().await; + + (Some(LocalEvaluator::new(cache)), Some(poller)) + } else { + warn!( "Local evaluation enabled but secret_key not set, falling back to API evaluation" ); + (None, None) + } + } else { (None, None) - } - } else { - (None, None) - }; + }; let transport = if options.is_disabled() { None diff --git a/src/client/blocking.rs b/src/client/blocking.rs index 873d4524..82af10bb 100644 --- a/src/client/blocking.rs +++ b/src/client/blocking.rs @@ -134,34 +134,33 @@ pub fn client>(options: C) -> Client { .build() .unwrap(); // Unwrap here is as safe as `HttpClient::new` - let (local_evaluator, flag_poller) = if options.enable_local_evaluation - && !options.is_disabled() - { - if let Some(ref secret_key) = options.secret_key { - let cache = FlagCache::new(); - - let config = LocalEvaluationConfig { - personal_api_key: secret_key.clone(), - project_api_key: options.api_key.clone(), - api_host: options.endpoints().api_host(), - poll_interval: Duration::from_secs(options.poll_interval_seconds), - request_timeout: Duration::from_secs(options.request_timeout_seconds), - }; - - let mut poller = FlagPoller::new(config, cache.clone()); - poller.set_on_error(options.on_error.clone()); - poller.start(); - - (Some(LocalEvaluator::new(cache)), Some(poller)) - } else { - warn!( + let (local_evaluator, flag_poller) = + if options.enable_local_evaluation && !options.is_disabled() { + if let Some(ref secret_key) = options.secret_key { + let cache = FlagCache::new(); + + let config = LocalEvaluationConfig { + personal_api_key: secret_key.clone(), + project_api_key: options.api_key.clone(), + api_host: options.endpoints().api_host(), + poll_interval: Duration::from_secs(options.poll_interval_seconds), + request_timeout: Duration::from_secs(options.request_timeout_seconds), + }; + + let mut poller = FlagPoller::new(config, cache.clone()); + poller.set_on_error(options.on_error.clone()); + poller.start(); + + (Some(LocalEvaluator::new(cache)), Some(poller)) + } else { + warn!( "Local evaluation enabled but secret_key not set, falling back to API evaluation" ); + (None, None) + } + } else { (None, None) - } - } else { - (None, None) - }; + }; let transport = if options.is_disabled() { None