Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ docker-compose up -d
- `rivet-envoy-client` transport features are mutually exclusive; native builds use the default `native-transport`, while wasm builds must set `default-features = false` and enable `wasm-transport`.
- `rivet-envoy-client` wasm WebSocket code lives behind `target_arch = "wasm32"` with a native-host `wasm-transport` stub so feature checks do not compile browser APIs on developer machines.
- `rivetkit-core` wasm builds use `--no-default-features --features wasm-runtime,sqlite-remote`; keep native process and runner-config HTTP code behind `native-runtime`.
- Do not treat wasm feature flags alone as browser builds; pair wasm runtime/transport behavior with the `wasm32-unknown-unknown` target, and keep host feature checks native-safe.
- Core-owned lifecycle tasks in `rivetkit-core` should spawn through `RuntimeSpawner` so native builds use Send-capable tasks and wasm builds use local tasks.
- `rivet-envoy-client::async_counter::AsyncCounter` is the shared HTTP request counter type consumed by core sleep logic; do not pull `rivet-util` into core for that counter.
- For `wasm32-unknown-unknown` Rust checks, use target-specific minimal Tokio plus `getrandom/js` and `uuid/js`; scan production dependencies with `cargo tree -e normal` so dev-dependencies do not create false native-dependency hits.
Expand Down
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ members = [
"rivetkit-typescript/packages/rivetkit-napi",
"rivetkit-typescript/packages/rivetkit-wasm"
]
# Keep default root checks on the native workspace graph. `rivetkit-wasm`
# enables wasm runtime features that conflict with NAPI's native runtime
# features when Cargo unifies the full workspace feature graph.
default-members = [
"engine/packages/*",
"engine/sdks/rust/*",
"rivetkit-rust/packages/actor-persist",
"rivetkit-rust/packages/rivetkit-core",
"rivetkit-rust/packages/shared-types",
"rivetkit-typescript/packages/rivetkit-napi"
]

[workspace.package]
version = "2.3.0-rc.5"
Expand Down
20 changes: 20 additions & 0 deletions engine/sdks/rust/envoy-client/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use std::env;

fn main() {
println!("cargo:rustc-check-cfg=cfg(rivet_envoy_native_transport)");
println!("cargo:rustc-check-cfg=cfg(rivet_envoy_wasm_transport)");

let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
let native_transport = env::var_os("CARGO_FEATURE_NATIVE_TRANSPORT").is_some();
let wasm_transport = env::var_os("CARGO_FEATURE_WASM_TRANSPORT").is_some();
let is_wasm = target_arch == "wasm32";

// Use custom cfgs instead of raw feature flags because Cargo features are
// additive, so native and wasm transport features can be enabled together.
// These cfgs collapse features plus target_arch into one effective transport.
if native_transport && !is_wasm {
println!("cargo:rustc-cfg=rivet_envoy_native_transport");
} else if wasm_transport {
println!("cargo:rustc-cfg=rivet_envoy_wasm_transport");
}
}
40 changes: 14 additions & 26 deletions engine/sdks/rust/envoy-client/src/connection/mod.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,36 @@
use rivet_envoy_protocol as protocol;
#[cfg(any(
feature = "native-transport",
all(feature = "wasm-transport", target_arch = "wasm32")
rivet_envoy_native_transport,
all(rivet_envoy_wasm_transport, target_arch = "wasm32")
))]
use rivet_util_serde::HashableMap;
use vbare::OwnedVersionedData;

use crate::context::SharedContext;
use crate::context::WsTxMessage;
#[cfg(any(
feature = "native-transport",
all(feature = "wasm-transport", target_arch = "wasm32")
rivet_envoy_native_transport,
all(rivet_envoy_wasm_transport, target_arch = "wasm32")
))]
use crate::envoy::ToEnvoyMessage;
#[cfg(any(
feature = "native-transport",
all(feature = "wasm-transport", target_arch = "wasm32")
rivet_envoy_native_transport,
all(rivet_envoy_wasm_transport, target_arch = "wasm32")
))]
use crate::stringify::stringify_to_envoy;
use crate::stringify::stringify_to_rivet;

#[cfg(all(feature = "native-transport", feature = "wasm-transport"))]
compile_error!(
"`native-transport` and `wasm-transport` are mutually exclusive. Enable exactly one envoy-client transport."
);

#[cfg(not(any(feature = "native-transport", feature = "wasm-transport")))]
compile_error!(
"rivet-envoy-client requires a WebSocket transport. Enable `native-transport` or `wasm-transport`."
);

#[cfg(feature = "native-transport")]
#[cfg(rivet_envoy_native_transport)]
mod native;
#[cfg(feature = "wasm-transport")]
mod transport;
#[cfg(rivet_envoy_wasm_transport)]
mod wasm;

#[cfg(feature = "native-transport")]
pub use native::start_connection;
#[cfg(feature = "wasm-transport")]
pub use wasm::start_connection;
pub use transport::start_connection;

#[cfg(any(
feature = "native-transport",
all(feature = "wasm-transport", target_arch = "wasm32")
rivet_envoy_native_transport,
all(rivet_envoy_wasm_transport, target_arch = "wasm32")
))]
async fn send_initial_metadata(shared: &SharedContext) {
let mut prepopulate_map = HashableMap::new();
Expand Down Expand Up @@ -73,8 +61,8 @@ async fn send_initial_metadata(shared: &SharedContext) {
}

#[cfg(any(
feature = "native-transport",
all(feature = "wasm-transport", target_arch = "wasm32")
rivet_envoy_native_transport,
all(rivet_envoy_wasm_transport, target_arch = "wasm32")
))]
async fn forward_to_envoy(shared: &SharedContext, message: protocol::ToEnvoy) {
if tracing::enabled!(tracing::Level::DEBUG) {
Expand Down
24 changes: 24 additions & 0 deletions engine/sdks/rust/envoy-client/src/connection/transport.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Selects the public connection entrypoint from the effective transport cfg.
//!
//! Raw feature cfgs are not enough here because Cargo features are additive
//! and transport selection also depends on the build target.

#[cfg(all(
target_arch = "wasm32",
feature = "native-transport",
feature = "wasm-transport"
))]
compile_error!(
"`native-transport` and `wasm-transport` are mutually exclusive. Enable exactly one envoy-client transport."
);

#[cfg(not(any(feature = "native-transport", feature = "wasm-transport")))]
compile_error!(
"rivet-envoy-client requires a WebSocket transport. Enable `native-transport` or `wasm-transport`."
);

#[cfg(rivet_envoy_native_transport)]
pub use super::native::start_connection;

#[cfg(rivet_envoy_wasm_transport)]
pub use super::wasm::start_connection;
4 changes: 2 additions & 2 deletions rivetkit-rust/packages/rivetkit-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ native-runtime = [
"dep:reqwest",
"rivet-envoy-client/native-transport",
]
wasm-runtime = ["rivet-envoy-client/wasm-transport"]
wasm-runtime = ["dep:wasm-bindgen-futures", "rivet-envoy-client/wasm-transport"]
sqlite = ["sqlite-local"]
sqlite-local = ["native-runtime", "dep:async-trait", "dep:depot-client"]
sqlite-remote = []
Expand Down Expand Up @@ -49,6 +49,7 @@ tokio-util.workspace = true
tracing.workspace = true
url.workspace = true
vbare.workspace = true
wasm-bindgen-futures = { version = "0.4", optional = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio.workspace = true
Expand All @@ -60,7 +61,6 @@ js-sys = "0.3"
tokio = { version = "1.44.0", default-features = false, features = ["macros", "rt", "sync", "time"] }
uuid = { version = "1.11.0", features = ["v4", "serde", "js"] }
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-time = "1.1"

[dev-dependencies]
Expand Down
18 changes: 18 additions & 0 deletions rivetkit-rust/packages/rivetkit-core/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::env;

fn main() {
println!("cargo:rustc-check-cfg=cfg(rivetkit_native_runtime)");
println!("cargo:rustc-check-cfg=cfg(rivetkit_wasm_runtime)");

let native_runtime = env::var_os("CARGO_FEATURE_NATIVE_RUNTIME").is_some();
let wasm_runtime = env::var_os("CARGO_FEATURE_WASM_RUNTIME").is_some();

// Use custom cfgs instead of raw feature flags because Cargo features are
// additive, so native and wasm features can be enabled at the same time.
// These cfgs collapse that feature set into exactly one effective runtime.
if wasm_runtime && !native_runtime {
println!("cargo:rustc-cfg=rivetkit_wasm_runtime");
} else {
println!("cargo:rustc-cfg=rivetkit_native_runtime");
}
}
18 changes: 9 additions & 9 deletions rivetkit-rust/packages/rivetkit-core/src/actor/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,9 +451,9 @@ impl ActorContext {
// so future changes to `mark_destroy_requested` cannot drift.
// `destroy_requested` is already true from the swap above. The redundant
// `store(true)` inside is harmless.
#[cfg(not(feature = "wasm-runtime"))]
#[cfg(not(rivetkit_wasm_runtime))]
self.mark_destroy_requested();
#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
self.mark_destroy_requested_without_spawn();

let ctx = self.clone();
Expand All @@ -480,7 +480,7 @@ impl ActorContext {
self.0.destroy_completed.store(false, Ordering::SeqCst);
}

#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
fn mark_destroy_requested_without_spawn(&self) {
self.cancel_sleep_timer();
self.0.destroy_requested.store(true, Ordering::SeqCst);
Expand Down Expand Up @@ -530,7 +530,7 @@ impl ActorContext {
self.0.shutdown_deadline.cancel();
}

#[cfg(not(feature = "wasm-runtime"))]
#[cfg(not(rivetkit_wasm_runtime))]
pub fn wait_until(&self, future: impl Future<Output = ()> + Send + 'static) {
if Handle::try_current().is_err() {
tracing::warn!("skipping wait_until without a tokio runtime");
Expand All @@ -550,15 +550,15 @@ impl ActorContext {
});
}

#[cfg(not(feature = "wasm-runtime"))]
#[cfg(not(rivetkit_wasm_runtime))]
pub fn register_task(&self, future: impl Future<Output = ()> + Send + 'static) {
let ctx = self.clone();
self.track_shutdown_task(async move {
Self::run_registered_task(ctx, future).await;
});
}

#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
pub fn wait_until(&self, future: impl Future<Output = ()> + 'static) {
let ctx = self.clone();
self.track_shutdown_task(async move {
Expand All @@ -570,7 +570,7 @@ impl ActorContext {
});
}

#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
pub fn register_task(&self, future: impl Future<Output = ()> + 'static) {
let ctx = self.clone();
self.track_shutdown_task(async move {
Expand Down Expand Up @@ -1286,10 +1286,10 @@ impl ActorContext {
return;
}

#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
return;

#[cfg(not(feature = "wasm-runtime"))]
#[cfg(not(rivetkit_wasm_runtime))]
self.reset_sleep_timer_state();
}

Expand Down
16 changes: 8 additions & 8 deletions rivetkit-rust/packages/rivetkit-core/src/actor/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use crate::ActorConfig;
use crate::actor::lifecycle_hooks::ActorStart;
use crate::runtime::RuntimeBoxFuture;

#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
pub type ActorEntryFn = dyn Fn(ActorStart) -> RuntimeBoxFuture<Result<()>>;

#[cfg(not(feature = "wasm-runtime"))]
#[cfg(not(rivetkit_wasm_runtime))]
pub type ActorEntryFn = dyn Fn(ActorStart) -> RuntimeBoxFuture<Result<()>> + Send + Sync;

/// Runtime extension point for building actor receive loops.
Expand All @@ -19,10 +19,10 @@ pub struct ActorFactory {
manual_startup_ready: bool,
}

#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
unsafe impl Send for ActorFactory {}

#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
unsafe impl Sync for ActorFactory {}

impl ActorFactory {
Expand Down Expand Up @@ -63,19 +63,19 @@ impl ActorFactory {
}
}

#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
pub trait ActorEntry: Fn(ActorStart) -> RuntimeBoxFuture<Result<()>> + 'static {}

#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
impl<F> ActorEntry for F where F: Fn(ActorStart) -> RuntimeBoxFuture<Result<()>> + 'static {}

#[cfg(not(feature = "wasm-runtime"))]
#[cfg(not(rivetkit_wasm_runtime))]
pub trait ActorEntry:
Fn(ActorStart) -> RuntimeBoxFuture<Result<()>> + Send + Sync + 'static
{
}

#[cfg(not(feature = "wasm-runtime"))]
#[cfg(not(rivetkit_wasm_runtime))]
impl<F> ActorEntry for F where
F: Fn(ActorStart) -> RuntimeBoxFuture<Result<()>> + Send + Sync + 'static
{
Expand Down
8 changes: 4 additions & 4 deletions rivetkit-rust/packages/rivetkit-core/src/actor/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use uuid::Uuid;
use crate::actor::context::ActorContext;
use crate::actor::state::PersistedScheduleEvent;
use crate::error::ActorRuntime;
#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
use crate::runtime::RuntimeSpawner;

pub(super) type InternalKeepAwakeCallback =
Expand Down Expand Up @@ -349,7 +349,7 @@ impl ActorContext {
return;
}

#[cfg(not(feature = "wasm-runtime"))]
#[cfg(not(rivetkit_wasm_runtime))]
let tokio_handle = match Handle::try_current() {
Ok(handle) => handle,
Err(_) => return,
Expand Down Expand Up @@ -384,10 +384,10 @@ impl ActorContext {
}
.in_current_span();

#[cfg(not(feature = "wasm-runtime"))]
#[cfg(not(rivetkit_wasm_runtime))]
let handle = tokio_handle.spawn(task);

#[cfg(feature = "wasm-runtime")]
#[cfg(rivetkit_wasm_runtime)]
let handle = RuntimeSpawner::spawn(task);

*self.0.schedule_local_alarm_task.lock() = Some(handle);
Expand Down
Loading
Loading