lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit 2c5927503fd34e3110906b3bbff46c42eabcffa7
parent 9002c272e4973d72d571d3787b2a3b65abd51f15
Author: triesap <tyson@radroots.org>
Date:   Sat,  1 Aug 2026 07:14:41 +0000

nostr-connect: quarantine superseded package surface

- Keep the canonical client and transport surface while hiding the publish-frozen bridge.
- Move package coverage and protocol tests off the transitional prelude.
- Record the exact CLI, Myc, residual-consumer, and final-removal steps.
- Verify package checks, tests, clippy, architecture policy, and the public API baseline.

Diffstat:
Mcontracts/releases/api_boundaries.toml | 12++++++------
Mcrates/nostr_connect/src/client.rs | 17+++++++++++------
Mcrates/nostr_connect/src/lib.rs | 30++++++++++++++----------------
Mcrates/nostr_connect/tests/client.rs | 1-
Mcrates/nostr_connect/tests/coverage.rs | 540+++++++++++++++++++++++++++++++++++++------------------------------------------
Mcrates/nostr_connect/tests/package_boundary.rs | 43+++++++++++++++++++++++++++++++++++++------
Mcrates/nostr_connect/tests/protocol.rs | 145++++++++++++++++++++++++++++++++++++-------------------------------------------
Mdocs/api/radroots_nostr_connect.txt | 26--------------------------
Mdocs/implementation/COMPATIBILITY_SHIMS.md | 6++++--
9 files changed, 389 insertions(+), 431 deletions(-)

diff --git a/contracts/releases/api_boundaries.toml b/contracts/releases/api_boundaries.toml @@ -117,8 +117,8 @@ observed_paths = [ "nostr::RelayUrl", ] adr = "docs/decisions/0001-public-api-leakage-migration-baseline.md" -removal_step = 140 -rationale = "Legacy client state-machine signatures retain exact protocol types until the Nostr Connect transport split and conformance gate." +removal_step = 313 +rationale = "Hidden, publish-frozen client signatures remain only for CLI, Myc, and residual first-party cutovers in Steps 271, 288, and 293." [[exception]] id = "RCRV1-API-007" @@ -138,8 +138,8 @@ observed_paths = [ "nostr::UnsignedEvent", ] adr = "docs/decisions/0001-public-api-leakage-migration-baseline.md" -removal_step = 140 -rationale = "Legacy request and response envelopes retain exact Nostr values until canonical package-owned envelopes are complete." +removal_step = 313 +rationale = "Hidden, publish-frozen aliases remain only for CLI, Myc, and residual first-party cutovers in Steps 271, 288, and 293." [[exception]] id = "RCRV1-API-008" @@ -152,5 +152,5 @@ items = [ ] observed_paths = ["nostr::PublicKey", "nostr::RelayUrl"] adr = "docs/decisions/0001-public-api-leakage-migration-baseline.md" -removal_step = 140 -rationale = "Legacy URI wrappers retain exact upstream value types until URI normalization and the package conformance gate." +removal_step = 313 +rationale = "Hidden, publish-frozen URI aliases remain only for CLI, Myc, and residual first-party cutovers in Steps 271, 288, and 293." diff --git a/crates/nostr_connect/src/client.rs b/crates/nostr_connect/src/client.rs @@ -457,15 +457,18 @@ fn build_request_event_for( .map_err(sign_error) } +#[doc(hidden)] pub type RadrootsNostrConnectClientTransportFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T, RadrootsNostrConnectError>> + Send + 'a>>; +#[doc(hidden)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct RadrootsNostrConnectClientTarget { pub remote_signer_public_key: PublicKey, pub relays: Vec<RelayUrl>, } +#[doc(hidden)] impl RadrootsNostrConnectClientTarget { pub fn new(remote_signer_public_key: PublicKey, relays: Vec<RelayUrl>) -> Self { Self { @@ -475,12 +478,14 @@ impl RadrootsNostrConnectClientTarget { } } +#[doc(hidden)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct RadrootsNostrConnectClientRequest { pub request_id: String, pub request: Request, } +#[doc(hidden)] impl RadrootsNostrConnectClientRequest { pub fn new(request_id: impl Into<String>, request: Request) -> Self { Self { @@ -498,11 +503,13 @@ impl RadrootsNostrConnectClientRequest { } } +#[doc(hidden)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum RadrootsNostrConnectClientProgress { AuthChallenge { url: String }, } +#[doc(hidden)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum RadrootsNostrConnectClientEventOutcome { Ignore, @@ -510,6 +517,7 @@ pub enum RadrootsNostrConnectClientEventOutcome { Response(Response), } +#[doc(hidden)] pub trait RadrootsNostrConnectClientTransport { fn publish_request_event<'a>( &'a mut self, @@ -521,6 +529,7 @@ pub trait RadrootsNostrConnectClientTransport { ) -> RadrootsNostrConnectClientTransportFuture<'a, Event>; } +#[doc(hidden)] pub fn build_request_event( client_keys: &Keys, target: &RadrootsNostrConnectClientTarget, @@ -541,6 +550,7 @@ pub fn build_request_event( .map_err(sign_error) } +#[doc(hidden)] pub fn parse_response_event( client_keys: &Keys, target: &RadrootsNostrConnectClientTarget, @@ -551,11 +561,9 @@ pub fn parse_response_event( if event.kind != Kind::Custom(RPC_KIND) { return Ok(RadrootsNostrConnectClientEventOutcome::Ignore); } - if event.pubkey != target.remote_signer_public_key { return Ok(RadrootsNostrConnectClientEventOutcome::Ignore); } - let client_public_key = client_keys.public_key(); if !event .tags @@ -567,7 +575,6 @@ pub fn parse_response_event( event .verify() .map_err(|_| RadrootsNostrConnectError::InvalidClientEvent)?; - let decrypted = nip44::decrypt( client_keys.secret_key(), &target.remote_signer_public_key, @@ -576,13 +583,11 @@ pub fn parse_response_event( .map_err(|error| RadrootsNostrConnectError::Decrypt { reason: error.to_string(), })?; - let envelope: ResponseEnvelope = serde_json::from_str(&decrypted).map_err(RadrootsNostrConnectError::from)?; if envelope.id != request_id { return Ok(RadrootsNostrConnectClientEventOutcome::Ignore); } - let response = Response::from_envelope(method, envelope)?; Ok(match response { Response::AuthUrl(url) => RadrootsNostrConnectClientEventOutcome::Progress( @@ -592,6 +597,7 @@ pub fn parse_response_event( }) } +#[doc(hidden)] pub async fn execute_request_with_transport<T, F>( client_keys: &Keys, target: &RadrootsNostrConnectClientTarget, @@ -607,7 +613,6 @@ where let request_id = request.request_id.clone(); let event = build_request_event(client_keys, target, request.into_message())?; transport.publish_request_event(event).await?; - loop { let event = transport.next_response_event().await?; match parse_response_event(client_keys, target, &request_id, &method, &event)? { diff --git a/crates/nostr_connect/src/lib.rs b/crates/nostr_connect/src/lib.rs @@ -19,8 +19,11 @@ pub use permission::Permission; pub use server::Server; pub use uri::{BunkerUri, ClientUri}; -// Transitional compatibility surface for consumers migrated in Step 141. -// Publication remains disabled, and Step 143 removes this module. +/// Private migration surface for separate first-party repositories. +/// +/// This module is excluded from the reviewed public API baseline, the package +/// remains `publish = false`, consumers cut over in Steps 271, 288, and 293, +/// and Step 313 removes the shim in full. #[doc(hidden)] pub mod prelude { pub use crate::client::{ @@ -49,25 +52,20 @@ pub mod prelude { Response as RadrootsNostrConnectResponse, ResponseEnvelope as RadrootsNostrConnectResponseEnvelope, }; - pub use crate::method::Method; - pub use crate::method::Method as RadrootsNostrConnectMethod; - pub use crate::permission::{Permission, Permissions}; + pub use crate::method::{Method, Method as RadrootsNostrConnectMethod}; pub use crate::permission::{ - Permission as RadrootsNostrConnectPermission, + Permission, Permission as RadrootsNostrConnectPermission, Permissions, Permissions as RadrootsNostrConnectPermissions, }; pub use crate::uri::{ - BUNKER_URI_SCHEME, BunkerUri, CLIENT_METADATA_JSON_MAX_BYTES, CLIENT_NAME_MAX_BYTES, - CLIENT_URL_MAX_BYTES, ClientMetadata, ClientUri, URI_SCHEME, Uri, - }; - pub use crate::uri::{ - BUNKER_URI_SCHEME as RADROOTS_NOSTR_CONNECT_BUNKER_URI_SCHEME, - BunkerUri as RadrootsNostrConnectBunkerUri, + BUNKER_URI_SCHEME, BUNKER_URI_SCHEME as RADROOTS_NOSTR_CONNECT_BUNKER_URI_SCHEME, + BunkerUri, BunkerUri as RadrootsNostrConnectBunkerUri, CLIENT_METADATA_JSON_MAX_BYTES, CLIENT_METADATA_JSON_MAX_BYTES as RADROOTS_NOSTR_CONNECT_CLIENT_METADATA_JSON_MAX_BYTES, + CLIENT_NAME_MAX_BYTES, CLIENT_NAME_MAX_BYTES as RADROOTS_NOSTR_CONNECT_CLIENT_NAME_MAX_BYTES, - CLIENT_URL_MAX_BYTES as RADROOTS_NOSTR_CONNECT_CLIENT_URL_MAX_BYTES, - ClientMetadata as RadrootsNostrConnectClientMetadata, - ClientUri as RadrootsNostrConnectClientUri, - URI_SCHEME as RADROOTS_NOSTR_CONNECT_URI_SCHEME, Uri as RadrootsNostrConnectUri, + CLIENT_URL_MAX_BYTES, CLIENT_URL_MAX_BYTES as RADROOTS_NOSTR_CONNECT_CLIENT_URL_MAX_BYTES, + ClientMetadata, ClientMetadata as RadrootsNostrConnectClientMetadata, ClientUri, + ClientUri as RadrootsNostrConnectClientUri, URI_SCHEME, + URI_SCHEME as RADROOTS_NOSTR_CONNECT_URI_SCHEME, Uri, Uri as RadrootsNostrConnectUri, }; } diff --git a/crates/nostr_connect/tests/client.rs b/crates/nostr_connect/tests/client.rs @@ -372,7 +372,6 @@ async fn executes_request_through_transport_with_auth_progress() { ) ); } - #[tokio::test] async fn ignores_events_not_addressed_by_expected_signer_and_client() { let client_keys = client_keys(); diff --git a/crates/nostr_connect/tests/coverage.rs b/crates/nostr_connect/tests/coverage.rs @@ -2,15 +2,13 @@ mod test_fixtures; use nostr::{Event, EventBuilder, JsonUtil, Keys, SecretKey, Timestamp, UnsignedEvent}; -use radroots_nostr_connect::prelude::{ - CLIENT_URL_MAX_BYTES, ClientMetadata, Method, Permission, Permissions, - RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR, RadrootsNostrConnectError, - RadrootsNostrConnectPendingConnectionPollOutcome, RadrootsNostrConnectRequest, - RadrootsNostrConnectRequestMessage, RadrootsNostrConnectResponse, - RadrootsNostrConnectResponseEnvelope, SignedEvent as ConnectSignedEvent, - UnsignedEvent as ConnectUnsignedEvent, Uri, +use radroots_nostr_connect::message::{ + PENDING_CONNECTION_ERROR, PendingConnectionOutcome, RemoteSessionCapability, RequestMessage, + ResponseEnvelope, SignedEvent as ConnectSignedEvent, UnsignedEvent as ConnectUnsignedEvent, }; -use radroots_nostr_connect::uri::RelayUrl; +use radroots_nostr_connect::permission::Permissions; +use radroots_nostr_connect::uri::{CLIENT_URL_MAX_BYTES, ClientMetadata, RelayUrl, Uri}; +use radroots_nostr_connect::{Error, Method, Permission, Request, Response}; use serde_json::{Value, json}; use std::str::FromStr; use test_fixtures::{ @@ -61,8 +59,8 @@ fn relay(value: &str) -> RelayUrl { fn error_method_and_permission_surfaces_cover_public_paths() { let json_error = serde_json::from_str::<Value>("{").expect_err("invalid json"); assert!(matches!( - RadrootsNostrConnectError::from(json_error), - RadrootsNostrConnectError::Json(message) if !message.is_empty() + Error::from(json_error), + Error::Json(message) if !message.is_empty() )); let methods = [ @@ -88,7 +86,7 @@ fn error_method_and_permission_surfaces_cover_public_paths() { ); assert!(matches!( Method::from_str(" "), - Err(RadrootsNostrConnectError::InvalidMethod(value)) if value == " " + Err(Error::InvalidMethod(value)) if value == " " )); assert_eq!( serde_json::from_str::<Method>("\"do_work\"").expect("deserialize custom method"), @@ -117,15 +115,15 @@ fn error_method_and_permission_surfaces_cover_public_paths() { ); assert!(matches!( Permission::from_str(" "), - Err(RadrootsNostrConnectError::InvalidPermission(value)) if value == " " + Err(Error::InvalidPermission(value)) if value == " " )); assert!(matches!( Permission::from_str("sign_event:"), - Err(RadrootsNostrConnectError::InvalidPermission(value)) if value == "sign_event:" + Err(Error::InvalidPermission(value)) if value == "sign_event:" )); assert!(matches!( Permission::from_str(" :kind"), - Err(RadrootsNostrConnectError::InvalidMethod(_)) + Err(Error::InvalidMethod(_)) )); let empty = Permissions::new(); @@ -159,7 +157,7 @@ fn error_method_and_permission_surfaces_cover_public_paths() { ); assert!(matches!( Permissions::from_str("sign_event:,ping"), - Err(RadrootsNostrConnectError::InvalidPermission(value)) if value == "sign_event:" + Err(Error::InvalidPermission(value)) if value == "sign_event:" )); let all_sign_events = Permission::new(Method::SignEvent); @@ -246,22 +244,22 @@ fn uri_surface_covers_rendering_ignored_queries_and_error_paths() { assert!(matches!( Uri::parse("not a uri"), - Err(RadrootsNostrConnectError::InvalidUrl { .. }) + Err(Error::InvalidUrl { .. }) )); assert!(matches!( Uri::parse("nostrconnect:///path?relay=wss%3A%2F%2Frelay.example.com&secret=abc"), - Err(RadrootsNostrConnectError::MissingPublicKey) + Err(Error::MissingPublicKey) )); assert!(matches!( Uri::parse(&format!("bunker://{}", FIXTURE_ALICE.public_key_hex)), - Err(RadrootsNostrConnectError::MissingRelay) + Err(Error::MissingRelay) )); assert!(matches!( Uri::parse(&format!( "nostrconnect://{}?secret=abc", FIXTURE_ALICE.public_key_hex )), - Err(RadrootsNostrConnectError::MissingRelay) + Err(Error::MissingRelay) )); assert!(matches!( Uri::parse(&format!( @@ -269,22 +267,22 @@ fn uri_surface_covers_rendering_ignored_queries_and_error_paths() { FIXTURE_ALICE.public_key_hex, encode_uri_component(RELAY_PRIMARY_WSS), )), - Err(RadrootsNostrConnectError::MissingSecret) + Err(Error::MissingSecret) )); assert!(matches!( Uri::parse("https://example.com"), - Err(RadrootsNostrConnectError::InvalidUriScheme(value)) if value == "https" + Err(Error::InvalidUriScheme(value)) if value == "https" )); assert!(matches!( Uri::parse("nostrconnect://bad-key?relay=wss%3A%2F%2Frelay.example.com&secret=abc"), - Err(RadrootsNostrConnectError::InvalidPublicKey { .. }) + Err(Error::InvalidPublicKey { .. }) )); assert!(matches!( Uri::parse(&format!( "nostrconnect://{}?relay=http%3A%2F%2Frelay.example.com&secret=abc", FIXTURE_ALICE.public_key_hex )), - Err(RadrootsNostrConnectError::InvalidRelayUrl { .. }) + Err(Error::InvalidRelayUrl { .. }) )); assert!(matches!( Uri::parse(&format!( @@ -292,18 +290,18 @@ fn uri_surface_covers_rendering_ignored_queries_and_error_paths() { FIXTURE_ALICE.public_key_hex, encode_uri_component(RELAY_PRIMARY_WSS), )), - Err(RadrootsNostrConnectError::InvalidClientMetadata { field: "url", .. }) + Err(Error::InvalidClientMetadata { field: "url", .. }) )); assert!(matches!( Uri::parse("bunker://bad-key?relay=wss%3A%2F%2Frelay.example.com"), - Err(RadrootsNostrConnectError::InvalidPublicKey { .. }) + Err(Error::InvalidPublicKey { .. }) )); assert!(matches!( Uri::parse(&format!( "bunker://{}?relay=http%3A%2F%2Frelay.example.com", FIXTURE_ALICE.public_key_hex )), - Err(RadrootsNostrConnectError::InvalidRelayUrl { .. }) + Err(Error::InvalidRelayUrl { .. }) )); assert!(matches!( Uri::parse(&format!( @@ -311,7 +309,7 @@ fn uri_surface_covers_rendering_ignored_queries_and_error_paths() { FIXTURE_ALICE.public_key_hex, encode_uri_component(RELAY_PRIMARY_WSS), )), - Err(RadrootsNostrConnectError::InvalidPermission(value)) if value == "sign_event:" + Err(Error::InvalidPermission(value)) if value == "sign_event:" )); assert!(matches!( Uri::parse(&format!( @@ -319,7 +317,7 @@ fn uri_surface_covers_rendering_ignored_queries_and_error_paths() { FIXTURE_ALICE.public_key_hex, encode_uri_component(RELAY_PRIMARY_WSS), )), - Err(RadrootsNostrConnectError::InvalidClientMetadata { field: "image", .. }) + Err(Error::InvalidClientMetadata { field: "image", .. }) )); assert!(matches!( Uri::parse(&format!( @@ -327,7 +325,7 @@ fn uri_surface_covers_rendering_ignored_queries_and_error_paths() { FIXTURE_ALICE.public_key_hex, encode_uri_component(RELAY_PRIMARY_WSS), )), - Err(RadrootsNostrConnectError::MissingSecret) + Err(Error::MissingSecret) )); } @@ -372,7 +370,7 @@ fn client_metadata_rejects_malformed_and_unsafe_display_fields() { assert!(matches!( ClientMetadata::from_connect_param("{"), - Err(RadrootsNostrConnectError::InvalidClientMetadata { + Err(Error::InvalidClientMetadata { field: "payload", .. }) @@ -391,7 +389,7 @@ fn client_metadata_rejects_malformed_and_unsafe_display_fields() { }; assert!(matches!( metadata.normalized(), - Err(RadrootsNostrConnectError::InvalidClientMetadata { + Err(Error::InvalidClientMetadata { field: actual, .. }) if actual == field @@ -405,7 +403,7 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { let requests = vec![ ( - RadrootsNostrConnectRequest::Connect { + Request::Connect { remote_signer_public_key: test_public_key(), secret: None, requested_permissions: Permissions::default(), @@ -415,7 +413,7 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { vec![test_public_key().to_hex()], ), ( - RadrootsNostrConnectRequest::Connect { + Request::Connect { remote_signer_public_key: test_public_key(), secret: None, requested_permissions: ping_permission.clone(), @@ -424,18 +422,14 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { Method::Connect, vec![test_public_key().to_hex(), String::new(), "ping".to_owned()], ), + (Request::GetPublicKey, Method::GetPublicKey, Vec::new()), ( - RadrootsNostrConnectRequest::GetPublicKey, - Method::GetPublicKey, - Vec::new(), - ), - ( - RadrootsNostrConnectRequest::GetSessionCapability, + Request::GetSessionCapability, Method::GetSessionCapability, Vec::new(), ), ( - RadrootsNostrConnectRequest::SignEvent( + Request::SignEvent( ConnectUnsignedEvent::from_json(&unsigned_event().as_json()) .expect("unsigned event payload"), ), @@ -443,7 +437,7 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { vec![serde_json::to_string(&unsigned_event()).expect("serialize unsigned event")], ), ( - RadrootsNostrConnectRequest::Nip04Encrypt { + Request::Nip04Encrypt { public_key: test_public_key(), plaintext: "hello".to_owned(), }, @@ -451,7 +445,7 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { vec![test_public_key().to_hex(), "hello".to_owned()], ), ( - RadrootsNostrConnectRequest::Nip04Decrypt { + Request::Nip04Decrypt { public_key: test_public_key(), ciphertext: "cipher".to_owned(), }, @@ -459,7 +453,7 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { vec![test_public_key().to_hex(), "cipher".to_owned()], ), ( - RadrootsNostrConnectRequest::Nip44Encrypt { + Request::Nip44Encrypt { public_key: test_public_key(), plaintext: "hello".to_owned(), }, @@ -467,26 +461,18 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { vec![test_public_key().to_hex(), "hello".to_owned()], ), ( - RadrootsNostrConnectRequest::Nip44Decrypt { + Request::Nip44Decrypt { public_key: test_public_key(), ciphertext: "cipher".to_owned(), }, Method::Nip44Decrypt, vec![test_public_key().to_hex(), "cipher".to_owned()], ), - (RadrootsNostrConnectRequest::Ping, Method::Ping, Vec::new()), - ( - RadrootsNostrConnectRequest::SwitchRelays, - Method::SwitchRelays, - Vec::new(), - ), + (Request::Ping, Method::Ping, Vec::new()), + (Request::SwitchRelays, Method::SwitchRelays, Vec::new()), + (Request::Logout, Method::Logout, Vec::new()), ( - RadrootsNostrConnectRequest::Logout, - Method::Logout, - Vec::new(), - ), - ( - RadrootsNostrConnectRequest::Custom { + Request::Custom { method: Method::custom("publish_note").expect("valid custom NIP-46 method"), params: vec!["one".to_owned(), "two".to_owned()], }, @@ -500,9 +486,9 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { } assert_eq!( - RadrootsNostrConnectRequest::from_parts(Method::Connect, vec![test_public_key().to_hex()],) + Request::from_parts(Method::Connect, vec![test_public_key().to_hex()],) .expect("connect without secret or perms"), - RadrootsNostrConnectRequest::Connect { + Request::Connect { remote_signer_public_key: test_public_key(), secret: None, requested_permissions: Permissions::default(), @@ -510,12 +496,12 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { } ); assert_eq!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Connect, vec![test_public_key().to_hex(), String::new(), "ping".to_owned()], ) .expect("connect with empty secret"), - RadrootsNostrConnectRequest::Connect { + Request::Connect { remote_signer_public_key: test_public_key(), secret: None, requested_permissions: Permissions::from(vec![Permission::new(Method::Ping),]), @@ -523,67 +509,65 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { } ); assert_eq!( - RadrootsNostrConnectRequest::from_parts(Method::GetPublicKey, Vec::new(),) - .expect("get_public_key from parts"), - RadrootsNostrConnectRequest::GetPublicKey + Request::from_parts(Method::GetPublicKey, Vec::new(),).expect("get_public_key from parts"), + Request::GetPublicKey ); assert_eq!( - RadrootsNostrConnectRequest::from_parts(Method::GetSessionCapability, Vec::new(),) + Request::from_parts(Method::GetSessionCapability, Vec::new(),) .expect("get_session_capability from parts"), - RadrootsNostrConnectRequest::GetSessionCapability + Request::GetSessionCapability ); assert_eq!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Nip04Encrypt, vec![test_public_key().to_hex(), "hello".to_owned()], ) .expect("nip04 encrypt from parts"), - RadrootsNostrConnectRequest::Nip04Encrypt { + Request::Nip04Encrypt { public_key: test_public_key(), plaintext: "hello".to_owned(), } ); assert_eq!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Nip04Decrypt, vec![test_public_key().to_hex(), "cipher".to_owned()], ) .expect("nip04 decrypt from parts"), - RadrootsNostrConnectRequest::Nip04Decrypt { + Request::Nip04Decrypt { public_key: test_public_key(), ciphertext: "cipher".to_owned(), } ); assert_eq!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Nip44Encrypt, vec![test_public_key().to_hex(), "hello".to_owned()], ) .expect("nip44 encrypt from parts"), - RadrootsNostrConnectRequest::Nip44Encrypt { + Request::Nip44Encrypt { public_key: test_public_key(), plaintext: "hello".to_owned(), } ); assert_eq!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Nip44Decrypt, vec![test_public_key().to_hex(), "cipher".to_owned()], ) .expect("nip44 decrypt from parts"), - RadrootsNostrConnectRequest::Nip44Decrypt { + Request::Nip44Decrypt { public_key: test_public_key(), ciphertext: "cipher".to_owned(), } ); assert_eq!( - RadrootsNostrConnectRequest::from_parts(Method::Ping, Vec::new()).expect("ping from parts"), - RadrootsNostrConnectRequest::Ping + Request::from_parts(Method::Ping, Vec::new()).expect("ping from parts"), + Request::Ping ); assert_eq!( - RadrootsNostrConnectRequest::from_parts(Method::SwitchRelays, Vec::new(),) - .expect("switch relays from parts"), - RadrootsNostrConnectRequest::SwitchRelays + Request::from_parts(Method::SwitchRelays, Vec::new(),).expect("switch relays from parts"), + Request::SwitchRelays ); for (method, params, expected_error) in [ @@ -619,28 +603,28 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { (Method::Logout, vec!["oops".to_owned()], "no params"), ] { assert!(matches!( - RadrootsNostrConnectRequest::from_parts(method, params), - Err(RadrootsNostrConnectError::InvalidParams { expected, .. }) if expected == expected_error + Request::from_parts(method, params), + Err(Error::InvalidParams { expected, .. }) if expected == expected_error )); } assert!(matches!( - RadrootsNostrConnectRequest::from_parts(Method::Connect, Vec::new()), - Err(RadrootsNostrConnectError::InvalidParams { expected, received, .. }) + Request::from_parts(Method::Connect, Vec::new()), + Err(Error::InvalidParams { expected, received, .. }) if expected == "1 to 4 params" && received == 0 )); assert!(matches!( - RadrootsNostrConnectRequest::from_parts(Method::Connect, vec!["bad-key".to_owned()],), - Err(RadrootsNostrConnectError::InvalidPublicKey { .. }) + Request::from_parts(Method::Connect, vec!["bad-key".to_owned()],), + Err(Error::InvalidPublicKey { .. }) )); assert!(matches!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Connect, vec![test_public_key().to_hex(), "secret".to_owned(), "sign_event:".to_owned()], ), - Err(RadrootsNostrConnectError::InvalidPermission(value)) if value == "sign_event:" + Err(Error::InvalidPermission(value)) if value == "sign_event:" )); assert!(matches!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Connect, vec![ test_public_key().to_hex(), @@ -650,61 +634,61 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { "too-many".to_owned(), ], ), - Err(RadrootsNostrConnectError::InvalidParams { expected, received, .. }) + Err(Error::InvalidParams { expected, received, .. }) if expected == "1 to 4 params" && received == 5 )); assert!(matches!( - RadrootsNostrConnectRequest::from_parts(Method::SignEvent, vec!["not-json".to_owned()],), - Err(RadrootsNostrConnectError::InvalidRequestPayload { .. }) + Request::from_parts(Method::SignEvent, vec!["not-json".to_owned()],), + Err(Error::InvalidRequestPayload { .. }) )); assert!(matches!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Nip04Encrypt, vec!["bad-key".to_owned(), "hello".to_owned()], ), - Err(RadrootsNostrConnectError::InvalidPublicKey { .. }) + Err(Error::InvalidPublicKey { .. }) )); assert!(matches!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Nip04Decrypt, vec!["bad-key".to_owned(), "cipher".to_owned()], ), - Err(RadrootsNostrConnectError::InvalidPublicKey { .. }) + Err(Error::InvalidPublicKey { .. }) )); assert!(matches!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Nip44Encrypt, vec!["bad-key".to_owned(), "hello".to_owned()], ), - Err(RadrootsNostrConnectError::InvalidPublicKey { .. }) + Err(Error::InvalidPublicKey { .. }) )); assert!(matches!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Nip44Decrypt, vec!["bad-key".to_owned(), "cipher".to_owned()], ), - Err(RadrootsNostrConnectError::InvalidPublicKey { .. }) + Err(Error::InvalidPublicKey { .. }) )); - let custom_message = RadrootsNostrConnectRequestMessage::new( + let custom_message = RequestMessage::new( "req-custom", - RadrootsNostrConnectRequest::Custom { + Request::Custom { method: Method::custom("publish_note").expect("valid custom NIP-46 method"), params: vec!["a".to_owned()], }, ); let encoded = serde_json::to_string(&custom_message).expect("serialize custom request"); - let decoded: RadrootsNostrConnectRequestMessage = + let decoded: RequestMessage = serde_json::from_str(&encoded).expect("deserialize custom request"); assert_eq!(decoded, custom_message); assert!( - serde_json::from_str::<RadrootsNostrConnectRequestMessage>("{") + serde_json::from_str::<RequestMessage>("{") .expect_err("invalid request message json") .to_string() .contains("EOF") ); assert!( - serde_json::from_str::<RadrootsNostrConnectRequestMessage>( + serde_json::from_str::<RequestMessage>( "{\"id\":\"req\",\"method\":\"get_public_key\",\"params\":[\"oops\"]}", ) .expect_err("invalid request params") @@ -716,104 +700,88 @@ fn request_surface_covers_variant_methods_serialization_and_validation() { #[test] fn response_surface_covers_success_and_error_paths() { let event = signed_event(); - let remote_session_capability = - radroots_nostr_connect::prelude::RadrootsNostrConnectRemoteSessionCapability { - user_public_key: test_public_key(), - relays: vec![relay(RELAY_PRIMARY_WSS), relay(RELAY_SECONDARY_WSS)], - permissions: Permissions::from(vec![ - Permission::new(Method::Ping), - Permission::with_parameter(Method::SignEvent, "kind:1"), - ]), - }; + let remote_session_capability = RemoteSessionCapability { + user_public_key: test_public_key(), + relays: vec![relay(RELAY_PRIMARY_WSS), relay(RELAY_SECONDARY_WSS)], + permissions: Permissions::from(vec![ + Permission::new(Method::Ping), + Permission::with_parameter(Method::SignEvent, "kind:1"), + ]), + }; let cases = vec![ ( - RadrootsNostrConnectResponse::ConnectAcknowledged, + Response::ConnectAcknowledged, Method::Connect, - RadrootsNostrConnectResponse::ConnectAcknowledged, + Response::ConnectAcknowledged, ), ( - RadrootsNostrConnectResponse::ConnectSecretEcho("secret".to_owned()), + Response::ConnectSecretEcho("secret".to_owned()), Method::Connect, - RadrootsNostrConnectResponse::ConnectSecretEcho("secret".to_owned()), + Response::ConnectSecretEcho("secret".to_owned()), ), ( - RadrootsNostrConnectResponse::UserPublicKey(test_public_key()), + Response::UserPublicKey(test_public_key()), Method::GetPublicKey, - RadrootsNostrConnectResponse::UserPublicKey(test_public_key()), + Response::UserPublicKey(test_public_key()), ), ( - RadrootsNostrConnectResponse::PendingConnection, + Response::PendingConnection, Method::GetSessionCapability, - RadrootsNostrConnectResponse::PendingConnection, + Response::PendingConnection, ), ( - RadrootsNostrConnectResponse::RemoteSessionCapability( - remote_session_capability.clone(), - ), + Response::RemoteSessionCapability(remote_session_capability.clone()), Method::GetSessionCapability, - RadrootsNostrConnectResponse::RemoteSessionCapability( - remote_session_capability.clone(), - ), + Response::RemoteSessionCapability(remote_session_capability.clone()), ), ( - RadrootsNostrConnectResponse::SignedEvent( + Response::SignedEvent( ConnectSignedEvent::from_json(&event.as_json()).expect("signed event payload"), ), Method::SignEvent, - RadrootsNostrConnectResponse::SignedEvent( + Response::SignedEvent( ConnectSignedEvent::from_json(&event.as_json()).expect("signed event payload"), ), ), + (Response::Pong, Method::Ping, Response::Pong), ( - RadrootsNostrConnectResponse::Pong, - Method::Ping, - RadrootsNostrConnectResponse::Pong, - ), - ( - RadrootsNostrConnectResponse::Nip04Encrypt("cipher".to_owned()), + Response::Nip04Encrypt("cipher".to_owned()), Method::Nip04Encrypt, - RadrootsNostrConnectResponse::Nip04Encrypt("cipher".to_owned()), + Response::Nip04Encrypt("cipher".to_owned()), ), ( - RadrootsNostrConnectResponse::Nip04Decrypt("plain".to_owned()), + Response::Nip04Decrypt("plain".to_owned()), Method::Nip04Decrypt, - RadrootsNostrConnectResponse::Nip04Decrypt("plain".to_owned()), + Response::Nip04Decrypt("plain".to_owned()), ), ( - RadrootsNostrConnectResponse::Nip44Encrypt("cipher".to_owned()), + Response::Nip44Encrypt("cipher".to_owned()), Method::Nip44Encrypt, - RadrootsNostrConnectResponse::Nip44Encrypt("cipher".to_owned()), + Response::Nip44Encrypt("cipher".to_owned()), ), ( - RadrootsNostrConnectResponse::Nip44Decrypt("plain".to_owned()), + Response::Nip44Decrypt("plain".to_owned()), Method::Nip44Decrypt, - RadrootsNostrConnectResponse::Nip44Decrypt("plain".to_owned()), + Response::Nip44Decrypt("plain".to_owned()), ), ( - RadrootsNostrConnectResponse::RelayList(vec![ - relay(RELAY_SECONDARY_WSS), - relay(RELAY_TERTIARY_WSS), - ]), + Response::RelayList(vec![relay(RELAY_SECONDARY_WSS), relay(RELAY_TERTIARY_WSS)]), Method::SwitchRelays, - RadrootsNostrConnectResponse::RelayList(vec![ - relay(RELAY_SECONDARY_WSS), - relay(RELAY_TERTIARY_WSS), - ]), + Response::RelayList(vec![relay(RELAY_SECONDARY_WSS), relay(RELAY_TERTIARY_WSS)]), ), ( - RadrootsNostrConnectResponse::RelayListUnchanged, + Response::RelayListUnchanged, Method::SwitchRelays, - RadrootsNostrConnectResponse::RelayListUnchanged, + Response::RelayListUnchanged, ), ]; for (response, method, expected) in cases { let envelope = response.into_envelope("req").expect("serialize response"); - let parsed = - RadrootsNostrConnectResponse::from_envelope(&method, envelope).expect("parse response"); + let parsed = Response::from_envelope(&method, envelope).expect("parse response"); assert_eq!(parsed, expected); } - let error_envelope = RadrootsNostrConnectResponse::Error { + let error_envelope = Response::Error { result: Some(json!("partial")), error: "denied".to_owned(), } @@ -821,7 +789,7 @@ fn response_surface_covers_success_and_error_paths() { .expect("serialize error response"); assert_eq!(error_envelope.error.as_deref(), Some("denied")); - let custom_envelope = RadrootsNostrConnectResponse::Custom { + let custom_envelope = Response::Custom { result: Some(json!({"ok": true})), error: Some("warning".to_owned()), } @@ -829,116 +797,114 @@ fn response_surface_covers_success_and_error_paths() { .expect("serialize custom response"); assert_eq!(custom_envelope.error.as_deref(), Some("warning")); - let auth_envelope = - RadrootsNostrConnectResponse::AuthUrl("https://auth.example.com/challenge".to_owned()) - .into_envelope("req-auth") - .expect("serialize auth_url"); + let auth_envelope = Response::AuthUrl("https://auth.example.com/challenge".to_owned()) + .into_envelope("req-auth") + .expect("serialize auth_url"); assert_eq!( - RadrootsNostrConnectResponse::from_envelope(&Method::SignEvent, auth_envelope,) - .expect("parse auth_url"), - RadrootsNostrConnectResponse::AuthUrl("https://auth.example.com/challenge".to_owned()) + Response::from_envelope(&Method::SignEvent, auth_envelope,).expect("parse auth_url"), + Response::AuthUrl("https://auth.example.com/challenge".to_owned()) ); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::custom("publish_note").expect("valid custom NIP-46 method"), - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-custom".to_owned(), result: Some(json!("ok")), error: None, }, ) .expect("parse custom response without error"), - RadrootsNostrConnectResponse::Custom { + Response::Custom { result: Some(json!("ok")), error: None, } ); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::custom("publish_note").expect("valid custom NIP-46 method"), - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-custom".to_owned(), result: Some(json!({"ok": true})), error: Some("warning".to_owned()), }, ) .expect("parse custom response"), - RadrootsNostrConnectResponse::Custom { + Response::Custom { result: Some(json!({"ok": true})), error: Some("warning".to_owned()), } ); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::GetPublicKey, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-pending".to_owned(), result: None, - error: Some(RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR.to_owned()), + error: Some(PENDING_CONNECTION_ERROR.to_owned()), }, ) .expect("parse typed pending response"), - RadrootsNostrConnectResponse::PendingConnection + Response::PendingConnection ); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::GetSessionCapability, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-pending-capability".to_owned(), result: None, - error: Some(RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR.to_owned()), + error: Some(PENDING_CONNECTION_ERROR.to_owned()), }, ) .expect("parse typed pending capability response"), - RadrootsNostrConnectResponse::PendingConnection + Response::PendingConnection ); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::GetPublicKey, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-nonpending-public-key".to_owned(), result: None, error: Some("denied".to_owned()), }, ) .expect("parse non-pending public key error"), - RadrootsNostrConnectResponse::Error { + Response::Error { result: None, error: "denied".to_owned(), } ); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::GetSessionCapability, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-capability-error-with-result".to_owned(), result: Some(json!({"code": "retry"})), error: Some("denied".to_owned()), }, ) .expect("parse capability error with result"), - RadrootsNostrConnectResponse::Error { + Response::Error { result: Some(json!({"code": "retry"})), error: "denied".to_owned(), } ); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::GetSessionCapability, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-capability-invalid-result".to_owned(), result: Some(json!({"permissions": "ping"})), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidResponsePayload { method, .. }) + Err(Error::InvalidResponsePayload { method, .. }) if method == "get_session_capability" )); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::GetSessionCapability, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-capability-string-result".to_owned(), result: Some(json!( serde_json::to_string(&remote_session_capability) @@ -948,350 +914,346 @@ fn response_surface_covers_success_and_error_paths() { }, ) .expect("parse stringified capability result"), - RadrootsNostrConnectResponse::RemoteSessionCapability(remote_session_capability.clone(),) + Response::RemoteSessionCapability(remote_session_capability.clone(),) ); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::GetSessionCapability, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-capability-invalid-string".to_owned(), result: Some(json!("{")), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidResponsePayload { method, .. }) + Err(Error::InvalidResponsePayload { method, .. }) if method == "get_session_capability" )); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::Ping, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-error".to_owned(), result: Some(json!("partial")), error: Some("denied".to_owned()), }, ) .expect("parse error response"), - RadrootsNostrConnectResponse::Error { + Response::Error { result: Some(json!("partial")), error: "denied".to_owned(), } ); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SignEvent, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-event".to_owned(), result: Some(serde_json::to_value(&event).expect("event value")), error: None, }, ) .expect("parse object event"), - RadrootsNostrConnectResponse::SignedEvent( + Response::SignedEvent( ConnectSignedEvent::from_json(&event.as_json()).expect("signed event payload") ) ); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SwitchRelays, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-switch".to_owned(), result: Some(json!("null")), error: None, }, ) .expect("parse string null"), - RadrootsNostrConnectResponse::RelayListUnchanged + Response::RelayListUnchanged ); assert_eq!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SwitchRelays, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-switch".to_owned(), result: Some(json!(format!("[\"{RELAY_SECONDARY_WSS}\"]"))), error: None, }, ) .expect("parse stringified relay list"), - RadrootsNostrConnectResponse::RelayList(vec![relay(RELAY_SECONDARY_WSS)]) + Response::RelayList(vec![relay(RELAY_SECONDARY_WSS)]) ); assert!(matches!( - RadrootsNostrConnectResponse::AuthUrl("not-a-url".to_owned()).into_envelope("req"), - Err(RadrootsNostrConnectError::InvalidUrl { value, .. }) if value == "[redacted auth URL]" + Response::AuthUrl("not-a-url".to_owned()).into_envelope("req"), + Err(Error::InvalidUrl { value, .. }) if value == "[redacted auth URL]" )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SignEvent, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-auth".to_owned(), result: Some(json!("auth_url")), error: Some("not-a-url".to_owned()), }, ), - Err(RadrootsNostrConnectError::InvalidUrl { value, .. }) if value == "[redacted auth URL]" + Err(Error::InvalidUrl { value, .. }) if value == "[redacted auth URL]" )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::GetPublicKey, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-key".to_owned(), result: Some(json!("bad-key")), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidPublicKey { .. }) + Err(Error::InvalidPublicKey { .. }) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::Connect, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-connect".to_owned(), result: None, error: None, }, ), - Err(RadrootsNostrConnectError::MissingResult) + Err(Error::MissingResult) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::GetPublicKey, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-key".to_owned(), result: None, error: None, }, ), - Err(RadrootsNostrConnectError::MissingResult) + Err(Error::MissingResult) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::Ping, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-ping".to_owned(), result: Some(json!("nope")), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidResponsePayload { .. }) + Err(Error::InvalidResponsePayload { .. }) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::Ping, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-ping".to_owned(), result: None, error: None, }, ), - Err(RadrootsNostrConnectError::MissingResult) + Err(Error::MissingResult) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::Nip04Encrypt, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-nip04".to_owned(), result: Some(json!(5)), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidResponsePayload { .. }) + Err(Error::InvalidResponsePayload { .. }) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::Nip04Encrypt, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-nip04".to_owned(), result: None, error: None, }, ), - Err(RadrootsNostrConnectError::MissingResult) + Err(Error::MissingResult) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SignEvent, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-event".to_owned(), result: Some(json!("not-json")), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidResponsePayload { .. }) + Err(Error::InvalidResponsePayload { .. }) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SignEvent, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-event".to_owned(), result: Some(json!(5)), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidResponsePayload { .. }) + Err(Error::InvalidResponsePayload { .. }) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SignEvent, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-event".to_owned(), result: None, error: None, }, ), - Err(RadrootsNostrConnectError::MissingResult) + Err(Error::MissingResult) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::Nip04Decrypt, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-nip04d".to_owned(), result: None, error: None, }, ), - Err(RadrootsNostrConnectError::MissingResult) + Err(Error::MissingResult) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::Nip44Encrypt, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-nip44e".to_owned(), result: None, error: None, }, ), - Err(RadrootsNostrConnectError::MissingResult) + Err(Error::MissingResult) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::Nip44Decrypt, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-nip44d".to_owned(), result: None, error: None, }, ), - Err(RadrootsNostrConnectError::MissingResult) + Err(Error::MissingResult) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SwitchRelays, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-switch".to_owned(), result: Some(json!("[invalid")), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidResponsePayload { .. }) + Err(Error::InvalidResponsePayload { .. }) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SwitchRelays, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-switch".to_owned(), result: Some(json!([1])), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidResponsePayload { .. }) + Err(Error::InvalidResponsePayload { .. }) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SwitchRelays, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-switch".to_owned(), result: Some(json!(["http://relay.example.com"])), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidRelayUrl { .. }) + Err(Error::InvalidRelayUrl { .. }) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::SwitchRelays, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-switch".to_owned(), result: Some(json!(5)), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidResponsePayload { .. }) + Err(Error::InvalidResponsePayload { .. }) )); assert!(matches!( - RadrootsNostrConnectResponse::from_envelope( + Response::from_envelope( &Method::Logout, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-logout".to_owned(), result: Some(json!("not-ack")), error: None, }, ), - Err(RadrootsNostrConnectError::InvalidResponsePayload { method, .. }) + Err(Error::InvalidResponsePayload { method, .. }) if method == "logout" )); } #[test] fn pending_connection_poll_outcome_uses_typed_variants() { - let remote_session_capability = - radroots_nostr_connect::prelude::RadrootsNostrConnectRemoteSessionCapability { - user_public_key: test_public_key(), - relays: vec![relay(RELAY_PRIMARY_WSS), relay(RELAY_SECONDARY_WSS)], - permissions: Permissions::from(vec![ - Permission::new(Method::Ping), - Permission::with_parameter(Method::SignEvent, "kind:1"), - ]), - }; + let remote_session_capability = RemoteSessionCapability { + user_public_key: test_public_key(), + relays: vec![relay(RELAY_PRIMARY_WSS), relay(RELAY_SECONDARY_WSS)], + permissions: Permissions::from(vec![ + Permission::new(Method::Ping), + Permission::with_parameter(Method::SignEvent, "kind:1"), + ]), + }; assert_eq!( - RadrootsNostrConnectResponse::PendingConnection.into_pending_connection_poll_outcome(), - RadrootsNostrConnectPendingConnectionPollOutcome::PendingApproval + Response::PendingConnection.into_pending_connection_poll_outcome(), + PendingConnectionOutcome::PendingApproval ); assert_eq!( - RadrootsNostrConnectResponse::UserPublicKey(test_public_key()) - .into_pending_connection_poll_outcome(), - RadrootsNostrConnectPendingConnectionPollOutcome::Approved(test_public_key()) + Response::UserPublicKey(test_public_key()).into_pending_connection_poll_outcome(), + PendingConnectionOutcome::Approved(test_public_key()) ); assert_eq!( - RadrootsNostrConnectResponse::RemoteSessionCapability(remote_session_capability.clone()) + Response::RemoteSessionCapability(remote_session_capability.clone()) .into_pending_connection_poll_outcome(), - RadrootsNostrConnectPendingConnectionPollOutcome::ApprovedCapability( - remote_session_capability - ) + PendingConnectionOutcome::ApprovedCapability(remote_session_capability) ); assert_eq!( - RadrootsNostrConnectResponse::Error { + Response::Error { result: Some(json!("partial")), error: "rejected".to_owned(), } .into_pending_connection_poll_outcome(), - RadrootsNostrConnectPendingConnectionPollOutcome::Rejected { + PendingConnectionOutcome::Rejected { message: "rejected".to_owned(), } ); assert_eq!( - RadrootsNostrConnectResponse::Error { + Response::Error { result: None, - error: RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR.to_owned(), + error: PENDING_CONNECTION_ERROR.to_owned(), } .into_pending_connection_poll_outcome(), - RadrootsNostrConnectPendingConnectionPollOutcome::PendingApproval + PendingConnectionOutcome::PendingApproval ); assert_eq!( - RadrootsNostrConnectResponse::AuthUrl("https://auth.example.com/challenge".to_owned()) + Response::AuthUrl("https://auth.example.com/challenge".to_owned()) .into_pending_connection_poll_outcome(), - RadrootsNostrConnectPendingConnectionPollOutcome::AuthChallenge { + PendingConnectionOutcome::AuthChallenge { url: "https://auth.example.com/challenge".to_owned(), } ); assert!(matches!( - RadrootsNostrConnectResponse::Pong.into_pending_connection_poll_outcome(), - RadrootsNostrConnectPendingConnectionPollOutcome::UnexpectedResponse { response } + Response::Pong.into_pending_connection_poll_outcome(), + PendingConnectionOutcome::UnexpectedResponse { response } if response == "pong" )); } diff --git a/crates/nostr_connect/tests/package_boundary.rs b/crates/nostr_connect/tests/package_boundary.rs @@ -98,7 +98,7 @@ fn manifest_has_final_identity_feature_vocabulary_and_radroots_dependencies() { #[test] fn crate_root_contains_the_approved_module_skeleton() { let final_root = ROOT - .split("// Transitional compatibility surface") + .split("/// Private migration surface") .next() .expect("final root declarations"); assert_eq!( @@ -133,10 +133,8 @@ fn crate_root_contains_the_approved_module_skeleton() { assert!(SERVER.starts_with("//! Relay- and persistence-independent NIP-46 server state.")); assert!(ROOT.contains("pub use server::Server;")); assert!(ROOT.contains("#[doc(hidden)]\npub mod prelude")); - assert!( - ROOT.contains("Step 143 removes this module"), - "the temporary prelude must carry an exact removal checkpoint" - ); + assert!(ROOT.contains("Steps 271, 288, and 293")); + assert!(ROOT.contains("Step 313 removes the shim")); } #[test] @@ -167,7 +165,7 @@ fn approved_root_exports_and_transport_trait_compile() { "pub trait RadrootsNostrConnectClientTransport {", "pub trait Transport: Send {", ]), - "only the final host transport SPI and Step 143 compatibility trait may remain" + "only the final host transport SPI and private migration shim may remain" ); for forbidden in [ "Relay", "Runtime", "Session", "Storage", "Secret", "Approval", @@ -235,6 +233,39 @@ fn client_root_and_transport_use_package_owned_state_machine_types() { } #[test] +fn separate_repository_compatibility_is_hidden_unpublished_and_scheduled() { + assert!(MANIFEST.contains("publish = false")); + for item in [ + "pub type RadrootsNostrConnectClientTransportFuture", + "pub struct RadrootsNostrConnectClientTarget", + "pub struct RadrootsNostrConnectClientRequest", + "pub enum RadrootsNostrConnectClientProgress", + "pub enum RadrootsNostrConnectClientEventOutcome", + "pub trait RadrootsNostrConnectClientTransport", + "pub fn build_request_event", + "pub fn parse_response_event", + "pub async fn execute_request_with_transport", + ] { + let position = CLIENT + .find(item) + .unwrap_or_else(|| panic!("missing `{item}`")); + assert!( + CLIENT[position.saturating_sub(100)..position].contains("#[doc(hidden)]"), + "compatibility item `{item}` must remain hidden" + ); + } + for legacy in [ + "RadrootsNostrConnectClient", + "radroots_nostr_connect::prelude", + ] { + assert!( + !PUBLIC_API.contains(legacy), + "compatibility identity `{legacy}` leaked into the public API baseline" + ); + } +} + +#[test] fn protocol_transport_boundary_has_no_relay_pool_or_runtime_owner() { for forbidden in [ "nostr-sdk", diff --git a/crates/nostr_connect/tests/protocol.rs b/crates/nostr_connect/tests/protocol.rs @@ -2,14 +2,16 @@ mod test_fixtures; use nostr::{EventBuilder, JsonUtil, Keys, PublicKey, SecretKey, Timestamp, UnsignedEvent}; -use radroots_nostr_connect::prelude::{ - CLIENT_METADATA_JSON_MAX_BYTES, CLIENT_NAME_MAX_BYTES, ClientMetadata, Method, Permission, - Permissions, RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR, RadrootsNostrConnectError, - RadrootsNostrConnectRequest, RadrootsNostrConnectRequestMessage, RadrootsNostrConnectResponse, - RadrootsNostrConnectResponseEnvelope, SignedEvent as ConnectSignedEvent, - UnsignedEvent as ConnectUnsignedEvent, Uri, +use radroots_nostr_connect::message::{ + PENDING_CONNECTION_ERROR, RemoteSessionCapability, RequestMessage, ResponseEnvelope, + SignedEvent as ConnectSignedEvent, UnsignedEvent as ConnectUnsignedEvent, }; -use radroots_nostr_connect::uri::RelayUrl as ConnectRelayUrl; +use radroots_nostr_connect::permission::Permissions; +use radroots_nostr_connect::uri::{ + CLIENT_METADATA_JSON_MAX_BYTES, CLIENT_NAME_MAX_BYTES, ClientMetadata, + RelayUrl as ConnectRelayUrl, Uri, +}; +use radroots_nostr_connect::{Error, Method, Permission, Request, Response}; use serde_json::{Value, json}; use test_fixtures::{ APP_PRIMARY_HTTPS, CDN_PRIMARY_HTTPS, FIXTURE_ALICE, RELAY_PRIMARY_WSS, RELAY_SECONDARY_WSS, @@ -38,9 +40,8 @@ fn logo_url() -> String { format!("{CDN_PRIMARY_HTTPS}/logo.png") } -fn remote_session_capability() --> radroots_nostr_connect::prelude::RadrootsNostrConnectRemoteSessionCapability { - radroots_nostr_connect::prelude::RadrootsNostrConnectRemoteSessionCapability { +fn remote_session_capability() -> RemoteSessionCapability { + RemoteSessionCapability { user_public_key: test_identity_public_key(), relays: vec![ ConnectRelayUrl::parse(RELAY_PRIMARY_WSS).expect("relay 1"), @@ -129,7 +130,7 @@ fn requested_permissions_roundtrip_as_csv() { #[test] fn connect_request_roundtrips_requested_permissions() { - let request = RadrootsNostrConnectRequest::Connect { + let request = Request::Connect { remote_signer_public_key: test_identity_public_key(), secret: Some("abcd".to_owned()), requested_permissions: Permissions::from(vec![ @@ -138,7 +139,7 @@ fn connect_request_roundtrips_requested_permissions() { ]), client_metadata: None, }; - let message = RadrootsNostrConnectRequestMessage::new("req-1", request); + let message = RequestMessage::new("req-1", request); let encoded = serde_json::to_value(&message).expect("serialize request"); assert_eq!( encoded, @@ -153,14 +154,13 @@ fn connect_request_roundtrips_requested_permissions() { }) ); - let decoded: RadrootsNostrConnectRequestMessage = - serde_json::from_value(encoded).expect("deserialize request"); + let decoded: RequestMessage = serde_json::from_value(encoded).expect("deserialize request"); assert_eq!(decoded, message); } #[test] fn connect_request_roundtrips_client_metadata_in_fourth_parameter() { - let request = RadrootsNostrConnectRequest::Connect { + let request = Request::Connect { remote_signer_public_key: test_identity_public_key(), secret: None, requested_permissions: Permissions::default(), @@ -171,7 +171,7 @@ fn connect_request_roundtrips_client_metadata_in_fourth_parameter() { image: Some(logo_url()), }), }; - let message = RadrootsNostrConnectRequestMessage::new("req-metadata", request); + let message = RequestMessage::new("req-metadata", request); let encoded = serde_json::to_value(&message).expect("serialize metadata request"); assert_eq!(encoded["params"][1], ""); assert_eq!(encoded["params"][2], ""); @@ -190,10 +190,10 @@ fn connect_request_roundtrips_client_metadata_in_fourth_parameter() { }) ); - let decoded: RadrootsNostrConnectRequestMessage = + let decoded: RequestMessage = serde_json::from_value(encoded.clone()).expect("deserialize metadata request"); match &decoded.request { - RadrootsNostrConnectRequest::Connect { + Request::Connect { client_metadata: Some(metadata), .. } => { @@ -210,23 +210,22 @@ fn connect_request_roundtrips_client_metadata_in_fourth_parameter() { #[test] fn logout_request_and_acknowledgement_roundtrip() { - let message = - RadrootsNostrConnectRequestMessage::new("req-logout", RadrootsNostrConnectRequest::Logout); + let message = RequestMessage::new("req-logout", Request::Logout); assert_eq!( serde_json::to_value(&message).expect("serialize logout"), json!({"id": "req-logout", "method": "logout", "params": []}) ); - let response = RadrootsNostrConnectResponse::from_envelope( + let response = Response::from_envelope( &Method::Logout, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-logout".to_owned(), result: Some(Value::String("ack".to_owned())), error: None, }, ) .expect("parse logout acknowledgement"); - assert_eq!(response, RadrootsNostrConnectResponse::LogoutAcknowledged); + assert_eq!(response, Response::LogoutAcknowledged); assert_eq!( response .into_envelope("req-logout") @@ -248,7 +247,7 @@ fn rejects_invalid_client_metadata() { serde_json::to_string(&json!({"name": "line\nbreak"})).expect("metadata") ] }); - assert!(serde_json::from_value::<RadrootsNostrConnectRequestMessage>(invalid_name).is_err()); + assert!(serde_json::from_value::<RequestMessage>(invalid_name).is_err()); let invalid_scheme = format!( "nostrconnect://{}?relay={}&secret=secret&url={}", @@ -266,12 +265,12 @@ fn rejects_invalid_client_metadata() { }; assert!(matches!( oversized_name.to_connect_param(), - Err(RadrootsNostrConnectError::InvalidClientMetadata { field: "name", .. }) + Err(Error::InvalidClientMetadata { field: "name", .. }) )); let oversized_payload = "x".repeat(CLIENT_METADATA_JSON_MAX_BYTES + 1); assert!(matches!( - RadrootsNostrConnectRequest::from_parts( + Request::from_parts( Method::Connect, vec![ test_public_key().to_hex(), @@ -280,7 +279,7 @@ fn rejects_invalid_client_metadata() { oversized_payload, ], ), - Err(RadrootsNostrConnectError::ClientMetadataTooLarge { .. }) + Err(Error::ClientMetadataTooLarge { .. }) )); } @@ -295,9 +294,9 @@ fn sign_event_request_roundtrips_unsigned_event_payload() { })) .expect("unsigned event"); - let message = RadrootsNostrConnectRequestMessage::new( + let message = RequestMessage::new( "req-sign", - RadrootsNostrConnectRequest::SignEvent( + Request::SignEvent( ConnectUnsignedEvent::from_json(&unsigned_event.as_json()) .expect("unsigned event payload"), ), @@ -305,12 +304,12 @@ fn sign_event_request_roundtrips_unsigned_event_payload() { let encoded = serde_json::to_value(&message).expect("serialize sign request"); assert_eq!(encoded["method"], "sign_event"); - let decoded: RadrootsNostrConnectRequestMessage = + let decoded: RequestMessage = serde_json::from_value(encoded).expect("deserialize sign request"); assert_eq!(decoded, message); assert_eq!( decoded.request, - RadrootsNostrConnectRequest::SignEvent( + Request::SignEvent( ConnectUnsignedEvent::from_json(&unsigned_event.as_json()) .expect("unsigned event payload"), ) @@ -319,66 +318,59 @@ fn sign_event_request_roundtrips_unsigned_event_payload() { #[test] fn switch_relays_response_accepts_array_or_null() { - let relays_response = RadrootsNostrConnectResponseEnvelope { + let relays_response = ResponseEnvelope { id: "req-switch".to_owned(), result: Some(json!([RELAY_SECONDARY_WSS, RELAY_TERTIARY_WSS])), error: None, }; let parsed = - RadrootsNostrConnectResponse::from_envelope(&Method::SwitchRelays, relays_response) - .expect("parse relay list"); + Response::from_envelope(&Method::SwitchRelays, relays_response).expect("parse relay list"); assert_eq!( parsed, - RadrootsNostrConnectResponse::RelayList(vec![ + Response::RelayList(vec![ ConnectRelayUrl::parse(RELAY_SECONDARY_WSS).expect("relay 1"), ConnectRelayUrl::parse(RELAY_TERTIARY_WSS).expect("relay 2"), ]) ); - let unchanged = RadrootsNostrConnectResponse::from_envelope( + let unchanged = Response::from_envelope( &Method::SwitchRelays, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-switch".to_owned(), result: Some(Value::Null), error: None, }, ) .expect("parse null relay result"); - assert_eq!(unchanged, RadrootsNostrConnectResponse::RelayListUnchanged); + assert_eq!(unchanged, Response::RelayListUnchanged); } #[test] fn get_session_capability_request_and_response_roundtrip() { - let request_message = RadrootsNostrConnectRequestMessage::new( - "req-cap", - RadrootsNostrConnectRequest::GetSessionCapability, - ); + let request_message = RequestMessage::new("req-cap", Request::GetSessionCapability); let encoded_request = serde_json::to_value(&request_message).expect("serialize request"); - let decoded_request: RadrootsNostrConnectRequestMessage = + let decoded_request: RequestMessage = serde_json::from_value(encoded_request).expect("deserialize request"); assert_eq!(decoded_request, request_message); let capability = remote_session_capability(); - let response_envelope = - RadrootsNostrConnectResponse::RemoteSessionCapability(capability.clone()) - .into_envelope("resp-cap") - .expect("serialize response"); - let decoded_response = RadrootsNostrConnectResponse::from_envelope( - &Method::GetSessionCapability, - response_envelope, - ) - .expect("deserialize response"); + let response_envelope = Response::RemoteSessionCapability(capability.clone()) + .into_envelope("resp-cap") + .expect("serialize response"); + let decoded_response = + Response::from_envelope(&Method::GetSessionCapability, response_envelope) + .expect("deserialize response"); assert_eq!( decoded_response, - RadrootsNostrConnectResponse::RemoteSessionCapability(capability) + Response::RemoteSessionCapability(capability) ); } #[test] fn auth_url_response_parses_from_result_and_error_fields() { - let response = RadrootsNostrConnectResponse::from_envelope( + let response = Response::from_envelope( &Method::SignEvent, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-auth".to_owned(), result: Some(json!("auth_url")), error: Some("https://auth.example.com/challenge".to_owned()), @@ -388,23 +380,23 @@ fn auth_url_response_parses_from_result_and_error_fields() { assert_eq!( response, - RadrootsNostrConnectResponse::AuthUrl("https://auth.example.com/challenge".to_owned()) + Response::AuthUrl("https://auth.example.com/challenge".to_owned()) ); } #[test] fn get_public_key_pending_response_parses_as_typed_pending_connection() { - let response = RadrootsNostrConnectResponse::from_envelope( + let response = Response::from_envelope( &Method::GetPublicKey, - RadrootsNostrConnectResponseEnvelope { + ResponseEnvelope { id: "req-pending".to_owned(), result: None, - error: Some(RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR.to_owned()), + error: Some(PENDING_CONNECTION_ERROR.to_owned()), }, ) .expect("parse pending get_public_key response"); - assert_eq!(response, RadrootsNostrConnectResponse::PendingConnection); + assert_eq!(response, Response::PendingConnection); } #[test] @@ -415,17 +407,17 @@ fn sign_event_response_roundtrips_signed_event_json_string() { .sign_with_keys(&keys) .expect("sign event"); - let envelope = RadrootsNostrConnectResponse::SignedEvent( + let envelope = Response::SignedEvent( ConnectSignedEvent::from_json(&event.as_json()).expect("signed event payload"), ) .into_envelope("req-sign") .expect("serialize response"); - let parsed = RadrootsNostrConnectResponse::from_envelope(&Method::SignEvent, envelope) - .expect("parse signed event response"); + let parsed = + Response::from_envelope(&Method::SignEvent, envelope).expect("parse signed event response"); assert_eq!( parsed, - RadrootsNostrConnectResponse::SignedEvent( + Response::SignedEvent( ConnectSignedEvent::from_json(&event.as_json()).expect("signed event payload") ) ); @@ -448,18 +440,15 @@ fn checked_in_current_session_vectors_match_protocol_behavior() { match kind { "nip46.request.valid" => { - let message: RadrootsNostrConnectRequestMessage = - serde_json::from_value(input["message"].clone()) - .unwrap_or_else(|error| panic!("{id}: parse request: {error}")); + let message: RequestMessage = serde_json::from_value(input["message"].clone()) + .unwrap_or_else(|error| panic!("{id}: parse request: {error}")); let normalized = serde_json::to_value(message) .unwrap_or_else(|error| panic!("{id}: serialize request: {error}")); assert_eq!(normalized, expected["normalized_message"], "{id}"); } "nip46.request.invalid" => { - let error = serde_json::from_value::<RadrootsNostrConnectRequestMessage>( - input["message"].clone(), - ) - .expect_err("invalid request vector"); + let error = serde_json::from_value::<RequestMessage>(input["message"].clone()) + .expect_err("invalid request vector"); assert_vector_error(id, expected, error); } "nip46.metadata.invalid" => { @@ -489,11 +478,10 @@ fn checked_in_current_session_vectors_match_protocol_behavior() { .expect("response method") .parse::<Method>() .expect("typed response method"); - let envelope: RadrootsNostrConnectResponseEnvelope = - serde_json::from_value(input["envelope"].clone()) - .unwrap_or_else(|error| panic!("{id}: parse envelope: {error}")); + let envelope: ResponseEnvelope = serde_json::from_value(input["envelope"].clone()) + .unwrap_or_else(|error| panic!("{id}: parse envelope: {error}")); let request_id = envelope.id.clone(); - let response = RadrootsNostrConnectResponse::from_envelope(&method, envelope) + let response = Response::from_envelope(&method, envelope) .unwrap_or_else(|error| panic!("{id}: parse response: {error}")); let normalized = response .into_envelope(request_id) @@ -508,10 +496,9 @@ fn checked_in_current_session_vectors_match_protocol_behavior() { .expect("response method") .parse::<Method>() .expect("typed response method"); - let envelope: RadrootsNostrConnectResponseEnvelope = - serde_json::from_value(input["envelope"].clone()) - .unwrap_or_else(|error| panic!("{id}: parse envelope: {error}")); - let error = RadrootsNostrConnectResponse::from_envelope(&method, envelope) + let envelope: ResponseEnvelope = serde_json::from_value(input["envelope"].clone()) + .unwrap_or_else(|error| panic!("{id}: parse envelope: {error}")); + let error = Response::from_envelope(&method, envelope) .expect_err("invalid response vector"); assert_vector_error(id, expected, error); } diff --git a/docs/api/radroots_nostr_connect.txt b/docs/api/radroots_nostr_connect.txt @@ -17,13 +17,6 @@ pub radroots_nostr_connect::client::Progress::AuthChallenge pub radroots_nostr_connect::client::Progress::AuthChallenge::url: alloc::string::String impl core::fmt::Debug for radroots_nostr_connect::client::Progress pub fn radroots_nostr_connect::client::Progress::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -pub enum radroots_nostr_connect::client::RadrootsNostrConnectClientEventOutcome -pub radroots_nostr_connect::client::RadrootsNostrConnectClientEventOutcome::Ignore -pub radroots_nostr_connect::client::RadrootsNostrConnectClientEventOutcome::Progress(radroots_nostr_connect::client::RadrootsNostrConnectClientProgress) -pub radroots_nostr_connect::client::RadrootsNostrConnectClientEventOutcome::Response(radroots_nostr_connect::message::Response) -pub enum radroots_nostr_connect::client::RadrootsNostrConnectClientProgress -pub radroots_nostr_connect::client::RadrootsNostrConnectClientProgress::AuthChallenge -pub radroots_nostr_connect::client::RadrootsNostrConnectClientProgress::AuthChallenge::url: alloc::string::String pub enum radroots_nostr_connect::client::Receive pub radroots_nostr_connect::client::Receive::Cancelled pub radroots_nostr_connect::client::Receive::Event(alloc::boxed::Box<radroots_nostr_connect::client::ClientEvent>) @@ -59,34 +52,15 @@ pub fn radroots_nostr_connect::client::Operation<'_>::publication(&self) -> core pub fn radroots_nostr_connect::client::Operation<'_>::select(&mut self, &radroots_nostr_connect::client::ClientEvent) -> core::result::Result<radroots_nostr_connect::client::EventOutcome, radroots_nostr_connect::error::RadrootsNostrConnectError> impl core::fmt::Debug for radroots_nostr_connect::client::Operation<'_> pub fn radroots_nostr_connect::client::Operation<'_>::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -pub struct radroots_nostr_connect::client::RadrootsNostrConnectClientRequest -pub radroots_nostr_connect::client::RadrootsNostrConnectClientRequest::request: radroots_nostr_connect::message::Request -pub radroots_nostr_connect::client::RadrootsNostrConnectClientRequest::request_id: alloc::string::String -impl radroots_nostr_connect::client::RadrootsNostrConnectClientRequest -pub fn radroots_nostr_connect::client::RadrootsNostrConnectClientRequest::into_message(self) -> radroots_nostr_connect::message::RequestMessage -pub fn radroots_nostr_connect::client::RadrootsNostrConnectClientRequest::method(&self) -> radroots_nostr_connect::method::Method -pub fn radroots_nostr_connect::client::RadrootsNostrConnectClientRequest::new(impl core::convert::Into<alloc::string::String>, radroots_nostr_connect::message::Request) -> Self -pub struct radroots_nostr_connect::client::RadrootsNostrConnectClientTarget -pub radroots_nostr_connect::client::RadrootsNostrConnectClientTarget::relays: alloc::vec::Vec<nostr::types::url::RelayUrl> -pub radroots_nostr_connect::client::RadrootsNostrConnectClientTarget::remote_signer_public_key: nostr::key::public_key::PublicKey -impl radroots_nostr_connect::client::RadrootsNostrConnectClientTarget -pub fn radroots_nostr_connect::client::RadrootsNostrConnectClientTarget::new(nostr::key::public_key::PublicKey, alloc::vec::Vec<nostr::types::url::RelayUrl>) -> Self pub struct radroots_nostr_connect::client::Target impl radroots_nostr_connect::client::Target pub fn radroots_nostr_connect::client::Target::relays(&self) -> &[radroots_nostr_connect::uri::RelayUrl] pub const fn radroots_nostr_connect::client::Target::remote_signer_public_key(&self) -> radroots_identity::key::PublicKey pub fn radroots_nostr_connect::client::Target::try_new(radroots_identity::key::PublicKey, alloc::vec::Vec<radroots_nostr_connect::uri::RelayUrl>) -> core::result::Result<Self, radroots_nostr_connect::error::RadrootsNostrConnectError> pub const radroots_nostr_connect::client::CLIENT_EVENT_MAX_BYTES: usize -pub trait radroots_nostr_connect::client::RadrootsNostrConnectClientTransport -pub fn radroots_nostr_connect::client::RadrootsNostrConnectClientTransport::next_response_event<'a>(&'a mut self) -> radroots_nostr_connect::client::RadrootsNostrConnectClientTransportFuture<'a, nostr::event::Event> -pub fn radroots_nostr_connect::client::RadrootsNostrConnectClientTransport::publish_request_event<'a>(&'a mut self, nostr::event::Event) -> radroots_nostr_connect::client::RadrootsNostrConnectClientTransportFuture<'a, ()> pub trait radroots_nostr_connect::client::Transport: core::marker::Send pub fn radroots_nostr_connect::client::Transport::publish<'a>(&'a mut self, radroots_nostr_connect::client::ClientEvent) -> radroots_nostr_connect::client::TransportFuture<'a, ()> pub fn radroots_nostr_connect::client::Transport::receive<'a>(&'a mut self, &'a radroots_nostr_connect::client::CancellationToken) -> radroots_nostr_connect::client::TransportFuture<'a, radroots_nostr_connect::client::Receive> -pub fn radroots_nostr_connect::client::build_request_event(&nostr::key::Keys, &radroots_nostr_connect::client::RadrootsNostrConnectClientTarget, radroots_nostr_connect::message::RequestMessage) -> core::result::Result<nostr::event::Event, radroots_nostr_connect::error::RadrootsNostrConnectError> -pub async fn radroots_nostr_connect::client::execute_request_with_transport<T, F>(&nostr::key::Keys, &radroots_nostr_connect::client::RadrootsNostrConnectClientTarget, radroots_nostr_connect::client::RadrootsNostrConnectClientRequest, &mut T, F) -> core::result::Result<radroots_nostr_connect::message::Response, radroots_nostr_connect::error::RadrootsNostrConnectError> where T: radroots_nostr_connect::client::RadrootsNostrConnectClientTransport, F: core::ops::function::FnMut(radroots_nostr_connect::client::RadrootsNostrConnectClientProgress) -> core::result::Result<(), radroots_nostr_connect::error::RadrootsNostrConnectError> -pub fn radroots_nostr_connect::client::parse_response_event(&nostr::key::Keys, &radroots_nostr_connect::client::RadrootsNostrConnectClientTarget, &str, &radroots_nostr_connect::method::Method, &nostr::event::Event) -> core::result::Result<radroots_nostr_connect::client::RadrootsNostrConnectClientEventOutcome, radroots_nostr_connect::error::RadrootsNostrConnectError> -pub type radroots_nostr_connect::client::RadrootsNostrConnectClientTransportFuture<'a, T> = core::pin::Pin<alloc::boxed::Box<(dyn core::future::future::Future<Output = core::result::Result<T, radroots_nostr_connect::error::RadrootsNostrConnectError>> + core::marker::Send + 'a)>> pub type radroots_nostr_connect::client::TransportFuture<'a, T> = core::pin::Pin<alloc::boxed::Box<(dyn core::future::future::Future<Output = core::result::Result<T, radroots_nostr_connect::error::RadrootsNostrConnectError>> + core::marker::Send + 'a)>> pub mod radroots_nostr_connect::error pub enum radroots_nostr_connect::error::RadrootsNostrConnectError diff --git a/docs/implementation/COMPATIBILITY_SHIMS.md b/docs/implementation/COMPATIBILITY_SHIMS.md @@ -9,9 +9,11 @@ public crate identity or a second contract authority. | --- | --- | --- | --- | --- | | `radroots_authority` | `radroots_signing` | `oss/cli`, `oss/studio_app` | downstream Steps 269-293; matrix Step 294 | Step 313 | | `radroots_nostr_signer` | `radroots_signing`, `radroots_nostr_connect`, Myc-private state | `radroots_net`, `radroots_nostr_accounts`, `oss/sdk`, `oss/myc`, `oss/cli` | crate Steps 109-143; SDK Step 248; downstream Steps 269-293; matrix Step 294 | Step 313 | +| hidden `radroots_nostr_connect::prelude` and prefixed client bridge | final `radroots_nostr_connect` modules and client state machine | `oss/cli`, `oss/myc`, enterprise NIP-46 adapters, integration harnesses | CLI Step 271; Myc Step 288; residual consumers Step 293; matrix Step 294 | Step 313 | -Both package manifests keep `publish = false`. Release policy classifies both -as private and excludes both from the exact 19-package public inventory. The +Both compatibility package manifests and `radroots_nostr_connect` keep +`publish = false`. Release policy classifies the shims as private and excludes +them from the exact 19-package public API inventory. The `radroots_nostr_signer` manifest intentionally has no docs.rs URL. No new consumer, feature, public contract, or behavior may be added before removal.