diff --git a/engine/packages/api-peer/src/actors/delete.rs b/engine/packages/api-peer/src/actors/delete.rs index afb5c3486c..865e87ea14 100644 --- a/engine/packages/api-peer/src/actors/delete.rs +++ b/engine/packages/api-peer/src/actors/delete.rs @@ -67,20 +67,14 @@ pub async fn delete(ctx: ApiCtx, path: DeletePath, query: DeleteQuery) -> Result .signal(pegboard::workflows::actor::Destroy {}) .to_workflow::() .tag("actor_id", path.actor_id) + .graceful_not_found() .send() - .await; - - if let Some(WorkflowError::WorkflowNotFound) = res - .as_ref() - .err() - .and_then(|x| x.chain().find_map(|x| x.downcast_ref::())) - { + .await?; + if res.is_none() { tracing::warn!( actor_id=?path.actor_id, "actor workflow not found, likely already stopped" ); - } else { - res?; } Ok(DeleteResponse {}) diff --git a/engine/packages/engine/src/util/wf/mod.rs b/engine/packages/engine/src/util/wf/mod.rs index 2c9e04c4c4..306d9e6961 100644 --- a/engine/packages/engine/src/util/wf/mod.rs +++ b/engine/packages/engine/src/util/wf/mod.rs @@ -468,22 +468,19 @@ pub async fn print_history( } } EventData::Signals(data) => { - // Indent - print!("{}{c} ", " ".repeat(indent)); - for ((signal_id, name), body) in data.signal_ids.iter().zip(&data.names).zip(&data.bodies) { // Indent - print!("{}{c} - ", " ".repeat(indent)); + print!("{}{c} - ", " ".repeat(indent)); println!("{}", event_style.apply_to(name)); - print!("{}{c} ", " ".repeat(indent)); + print!("{}{c} ", " ".repeat(indent)); println!("id {}", style(signal_id).green()); if !exclude_json { // Indent - print!("{}{c} ", " ".repeat(indent)); + print!("{}{c} ", " ".repeat(indent)); println!( "body {}", @@ -590,7 +587,7 @@ pub fn print_event_name(event: &Event) { ), EventData::Signal(signal) => print!( "{} {}", - style.apply_to("signal receive").bold(), + style.apply_to("signal").bold(), style.apply_to(&signal.name) ), EventData::SignalSend(signal_send) => print!( @@ -626,7 +623,7 @@ pub fn print_event_name(event: &Event) { EventData::Branch => print!("{}", style.apply_to("branch").bold()), EventData::Signals(signal) => print!( "{} {}", - style.apply_to("signal receive").bold(), + style.apply_to("signal recv").bold(), style.apply_to(&signal.names.len()) ), } diff --git a/engine/packages/gasoline/src/builder/common/signal.rs b/engine/packages/gasoline/src/builder/common/signal.rs index 86f03fa878..7f1b4d2692 100644 --- a/engine/packages/gasoline/src/builder/common/signal.rs +++ b/engine/packages/gasoline/src/builder/common/signal.rs @@ -18,6 +18,7 @@ pub struct SignalBuilder { to_workflow_name: Option<&'static str>, to_workflow_id: Option, tags: serde_json::Map, + graceful_not_found: bool, error: Option, } @@ -37,6 +38,7 @@ impl SignalBuilder { to_workflow_name: None, to_workflow_id: None, tags: serde_json::Map::new(), + graceful_not_found: false, error: from_workflow.then_some(BuilderError::CannotDispatchFromOpInWorkflow), } } @@ -102,8 +104,21 @@ impl SignalBuilder { self } + /// Does not throw an error when the signal target is not found and instead returns `Ok(None)`. + pub fn graceful_not_found(mut self) -> Self { + if self.error.is_some() { + return self; + } + + self.graceful_not_found = true; + + self + } + + /// Returns the signal id that was just sent. Unless `graceful_not_found` is set and the workflow does not + /// exist, will always return `Some`. #[tracing::instrument(skip_all, fields(signal_name=T::NAME, signal_id))] - pub async fn send(self) -> Result { + pub async fn send(self) -> Result> { if let Some(err) = self.error { return Err(err.into()); } @@ -132,8 +147,18 @@ impl SignalBuilder { let workflow_id = self .db .find_workflow(workflow_name, &serde_json::Value::Object(self.tags)) - .await? - .ok_or(WorkflowError::WorkflowNotFound)?; + .await?; + + let Some(workflow_id) = workflow_id else { + // Handle signal target not found gracefully + if self.graceful_not_found { + tracing::debug!("signal target not found"); + + return Ok(None); + } else { + return Err(WorkflowError::WorkflowNotFound.into()); + } + }; self.db .publish_signal(self.ray_id, workflow_id, signal_id, T::NAME, &input_val) @@ -188,6 +213,6 @@ impl SignalBuilder { ], ); - Ok(signal_id) + Ok(Some(signal_id)) } } diff --git a/engine/packages/gasoline/src/builder/workflow/signal.rs b/engine/packages/gasoline/src/builder/workflow/signal.rs index 6c304ad841..bd476a4f4b 100644 --- a/engine/packages/gasoline/src/builder/workflow/signal.rs +++ b/engine/packages/gasoline/src/builder/workflow/signal.rs @@ -6,8 +6,13 @@ use rivet_util::Id; use serde::Serialize; use crate::{ - builder::BuilderError, ctx::WorkflowCtx, error::WorkflowError, history::cursor::HistoryResult, - metrics, signal::Signal, workflow::Workflow, + builder::BuilderError, + ctx::WorkflowCtx, + error::WorkflowError, + history::{cursor::HistoryResult, event::EventType, removed::Signal as RemovedSignal}, + metrics, + signal::Signal, + workflow::Workflow, }; pub struct SignalBuilder<'a, T: Signal + Serialize> { @@ -18,6 +23,7 @@ pub struct SignalBuilder<'a, T: Signal + Serialize> { to_workflow_name: Option<&'static str>, to_workflow_id: Option, tags: serde_json::Map, + graceful_not_found: bool, error: Option, } @@ -31,6 +37,7 @@ impl<'a, T: Signal + Serialize> SignalBuilder<'a, T> { to_workflow_name: None, to_workflow_id: None, tags: serde_json::Map::new(), + graceful_not_found: false, error: None, } } @@ -85,14 +92,39 @@ impl<'a, T: Signal + Serialize> SignalBuilder<'a, T> { self } + /// Does not throw an error when the signal target is not found and instead returns `Ok(None)`. + pub fn graceful_not_found(mut self) -> Self { + if self.error.is_some() { + return self; + } + + self.graceful_not_found = true; + + self + } + + /// Returns the signal id that was just sent. Unless `graceful_not_found` is set and the workflow does not + /// exist, will always return `Some`. #[tracing::instrument(skip_all, fields(signal_name=T::NAME, signal_id))] - pub async fn send(self) -> Result { + pub async fn send(self) -> Result> { self.ctx.check_stop()?; if let Some(err) = self.error { return Err(err.into()); } + // Check if this signal is being replayed and previously had no target (will have a removed event) + if self.graceful_not_found && self.ctx.cursor().is_removed() { + self.ctx.cursor().compare_removed::>()?; + + tracing::debug!("replaying gracefully not found signal dispatch"); + + // Move to next event + self.ctx.cursor_mut().inc(); + + return Ok(None); + } + // Error for version mismatch. This is done in the builder instead of in `VersionedWorkflowCtx` to // defer the error. self.ctx.compare_version("signal", self.version)?; @@ -105,7 +137,7 @@ impl<'a, T: Signal + Serialize> SignalBuilder<'a, T> { // Signal sent before let signal_id = if let HistoryResult::Event(signal) = history_res { - tracing::debug!("replaying signal dispatch",); + tracing::debug!("replaying signal dispatch"); signal.signal_id } @@ -133,8 +165,33 @@ impl<'a, T: Signal + Serialize> SignalBuilder<'a, T> { .ctx .db() .find_workflow(workflow_name, &serde_json::Value::Object(self.tags)) - .await? - .ok_or(WorkflowError::WorkflowNotFound)?; + .await?; + + let Some(workflow_id) = workflow_id else { + // Handle signal target not found gracefully + if self.graceful_not_found { + tracing::debug!("signal target not found"); + + // Insert removed event + self.ctx + .db() + .commit_workflow_removed_event( + self.ctx.workflow_id(), + &location, + EventType::SignalSend, + Some(T::NAME), + self.ctx.loop_location(), + ) + .await?; + + // Move to next event + self.ctx.cursor_mut().update(&location); + + return Ok(None); + } else { + return Err(WorkflowError::WorkflowNotFound.into()); + } + }; self.ctx .db() @@ -222,6 +279,6 @@ impl<'a, T: Signal + Serialize> SignalBuilder<'a, T> { // Move to next event self.ctx.cursor_mut().update(&location); - Ok(signal_id) + Ok(Some(signal_id)) } } diff --git a/engine/packages/gasoline/src/ctx/workflow.rs b/engine/packages/gasoline/src/ctx/workflow.rs index d2c567659e..93470ebf8b 100644 --- a/engine/packages/gasoline/src/ctx/workflow.rs +++ b/engine/packages/gasoline/src/ctx/workflow.rs @@ -819,7 +819,7 @@ impl WorkflowCtx { let loop_location = self.cursor.current_location_for(&history_res); // Loop existed before - let (mut iteration, mut state, output, mut loop_event_commit_fut) = + let (mut iteration, mut state, output, mut loop_event_init_fut) = if let HistoryResult::Event(loop_event) = history_res { let state = loop_event.parse_state()?; let output = loop_event.parse_output()?; @@ -838,7 +838,7 @@ impl WorkflowCtx { let nested_loop_location = self.loop_location().cloned(); // This future is deferred until later for parallelization - let loop_event_commit_fut = async move { + let loop_event_init_fut = async move { db2.upsert_workflow_loop_event( workflow_id, &name, @@ -852,7 +852,7 @@ impl WorkflowCtx { .await }; - (0, state, None, Some(loop_event_commit_fut)) + (0, state, None, Some(loop_event_init_fut)) }; // Create a branch for the loop event @@ -869,6 +869,9 @@ impl WorkflowCtx { else { tracing::debug!("running loop"); + // Used to defer loop upsertion for parallelization + let mut loop_event_upsert_fut = None; + loop { self.check_stop()?; @@ -898,14 +901,23 @@ impl WorkflowCtx { // NOTE: Great care has been taken to optimize this function. This join allows multiple // txns to run simultaneously instead of in series but is hard to read. // - // 1. First, but not necessarily chronologically first because its parallelized, we + // 1. First (but not necessarily chronologically first because its parallelized), we // commit the loop event. This only happens on the first iteration of the loop // 2. Second, we commit the branch event for the current iteration - // 3. Last, we run the user's loop code - let (loop_event_commit_res, branch_commit_res, loop_res) = tokio::join!( + // 3. Third, we run the user's loop code + // 4. Last, if we have to upsert the loop event, we save the future and process it in the + // next iteration of the loop as part of this join + let (loop_event_commit_res, loop_event_upsert_res, branch_commit_res, loop_res) = tokio::join!( async { - if let Some(loop_event_commit_fut) = loop_event_commit_fut.take() { - loop_event_commit_fut.await + if let Some(loop_event_init_fut) = loop_event_init_fut.take() { + loop_event_init_fut.await + } else { + Ok(()) + } + }, + async { + if let Some(loop_event_upsert_fut) = loop_event_upsert_fut.take() { + loop_event_upsert_fut.await } else { Ok(()) } @@ -928,6 +940,7 @@ impl WorkflowCtx { ); loop_event_commit_res?; + loop_event_upsert_res?; branch_commit_res?; // Run loop @@ -936,23 +949,33 @@ impl WorkflowCtx { let dt2 = start_instant2.elapsed().as_secs_f64(); iteration += 1; - let state_val = serde_json::value::to_raw_value(&state) - .map_err(WorkflowError::SerializeLoopOutput)?; - // Commit workflow state to db if iteration % LOOP_ITERS_PER_COMMIT == 0 { - self.db - .upsert_workflow_loop_event( - self.workflow_id, - &self.name, + let state_val = serde_json::value::to_raw_value(&state) + .map_err(WorkflowError::SerializeLoopOutput)?; + + // Clone data to move into future + let loop_location = loop_location.clone(); + let db2 = self.db.clone(); + let workflow_id = self.workflow_id; + let name = self.name.clone(); + let version = self.version; + let nested_loop_location = self.loop_location().cloned(); + + // Defer upsertion to next iteration so it runs in parallel + loop_event_upsert_fut = Some(async move { + db2.upsert_workflow_loop_event( + workflow_id, + &name, &loop_location, - self.version, + version, iteration, &state_val, None, - self.loop_location(), + nested_loop_location.as_ref(), ) - .await?; + .await + }); } anyhow::Ok((dt2, None)) @@ -966,7 +989,8 @@ impl WorkflowCtx { let output_val = serde_json::value::to_raw_value(&res) .map_err(WorkflowError::SerializeLoopOutput)?; - // Commit loop output and final state to db + // Commit loop output and final state to db. Note that we don't defer this because + // there will be no more loop iterations afterwards. self.db .upsert_workflow_loop_event( self.workflow_id, @@ -1338,7 +1362,7 @@ impl WorkflowCtx { // Existing event if self.cursor.compare_removed::()? { - tracing::debug!("skipping removed step",); + tracing::debug!("skipping removed step"); } // New "removed" event else { diff --git a/engine/packages/gasoline/src/db/debug.rs b/engine/packages/gasoline/src/db/debug.rs index 0dba696a81..5b37dabe46 100644 --- a/engine/packages/gasoline/src/db/debug.rs +++ b/engine/packages/gasoline/src/db/debug.rs @@ -122,7 +122,7 @@ impl std::fmt::Display for EventData { unique_names.sort(); unique_names.dedup(); - write!(f, "signals {:?}", unique_names.join(", ")) + write!(f, "signal receive {:?}", unique_names.join(", ")) } } } diff --git a/engine/packages/gasoline/src/db/kv/debug.rs b/engine/packages/gasoline/src/db/kv/debug.rs index 21c6fbe7d3..28ae6793f5 100644 --- a/engine/packages/gasoline/src/db/kv/debug.rs +++ b/engine/packages/gasoline/src/db/kv/debug.rs @@ -932,6 +932,10 @@ impl DatabaseDebug for DatabaseKv { current_event.indexed_input_chunks.get_mut(key.index) { input_chunks.push(entry); + } else { + current_event + .indexed_input_chunks + .insert(key.index, vec![entry]); } } diff --git a/engine/packages/gasoline/src/db/kv/keys/history.rs b/engine/packages/gasoline/src/db/kv/keys/history.rs index 7650ad4d63..784797383e 100644 --- a/engine/packages/gasoline/src/db/kv/keys/history.rs +++ b/engine/packages/gasoline/src/db/kv/keys/history.rs @@ -1642,7 +1642,7 @@ pub mod insert { tx, workflow_id, location, - EventType::Signal, + EventType::Signals, version, create_ts, ) diff --git a/engine/packages/gasoline/src/db/kv/mod.rs b/engine/packages/gasoline/src/db/kv/mod.rs index 6d3777c3be..27dad968a9 100644 --- a/engine/packages/gasoline/src/db/kv/mod.rs +++ b/engine/packages/gasoline/src/db/kv/mod.rs @@ -1505,6 +1505,10 @@ impl Database for DatabaseKv { .get_mut(key.index) { input_chunks.push(entry); + } else { + current_event + .indexed_input_chunks + .insert(key.index, vec![entry]); } } @@ -1938,7 +1942,7 @@ impl Database for DatabaseKv { async move { // Fetch signals from all streams at the same time - let signals = futures_util::stream::iter(owned_filter.clone()) + let mut signals = futures_util::stream::iter(owned_filter.clone()) .map(|signal_name| { let pending_signal_subspace = self.subspace.subspace( &keys::workflow::PendingSignalKey::subspace( @@ -1972,6 +1976,9 @@ impl Database for DatabaseKv { .await?; if !signals.is_empty() { + // Sort by ts + signals.sort_by_key(|key| key.ts); + let now = rivet_util::timestamp::now(); // Insert history event @@ -1984,13 +1991,14 @@ impl Database for DatabaseKv { now, )?; - let mut signals = - futures_util::stream::iter(signals.into_iter().enumerate()) + let signals = + futures_util::stream::iter(signals.into_iter().take(limit).enumerate()) .map(|(index, key)| { let tx = tx.clone(); async move { let ack_ts_key = keys::signal::AckTsKey::new(key.signal_id); - let packed_key = tx.pack(&key); + + let packed_key = self.subspace.pack(&key); // Ack signal tx.add_conflict_range( @@ -2000,7 +2008,7 @@ impl Database for DatabaseKv { )?; tx.set( &self.subspace.pack(&ack_ts_key), - &ack_ts_key.serialize(rivet_util::timestamp::now())?, + &ack_ts_key.serialize(now)?, ); update_metric( @@ -2056,11 +2064,7 @@ impl Database for DatabaseKv { .try_collect::>() .await?; - // Sort by ts - signals.sort_by_key(|key| key.create_ts); - - // Apply limit - Ok(signals.into_iter().take(limit).collect()) + Ok(signals) } // No signals found else { diff --git a/engine/packages/gasoline/src/history/cursor.rs b/engine/packages/gasoline/src/history/cursor.rs index a098c351be..79fc6579eb 100644 --- a/engine/packages/gasoline/src/history/cursor.rs +++ b/engine/packages/gasoline/src/history/cursor.rs @@ -475,6 +475,15 @@ impl Cursor { } } + // Helper function for signal functionality + pub fn is_removed(&self) -> bool { + let Some(event) = self.current_event() else { + return false; + }; + + matches!(&event.data, EventData::Removed(_)) + } + /// Returns `true` if the current event is being replayed. pub fn compare_removed(&self) -> WorkflowResult { let Some(event) = self.current_event() else { diff --git a/engine/packages/guard/src/routing/pegboard_gateway.rs b/engine/packages/guard/src/routing/pegboard_gateway.rs index 4d460fa0c1..a13e9740ac 100644 --- a/engine/packages/guard/src/routing/pegboard_gateway.rs +++ b/engine/packages/guard/src/routing/pegboard_gateway.rs @@ -195,20 +195,15 @@ async fn route_request_inner( let res = ctx.signal(pegboard::workflows::actor::Wake {}) .to_workflow_id(actor.workflow_id) + .graceful_not_found() .send() - .await; + .await?; - if let Some(WorkflowError::WorkflowNotFound) = res - .as_ref() - .err() - .and_then(|x| x.chain().find_map(|x| x.downcast_ref::())) - { + if res.is_none() { tracing::warn!( ?actor_id, "actor workflow not found for rewake" ); - } else { - res?; } } else { tracing::warn!("actor retried waking 16 times, has not yet started"); diff --git a/engine/packages/pegboard-runner/src/ping_task.rs b/engine/packages/pegboard-runner/src/ping_task.rs index 02cfca5a01..5a0e783481 100644 --- a/engine/packages/pegboard-runner/src/ping_task.rs +++ b/engine/packages/pegboard-runner/src/ping_task.rs @@ -21,7 +21,7 @@ pub async fn task( } let Some(wf) = ctx - .workflow::(conn.workflow_id) + .workflow::(conn.workflow_id) .get() .await? else { diff --git a/engine/packages/pegboard-serverless/src/lib.rs b/engine/packages/pegboard-serverless/src/lib.rs index 19d9c9386c..336b9586c2 100644 --- a/engine/packages/pegboard-serverless/src/lib.rs +++ b/engine/packages/pegboard-serverless/src/lib.rs @@ -480,25 +480,33 @@ async fn outbound_handler( async fn drain_runner(ctx: &StandaloneCtx, runner_id: Id) -> Result<()> { let res = ctx - .signal(pegboard::workflows::runner::Stop { + .signal(pegboard::workflows::runner2::Stop { reset_actor_rescheduling: true, }) - .to_workflow::() + .to_workflow::() .tag("runner_id", runner_id) + .graceful_not_found() .send() - .await; - - if let Some(WorkflowError::WorkflowNotFound) = res - .as_ref() - .err() - .and_then(|x| x.chain().find_map(|x| x.downcast_ref::())) - { - tracing::warn!( - ?runner_id, - "runner workflow not found, likely already stopped" - ); - } else { - res?; + .await?; + + if res.is_none() { + // Retry with old runner wf + let res = ctx + .signal(pegboard::workflows::runner::Stop { + reset_actor_rescheduling: true, + }) + .to_workflow::() + .tag("runner_id", runner_id) + .graceful_not_found() + .send() + .await?; + + if res.is_none() { + tracing::warn!( + ?runner_id, + "runner workflow not found, likely already stopped" + ); + } } Ok(()) diff --git a/engine/packages/pegboard/src/lib.rs b/engine/packages/pegboard/src/lib.rs index a776a3d227..b1c9467996 100644 --- a/engine/packages/pegboard/src/lib.rs +++ b/engine/packages/pegboard/src/lib.rs @@ -14,6 +14,7 @@ pub fn registry() -> WorkflowResult { let mut registry = Registry::new(); registry.register_workflow::()?; registry.register_workflow::()?; + registry.register_workflow::()?; Ok(registry) } diff --git a/engine/packages/pegboard/src/workflows/actor/mod.rs b/engine/packages/pegboard/src/workflows/actor/mod.rs index 4f555e3a5d..c90f614051 100644 --- a/engine/packages/pegboard/src/workflows/actor/mod.rs +++ b/engine/packages/pegboard/src/workflows/actor/mod.rs @@ -3,7 +3,7 @@ use gas::prelude::*; use rivet_runner_protocol as protocol; use rivet_types::actors::CrashPolicy; -use crate::{errors, workflows::runner::AllocatePendingActorsInput}; +use crate::{errors, workflows::runner2::AllocatePendingActorsInput}; mod destroy; mod keys; diff --git a/engine/packages/pegboard/src/workflows/runner.rs b/engine/packages/pegboard/src/workflows/runner.rs index a0a4eb0276..41739b4b26 100644 --- a/engine/packages/pegboard/src/workflows/runner.rs +++ b/engine/packages/pegboard/src/workflows/runner.rs @@ -164,19 +164,14 @@ pub async fn pegboard_runner(ctx: &mut WorkflowCtx, input: &Input) -> Result<()> }) .to_workflow::() .tag("actor_id", &actor_id) + .graceful_not_found() .send() - .await; - - if let Some(WorkflowError::WorkflowNotFound) = - res.as_ref().err().and_then(|x| { - x.chain().find_map(|x| x.downcast_ref::()) - }) { + .await?; + if res.is_none() { tracing::warn!( ?actor_id, "actor workflow not found, likely already stopped" ); - } else { - res?; } } @@ -257,20 +252,14 @@ pub async fn pegboard_runner(ctx: &mut WorkflowCtx, input: &Input) -> Result<()> }) .to_workflow::() .tag("actor_id", actor_id) + .graceful_not_found() .send() - .await; - - if let Some(WorkflowError::WorkflowNotFound) = res - .as_ref() - .err() - .and_then(|x| x.chain().find_map(|x| x.downcast_ref::())) - { + .await?; + if res.is_none() { tracing::warn!( ?actor_id, "actor workflow not found, likely already stopped" ); - } else { - res?; } } else { let index = ctx @@ -356,20 +345,14 @@ pub async fn pegboard_runner(ctx: &mut WorkflowCtx, input: &Input) -> Result<()> }) .to_workflow::() .tag("actor_id", actor_id) + .graceful_not_found() .send() - .await; - - if let Some(WorkflowError::WorkflowNotFound) = res - .as_ref() - .err() - .and_then(|x| x.chain().find_map(|x| x.downcast_ref::())) - { + .await?; + if res.is_none() { tracing::warn!( ?actor_id, "actor workflow not found, likely already stopped" ); - } else { - res?; } } diff --git a/engine/packages/universaldb/src/driver/rocksdb/transaction_conflict_tracker.rs b/engine/packages/universaldb/src/driver/rocksdb/transaction_conflict_tracker.rs index 988c166a39..8ef61500d5 100644 --- a/engine/packages/universaldb/src/driver/rocksdb/transaction_conflict_tracker.rs +++ b/engine/packages/universaldb/src/driver/rocksdb/transaction_conflict_tracker.rs @@ -64,7 +64,7 @@ impl TransactionConflictTracker { for (cr2_start, cr2_end, cr2_type) in &txn2.conflict_ranges { // Check conflict ranges overlap if cr1_start < cr2_end && cr2_start < cr1_end && cr1_type != cr2_type { - tracing::info!( + tracing::debug!( ?cr1_start, ?cr1_end, ?cr1_type, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24f828e45a..cb6c37199a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,6 +46,12 @@ importers: specifier: ^8.8.5 version: 8.8.5 + engine: + dependencies: + '@vbare/compiler': + specifier: ^0.0.3 + version: 0.0.3(@bare-ts/lib@0.4.0) + engine/docker/template: dependencies: '@types/js-yaml': @@ -1989,7 +1995,7 @@ importers: version: 3.13.12(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@uiw/codemirror-extensions-basic-setup': specifier: ^4.25.1 - version: 4.25.1(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.8.1)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2) + version: 4.25.1(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2) '@uiw/codemirror-theme-github': specifier: ^4.25.1 version: 4.25.1(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2) @@ -2331,6 +2337,9 @@ importers: '@bare-ts/tools': specifier: ^0.13.0 version: 0.13.0(@bare-ts/lib@0.3.0) + '@biomejs/biome': + specifier: ^2.2.3 + version: 2.2.3 '@hono/node-server': specifier: ^1.18.2 version: 1.19.1(hono@4.9.8) @@ -2442,13 +2451,13 @@ importers: version: 6.0.1 '@mdx-js/loader': specifier: ^3.1.1 - version: 3.1.1(webpack@5.101.3) + version: 3.1.1(webpack@5.101.3(esbuild@0.25.9)) '@mdx-js/react': specifier: ^3.1.1 version: 3.1.1(@types/react@19.2.2)(react@19.1.1) '@next/mdx': specifier: ^15.5.2 - version: 15.5.2(@mdx-js/loader@3.1.1(webpack@5.101.3))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.1.1)) + version: 15.5.2(@mdx-js/loader@3.1.1(webpack@5.101.3(esbuild@0.25.9)))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.1.1)) '@next/third-parties': specifier: latest version: 16.0.1(next@15.5.2(@opentelemetry/api@1.9.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2))(react@19.1.1) @@ -2638,7 +2647,7 @@ importers: version: 13.0.2(eslint@8.26.0)(typescript@5.9.2) file-loader: specifier: ^6.2.0 - version: 6.2.0(webpack@5.101.3) + version: 6.2.0(webpack@5.101.3(esbuild@0.25.9)) prettier: specifier: ^2.8.8 version: 2.8.8 @@ -3250,6 +3259,13 @@ packages: peerDependencies: '@bare-ts/lib': '>=0.3.0 <=0.4.0' + '@bare-ts/tools@0.16.1': + resolution: {integrity: sha512-eKXTnVqzuKDxr1ozKsFSZfM1wcN4g/iMRnG9GB2fA8oyUcHxwokJC50CANfuSLe6rLnjhZ8Ave1Y2TnZqUqGcQ==} + engines: {node: '>=20.0.0'} + hasBin: true + peerDependencies: + '@bare-ts/lib': '>=0.3.0 <=0.4.0' + '@base-org/account@2.0.1': resolution: {integrity: sha512-tySVNx+vd6XEynZL0uvB10uKiwnAfThr8AbKTwILVG86mPbLAhEOInQIk+uDnvpTvfdUhC1Bi5T/46JvFoLZQQ==} @@ -7021,6 +7037,11 @@ packages: peerDependencies: '@urql/core': ^5.0.0 + '@vbare/compiler@0.0.3': + resolution: {integrity: sha512-Dhz0iwYjIhyGAPsNpiqDmDqgwLXfEonjFJLVQ0m/s4Tt9CsTjY0WV3KiQtJi5BdPt9481HR+0uwExH36FuuR2A==} + engines: {node: '>=18.0.0'} + hasBin: true + '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -14436,6 +14457,10 @@ snapshots: '@bare-ts/lib': 0.4.0 commander: 11.1.0 + '@bare-ts/tools@0.16.1(@bare-ts/lib@0.4.0)': + dependencies: + '@bare-ts/lib': 0.4.0 + '@base-org/account@2.0.1(@types/react@19.2.2)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(zod@3.25.76)': dependencies: '@noble/hashes': 1.4.0 @@ -16201,12 +16226,12 @@ snapshots: '@marijn/find-cluster-break@1.0.2': {} - '@mdx-js/loader@3.1.1(webpack@5.101.3)': + '@mdx-js/loader@3.1.1(webpack@5.101.3(esbuild@0.25.9))': dependencies: '@mdx-js/mdx': 3.1.1 source-map: 0.7.6 optionalDependencies: - webpack: 5.101.3 + webpack: 5.101.3(esbuild@0.25.9) transitivePeerDependencies: - supports-color @@ -16384,11 +16409,11 @@ snapshots: dependencies: glob: 7.1.7 - '@next/mdx@15.5.2(@mdx-js/loader@3.1.1(webpack@5.101.3))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.1.1))': + '@next/mdx@15.5.2(@mdx-js/loader@3.1.1(webpack@5.101.3(esbuild@0.25.9)))(@mdx-js/react@3.1.1(@types/react@19.2.2)(react@19.1.1))': dependencies: source-map: 0.7.6 optionalDependencies: - '@mdx-js/loader': 3.1.1(webpack@5.101.3) + '@mdx-js/loader': 3.1.1(webpack@5.101.3(esbuild@0.25.9)) '@mdx-js/react': 3.1.1(@types/react@19.2.2)(react@19.1.1) '@next/swc-darwin-arm64@15.4.5': @@ -18615,6 +18640,16 @@ snapshots: '@codemirror/state': 6.5.2 '@codemirror/view': 6.38.2 + '@uiw/codemirror-extensions-basic-setup@4.25.1(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2)': + dependencies: + '@codemirror/autocomplete': 6.19.0 + '@codemirror/commands': 6.9.0 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.9.0 + '@codemirror/search': 6.5.11 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.2 + '@uiw/codemirror-theme-github@4.25.1(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2)': dependencies: '@uiw/codemirror-themes': 4.25.1(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.2) @@ -18677,6 +18712,13 @@ snapshots: '@urql/core': 5.2.0 wonka: 6.3.5 + '@vbare/compiler@0.0.3(@bare-ts/lib@0.4.0)': + dependencies: + '@bare-ts/tools': 0.16.1(@bare-ts/lib@0.4.0) + commander: 11.1.0 + transitivePeerDependencies: + - '@bare-ts/lib' + '@vitejs/plugin-react@4.7.0(vite@5.4.20(@types/node@20.19.13)(less@4.4.1)(lightningcss@1.30.2)(sass@1.93.2)(stylus@0.62.0)(terser@5.44.0))': dependencies: '@babel/core': 7.28.4 @@ -20670,7 +20712,7 @@ snapshots: eslint: 8.26.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.26.0) eslint-plugin-react: 7.37.5(eslint@8.26.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.26.0) @@ -20692,7 +20734,7 @@ snapshots: dependencies: debug: 4.4.1 eslint: 8.26.0 - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0) glob: 7.2.3 is-glob: 4.0.3 resolve: 1.22.10 @@ -20711,7 +20753,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1)(eslint@8.26.0): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(eslint@8.26.0)(typescript@5.9.2))(eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.32.0(eslint@8.26.0))(eslint@8.26.0))(eslint@8.26.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -21210,11 +21252,11 @@ snapshots: dependencies: flat-cache: 3.2.0 - file-loader@6.2.0(webpack@5.101.3): + file-loader@6.2.0(webpack@5.101.3(esbuild@0.25.9)): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.101.3 + webpack: 5.101.3(esbuild@0.25.9) file-saver@2.0.5: {} @@ -25745,16 +25787,6 @@ snapshots: webpack: 5.101.3(esbuild@0.25.9) optionalDependencies: esbuild: 0.25.9 - optional: true - - terser-webpack-plugin@5.3.14(webpack@5.101.3): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - jest-worker: 27.5.1 - schema-utils: 4.3.3 - serialize-javascript: 6.0.2 - terser: 5.44.0 - webpack: 5.101.3 terser@5.44.0: dependencies: @@ -26878,38 +26910,6 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.101.3: - dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/wasm-edit': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.26.3 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.3 - es-module-lexer: 1.7.0 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.1 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 4.3.3 - tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(webpack@5.101.3) - watchpack: 2.4.4 - webpack-sources: 3.3.3 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - webpack@5.101.3(esbuild@0.25.9): dependencies: '@types/eslint-scope': 3.7.7 @@ -26941,7 +26941,6 @@ snapshots: - '@swc/core' - esbuild - uglify-js - optional: true whatwg-fetch@3.6.20: {} diff --git a/rivetkit-asyncapi/asyncapi.json b/rivetkit-asyncapi/asyncapi.json index 0d9c7c7f98..e6074c7754 100644 --- a/rivetkit-asyncapi/asyncapi.json +++ b/rivetkit-asyncapi/asyncapi.json @@ -1,436 +1,489 @@ { - "asyncapi": "3.0.0", - "info": { - "title": "RivetKit WebSocket Protocol", - "version": "2.0.24-rc.1", - "description": "WebSocket protocol for bidirectional communication between RivetKit clients and actors" - }, - "channels": { - "/gateway/{actorId}/connect": { - "address": "/gateway/{actorId}/connect", - "parameters": { - "actorId": { - "description": "The unique identifier for the actor instance" - } - }, - "messages": { - "toClient": { - "$ref": "#/components/messages/ToClient" - }, - "toServer": { - "$ref": "#/components/messages/ToServer" - } - } - } - }, - "operations": { - "sendToClient": { - "action": "send", - "channel": { - "$ref": "#/channels/~1gateway~1{actorId}~1connect" - }, - "messages": [ - { - "$ref": "#/channels/~1gateway~1{actorId}~1connect/messages/toClient" - } - ], - "summary": "Send messages from server to client", - "description": "Messages sent from the RivetKit actor to connected clients" - }, - "receiveFromClient": { - "action": "receive", - "channel": { - "$ref": "#/channels/~1gateway~1{actorId}~1connect" - }, - "messages": [ - { - "$ref": "#/channels/~1gateway~1{actorId}~1connect/messages/toServer" - } - ], - "summary": "Receive messages from client", - "description": "Messages received by the RivetKit actor from connected clients" - } - }, - "components": { - "messages": { - "ToClient": { - "name": "ToClient", - "title": "Message To Client", - "summary": "A message sent from the server to the client", - "contentType": "application/json", - "payload": { - "type": "object", - "properties": { - "body": { - "anyOf": [ - { - "type": "object", - "properties": { - "tag": { - "type": "string", - "const": "Init" - }, - "val": { - "type": "object", - "properties": { - "actorId": { - "type": "string" - }, - "connectionId": { - "type": "string" - } - }, - "required": [ - "actorId", - "connectionId" - ], - "additionalProperties": false - } - }, - "required": ["tag", "val"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "tag": { - "type": "string", - "const": "Error" - }, - "val": { - "type": "object", - "properties": { - "group": { - "type": "string" - }, - "code": { - "type": "string" - }, - "message": { - "type": "string" - }, - "metadata": {}, - "actionId": { - "type": ["integer", "null"] - } - }, - "required": [ - "group", - "code", - "message", - "actionId" - ], - "additionalProperties": false - } - }, - "required": ["tag", "val"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "tag": { - "type": "string", - "const": "ActionResponse" - }, - "val": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "output": {} - }, - "required": ["id"], - "additionalProperties": false - } - }, - "required": ["tag", "val"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "tag": { - "type": "string", - "const": "Event" - }, - "val": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "args": {} - }, - "required": ["name"], - "additionalProperties": false - } - }, - "required": ["tag", "val"], - "additionalProperties": false - } - ] - } - }, - "required": ["body"], - "additionalProperties": false - }, - "examples": [ - { - "name": "Init message", - "summary": "Initial connection message", - "payload": { - "body": { - "tag": "Init", - "val": { - "actorId": "actor_123", - "connectionId": "conn_456" - } - } - } - }, - { - "name": "Error message", - "summary": "Error response", - "payload": { - "body": { - "tag": "Error", - "val": { - "group": "auth", - "code": "unauthorized", - "message": "Authentication failed", - "actionId": null - } - } - } - }, - { - "name": "Action response", - "summary": "Response to an action request", - "payload": { - "body": { - "tag": "ActionResponse", - "val": { - "id": "123", - "output": { - "result": "success" - } - } - } - } - }, - { - "name": "Event", - "summary": "Event broadcast to subscribed clients", - "payload": { - "body": { - "tag": "Event", - "val": { - "name": "stateChanged", - "args": { - "newState": "active" - } - } - } - } - } - ] - }, - "ToServer": { - "name": "ToServer", - "title": "Message To Server", - "summary": "A message sent from the client to the server", - "contentType": "application/json", - "payload": { - "type": "object", - "properties": { - "body": { - "anyOf": [ - { - "type": "object", - "properties": { - "tag": { - "type": "string", - "const": "ActionRequest" - }, - "val": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "args": {} - }, - "required": ["id", "name"], - "additionalProperties": false - } - }, - "required": ["tag", "val"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "tag": { - "type": "string", - "const": "SubscriptionRequest" - }, - "val": { - "type": "object", - "properties": { - "eventName": { - "type": "string" - }, - "subscribe": { - "type": "boolean" - } - }, - "required": [ - "eventName", - "subscribe" - ], - "additionalProperties": false - } - }, - "required": ["tag", "val"], - "additionalProperties": false - } - ] - } - }, - "required": ["body"], - "additionalProperties": false - }, - "examples": [ - { - "name": "Action request", - "summary": "Request to execute an action", - "payload": { - "body": { - "tag": "ActionRequest", - "val": { - "id": "123", - "name": "updateState", - "args": { - "key": "value" - } - } - } - } - }, - { - "name": "Subscription request", - "summary": "Request to subscribe/unsubscribe from an event", - "payload": { - "body": { - "tag": "SubscriptionRequest", - "val": { - "eventName": "stateChanged", - "subscribe": true - } - } - } - } - ] - } - }, - "schemas": { - "Init": { - "type": "object", - "properties": { - "actorId": { - "type": "string" - }, - "connectionId": { - "type": "string" - } - }, - "required": ["actorId", "connectionId"], - "additionalProperties": false, - "description": "Initial connection message sent from server to client" - }, - "Error": { - "type": "object", - "properties": { - "group": { - "type": "string" - }, - "code": { - "type": "string" - }, - "message": { - "type": "string" - }, - "metadata": {}, - "actionId": { - "type": ["integer", "null"] - } - }, - "required": ["group", "code", "message", "actionId"], - "additionalProperties": false, - "description": "Error message sent from server to client" - }, - "ActionResponse": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "output": {} - }, - "required": ["id"], - "additionalProperties": false, - "description": "Response to an action request" - }, - "Event": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "args": {} - }, - "required": ["name"], - "additionalProperties": false, - "description": "Event broadcast to subscribed clients" - }, - "ActionRequest": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "args": {} - }, - "required": ["id", "name"], - "additionalProperties": false, - "description": "Request to execute an action on the actor" - }, - "SubscriptionRequest": { - "type": "object", - "properties": { - "eventName": { - "type": "string" - }, - "subscribe": { - "type": "boolean" - } - }, - "required": ["eventName", "subscribe"], - "additionalProperties": false, - "description": "Request to subscribe or unsubscribe from an event" - } - } - } -} + "asyncapi": "3.0.0", + "info": { + "title": "RivetKit WebSocket Protocol", + "version": "2.0.24-rc.1", + "description": "WebSocket protocol for bidirectional communication between RivetKit clients and actors" + }, + "channels": { + "/gateway/{actorId}/connect": { + "address": "/gateway/{actorId}/connect", + "parameters": { + "actorId": { + "description": "The unique identifier for the actor instance" + } + }, + "messages": { + "toClient": { + "$ref": "#/components/messages/ToClient" + }, + "toServer": { + "$ref": "#/components/messages/ToServer" + } + } + } + }, + "operations": { + "sendToClient": { + "action": "send", + "channel": { + "$ref": "#/channels/~1gateway~1{actorId}~1connect" + }, + "messages": [ + { + "$ref": "#/channels/~1gateway~1{actorId}~1connect/messages/toClient" + } + ], + "summary": "Send messages from server to client", + "description": "Messages sent from the RivetKit actor to connected clients" + }, + "receiveFromClient": { + "action": "receive", + "channel": { + "$ref": "#/channels/~1gateway~1{actorId}~1connect" + }, + "messages": [ + { + "$ref": "#/channels/~1gateway~1{actorId}~1connect/messages/toServer" + } + ], + "summary": "Receive messages from client", + "description": "Messages received by the RivetKit actor from connected clients" + } + }, + "components": { + "messages": { + "ToClient": { + "name": "ToClient", + "title": "Message To Client", + "summary": "A message sent from the server to the client", + "contentType": "application/json", + "payload": { + "type": "object", + "properties": { + "body": { + "anyOf": [ + { + "type": "object", + "properties": { + "tag": { + "type": "string", + "const": "Init" + }, + "val": { + "type": "object", + "properties": { + "actorId": { + "type": "string" + }, + "connectionId": { + "type": "string" + } + }, + "required": [ + "actorId", + "connectionId" + ], + "additionalProperties": false + } + }, + "required": [ + "tag", + "val" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "tag": { + "type": "string", + "const": "Error" + }, + "val": { + "type": "object", + "properties": { + "group": { + "type": "string" + }, + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "metadata": {}, + "actionId": { + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "group", + "code", + "message", + "actionId" + ], + "additionalProperties": false + } + }, + "required": [ + "tag", + "val" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "tag": { + "type": "string", + "const": "ActionResponse" + }, + "val": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "output": {} + }, + "required": [ + "id" + ], + "additionalProperties": false + } + }, + "required": [ + "tag", + "val" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "tag": { + "type": "string", + "const": "Event" + }, + "val": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "args": {} + }, + "required": [ + "name" + ], + "additionalProperties": false + } + }, + "required": [ + "tag", + "val" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "body" + ], + "additionalProperties": false + }, + "examples": [ + { + "name": "Init message", + "summary": "Initial connection message", + "payload": { + "body": { + "tag": "Init", + "val": { + "actorId": "actor_123", + "connectionId": "conn_456" + } + } + } + }, + { + "name": "Error message", + "summary": "Error response", + "payload": { + "body": { + "tag": "Error", + "val": { + "group": "auth", + "code": "unauthorized", + "message": "Authentication failed", + "actionId": null + } + } + } + }, + { + "name": "Action response", + "summary": "Response to an action request", + "payload": { + "body": { + "tag": "ActionResponse", + "val": { + "id": "123", + "output": { + "result": "success" + } + } + } + } + }, + { + "name": "Event", + "summary": "Event broadcast to subscribed clients", + "payload": { + "body": { + "tag": "Event", + "val": { + "name": "stateChanged", + "args": { + "newState": "active" + } + } + } + } + } + ] + }, + "ToServer": { + "name": "ToServer", + "title": "Message To Server", + "summary": "A message sent from the client to the server", + "contentType": "application/json", + "payload": { + "type": "object", + "properties": { + "body": { + "anyOf": [ + { + "type": "object", + "properties": { + "tag": { + "type": "string", + "const": "ActionRequest" + }, + "val": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "args": {} + }, + "required": [ + "id", + "name" + ], + "additionalProperties": false + } + }, + "required": [ + "tag", + "val" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "tag": { + "type": "string", + "const": "SubscriptionRequest" + }, + "val": { + "type": "object", + "properties": { + "eventName": { + "type": "string" + }, + "subscribe": { + "type": "boolean" + } + }, + "required": [ + "eventName", + "subscribe" + ], + "additionalProperties": false + } + }, + "required": [ + "tag", + "val" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "body" + ], + "additionalProperties": false + }, + "examples": [ + { + "name": "Action request", + "summary": "Request to execute an action", + "payload": { + "body": { + "tag": "ActionRequest", + "val": { + "id": "123", + "name": "updateState", + "args": { + "key": "value" + } + } + } + } + }, + { + "name": "Subscription request", + "summary": "Request to subscribe/unsubscribe from an event", + "payload": { + "body": { + "tag": "SubscriptionRequest", + "val": { + "eventName": "stateChanged", + "subscribe": true + } + } + } + } + ] + } + }, + "schemas": { + "Init": { + "type": "object", + "properties": { + "actorId": { + "type": "string" + }, + "connectionId": { + "type": "string" + } + }, + "required": [ + "actorId", + "connectionId" + ], + "additionalProperties": false, + "description": "Initial connection message sent from server to client" + }, + "Error": { + "type": "object", + "properties": { + "group": { + "type": "string" + }, + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "metadata": {}, + "actionId": { + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "group", + "code", + "message", + "actionId" + ], + "additionalProperties": false, + "description": "Error message sent from server to client" + }, + "ActionResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "output": {} + }, + "required": [ + "id" + ], + "additionalProperties": false, + "description": "Response to an action request" + }, + "Event": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "args": {} + }, + "required": [ + "name" + ], + "additionalProperties": false, + "description": "Event broadcast to subscribed clients" + }, + "ActionRequest": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "args": {} + }, + "required": [ + "id", + "name" + ], + "additionalProperties": false, + "description": "Request to execute an action on the actor" + }, + "SubscriptionRequest": { + "type": "object", + "properties": { + "eventName": { + "type": "string" + }, + "subscribe": { + "type": "boolean" + } + }, + "required": [ + "eventName", + "subscribe" + ], + "additionalProperties": false, + "description": "Request to subscribe or unsubscribe from an event" + } + } + } +} \ No newline at end of file diff --git a/rivetkit-openapi/openapi.json b/rivetkit-openapi/openapi.json index 90803b4757..4ab454fa07 100644 --- a/rivetkit-openapi/openapi.json +++ b/rivetkit-openapi/openapi.json @@ -113,6 +113,7 @@ }, "put": { "requestBody": { + "required": true, "content": { "application/json": { "schema": { @@ -225,6 +226,7 @@ }, "post": { "requestBody": { + "required": true, "content": { "application/json": { "schema": { @@ -385,283 +387,6 @@ } } } - }, - "/gateway/{actorId}/health": { - "get": { - "parameters": [ - { - "name": "actorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the actor to target" - } - ], - "responses": { - "200": { - "description": "Health check", - "content": { - "text/plain": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/gateway/{actorId}/action/{action}": { - "post": { - "parameters": [ - { - "name": "actorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the actor to target" - }, - { - "name": "action", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The name of the action to execute" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "args": {} - }, - "additionalProperties": false - } - } - } - }, - "responses": { - "200": { - "description": "Action executed successfully", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "output": {} - }, - "additionalProperties": false - } - } - } - }, - "400": { - "description": "Invalid action" - }, - "500": { - "description": "Internal error" - } - } - } - }, - "/gateway/{actorId}/request/{path}": { - "get": { - "parameters": [ - { - "name": "actorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the actor to target" - }, - { - "name": "path", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The HTTP path to forward to the actor" - } - ], - "responses": { - "200": { - "description": "Response from actor's raw HTTP handler" - } - } - }, - "post": { - "parameters": [ - { - "name": "actorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the actor to target" - }, - { - "name": "path", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The HTTP path to forward to the actor" - } - ], - "responses": { - "200": { - "description": "Response from actor's raw HTTP handler" - } - } - }, - "put": { - "parameters": [ - { - "name": "actorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the actor to target" - }, - { - "name": "path", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The HTTP path to forward to the actor" - } - ], - "responses": { - "200": { - "description": "Response from actor's raw HTTP handler" - } - } - }, - "delete": { - "parameters": [ - { - "name": "actorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the actor to target" - }, - { - "name": "path", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The HTTP path to forward to the actor" - } - ], - "responses": { - "200": { - "description": "Response from actor's raw HTTP handler" - } - } - }, - "patch": { - "parameters": [ - { - "name": "actorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the actor to target" - }, - { - "name": "path", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The HTTP path to forward to the actor" - } - ], - "responses": { - "200": { - "description": "Response from actor's raw HTTP handler" - } - } - }, - "head": { - "parameters": [ - { - "name": "actorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the actor to target" - }, - { - "name": "path", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The HTTP path to forward to the actor" - } - ], - "responses": { - "200": { - "description": "Response from actor's raw HTTP handler" - } - } - }, - "options": { - "parameters": [ - { - "name": "actorId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the actor to target" - }, - { - "name": "path", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The HTTP path to forward to the actor" - } - ], - "responses": { - "200": { - "description": "Response from actor's raw HTTP handler" - } - } - } } } } \ No newline at end of file diff --git a/website/public/llms-full.txt b/website/public/llms-full.txt index 326df2442c..2bad7ad3be 100644 --- a/website/public/llms-full.txt +++ b/website/public/llms-full.txt @@ -182,32 +182,7 @@ See [helper types](/docs/actors/helper-types) for more details on using `ActionC # API Reference -For comprehensive API documentation, please refer to the TypeDoc generated documentation for each package: - -## Core Packages - -- [**rivetkit**](/typedoc/rivetkit/) - Main RivetKit package with full actor framework -- [**@rivetkit/actor**](/typedoc/actor/) - Core actor primitives -- [**@rivetkit/core**](/typedoc/core/) - Core utilities and types - -## Platform Adapters - -- [**@rivetkit/cloudflare-workers**](/typedoc/cloudflare-workers/) - Cloudflare Workers adapter -- [**@rivetkit/next-js**](/typedoc/next-js/) - Next.js integration -- [**@rivetkit/react**](/typedoc/react/) - React hooks and components - -## Additional Packages - -- [**@rivetkit/db**](/typedoc/db/) - Database integration utilities - ---- - -The TypeDoc documentation provides detailed information about: - -- All exported types and interfaces -- Function signatures and parameters -- Class methods and properties -- Return types and examples +For comprehensive API documentation, please refer to the [TypeDoc generated documentation](/typedoc/). ## Authentication # Authentication diff --git a/website/public/llms.txt b/website/public/llms.txt index 213416054c..93aa0e255a 100644 --- a/website/public/llms.txt +++ b/website/public/llms.txt @@ -29,6 +29,7 @@ https://rivet.dev/blog/2025-10-20-how-we-built-websocket-servers-for-vercel-func https://rivet.dev/blog/2025-10-20-weekly-updates https://rivet.dev/blog/2025-10-24-weekly-updates https://rivet.dev/blog/2025-11-02-weekly-updates +https://rivet.dev/blog/2025-11-09-weekly-updates https://rivet.dev/blog/godot-multiplayer-compared-to-unity https://rivet.dev/changelog https://rivet.dev/changelog.json @@ -58,6 +59,7 @@ https://rivet.dev/changelog/2025-10-20-how-we-built-websocket-servers-for-vercel https://rivet.dev/changelog/2025-10-20-weekly-updates https://rivet.dev/changelog/2025-10-24-weekly-updates https://rivet.dev/changelog/2025-11-02-weekly-updates +https://rivet.dev/changelog/2025-11-09-weekly-updates https://rivet.dev/changelog/godot-multiplayer-compared-to-unity https://rivet.dev/cloud https://rivet.dev/docs/actors