diff --git a/crates/relayburn-sdk/Cargo.toml b/crates/relayburn-sdk/Cargo.toml index 4e8e305a..d5e28dd3 100644 --- a/crates/relayburn-sdk/Cargo.toml +++ b/crates/relayburn-sdk/Cargo.toml @@ -7,6 +7,14 @@ license.workspace = true repository.workspace = true description = "Embedding API for relayburn — published to crates.io as the supported Rust surface." +[features] +# Opt-in feature for downstream integration tests that need the +# test-only API (`set_ingest_gap_writer` / `restore_ingest_gap_writer`). +# In-crate unit tests reach the same items via `cfg(test)`; external +# crates wanting the same hooks enable this feature explicitly so the +# items are NOT part of the SDK's default public surface. +test-utils = [] + [dependencies] # Deps absorbed from the four former lower crates # (`relayburn-{reader,ledger,analyze,ingest}`) when the monolith diff --git a/crates/relayburn-sdk/src/ingest.rs b/crates/relayburn-sdk/src/ingest.rs index 00264dcb..5ae13ada 100644 --- a/crates/relayburn-sdk/src/ingest.rs +++ b/crates/relayburn-sdk/src/ingest.rs @@ -66,9 +66,14 @@ pub use cursors::{ }; pub use gap::{ count_new_tool_calls, count_new_tool_results, count_tool_call_gaps, emit_gap_warning, - record_session_gap, reset_ingest_gap_warnings, restore_ingest_gap_writer, - set_ingest_gap_writer, AdapterName, ToolCallGapCounts, + record_session_gap, reset_ingest_gap_warnings, AdapterName, ToolCallGapCounts, }; +// Test-only writer-override hooks. Gated to `cfg(test)` for in-crate +// tests and to the `test-utils` feature for downstream integration +// tests; deliberately NOT part of the default SDK surface so embedders +// can't hijack the global gap-warning writer for the whole process. +#[cfg(any(test, feature = "test-utils"))] +pub use gap::{restore_ingest_gap_writer, set_ingest_gap_writer}; pub use ingest::{ ingest_all, ingest_claude_projects, ingest_claude_session, ingest_codex_sessions, ingest_opencode_sessions, IngestOptions, IngestReport, IngestRoots, diff --git a/crates/relayburn-sdk/src/ingest/gap.rs b/crates/relayburn-sdk/src/ingest/gap.rs index b3224002..7d50dcc5 100644 --- a/crates/relayburn-sdk/src/ingest/gap.rs +++ b/crates/relayburn-sdk/src/ingest/gap.rs @@ -135,6 +135,12 @@ pub fn reset_ingest_gap_warnings() { /// back to `set_ingest_gap_writer` in a `defer`-equivalent so a panicking /// test leaves the global state restored on the next test that pulls the /// guard. +/// +/// Gated to `cfg(test)` for in-crate tests and to the `test-utils` +/// feature for downstream integration tests so the hook is NOT part of +/// the SDK's default public surface — otherwise an embedder could +/// hijack stderr writes for the whole process. +#[cfg(any(test, feature = "test-utils"))] pub fn set_ingest_gap_writer(write: F) -> WriterFn where F: Fn(&str) + Send + Sync + 'static, @@ -146,6 +152,9 @@ where /// Restore a previously captured sink. Convenience for the /// `let prev = set_ingest_gap_writer(...); ...; restore_ingest_gap_writer(prev);` /// idiom — equivalent to `setIngestGapWriter(prev)` in TS. +/// +/// Gated alongside `set_ingest_gap_writer` — see that function. +#[cfg(any(test, feature = "test-utils"))] pub fn restore_ingest_gap_writer(write: WriterFn) { state_lock().write = write; }