From 5645288df7595cb872cd8ff0f9fd1b7e22b45c16 Mon Sep 17 00:00:00 2001 From: "kistz (Kilian Strunz)" Date: Sun, 14 Jun 2026 14:10:57 +0200 Subject: [PATCH 01/15] impl --- crates/bindings/src/lib.rs | 75 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 62b78e14be4..9d70a410c77 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1440,6 +1440,7 @@ impl ProcedureContext { } /// A handle on a database with a particular table schema. +#[deprecated(note = "Use the capability based traits (CtxDbRead, CtxDbWrite, CtxWithSender) instead!")] pub trait DbContext { /// A view into the tables of a database. /// @@ -1461,6 +1462,7 @@ pub trait DbContext { fn db_read_only(&self) -> &LocalReadOnly; } +#[allow(deprecated)] impl DbContext for AnonymousViewContext { type DbView = LocalReadOnly; @@ -1473,6 +1475,7 @@ impl DbContext for AnonymousViewContext { } } +#[allow(deprecated)] impl DbContext for ReducerContext { type DbView = Local; @@ -1485,6 +1488,7 @@ impl DbContext for ReducerContext { } } +#[allow(deprecated)] impl DbContext for TxContext { type DbView = Local; @@ -1497,6 +1501,7 @@ impl DbContext for TxContext { } } +#[allow(deprecated)] impl DbContext for ViewContext { type DbView = LocalReadOnly; @@ -1526,6 +1531,76 @@ impl Local { } } +/// This trait allows you to be generic over all contexts that allow you to read from the db. +/// including views, reducers and event procedures and http handlers through [TxContext]. +pub trait CtxDbRead { + fn db(&self) -> &LocalReadOnly; +} + +impl CtxDbRead for TxContext { + fn db(&self) -> &LocalReadOnly { + &LocalReadOnly {} + } +} + +impl CtxDbRead for ReducerContext { + fn db(&self) -> &LocalReadOnly { + &LocalReadOnly {} + } +} + +impl CtxDbRead for ViewContext { + fn db(&self) -> &LocalReadOnly { + &LocalReadOnly {} + } +} + +impl CtxDbRead for AnonymousViewContext { + fn db(&self) -> &LocalReadOnly { + &LocalReadOnly {} + } +} + +/// This trait allows you to be generic over all contexts that allow read/write access to the db. +pub trait CtxDbWrite { + fn db(&self) -> &Local; +} + +impl CtxDbWrite for TxContext { + fn db(&self) -> &Local { + &Local {} + } +} + +impl CtxDbWrite for ReducerContext { + fn db(&self) -> &Local { + &Local {} + } +} + +/// This trait allows you to be generic over all contexts that allow to retrieve the caller identity. +pub trait CtxWithSender { + fn sender(&self) -> Identity; +} + +impl CtxWithSender for ViewContext { + fn sender(&self) -> Identity { + self.sender + } +} + +impl CtxWithSender for ReducerContext { + fn sender(&self) -> Identity { + self.sender + } +} + +impl CtxWithSender for TxContext { + fn sender(&self) -> Identity { + self.0.sender + } +} + /// The [JWT] of an [`AuthCtx`]. /// /// [JWT]: https://en.wikipedia.org/wiki/JSON_Web_Token From 0b0c5f99417e3f72748c0bcf953c7dba8ba8ea2e Mon Sep 17 00:00:00 2001 From: "kistz (Kilian Strunz)" Date: Sun, 14 Jun 2026 15:43:55 +0200 Subject: [PATCH 02/15] finish the impl --- crates/bindings/src/lib.rs | 41 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 9d70a410c77..f31de2c1237 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -912,6 +912,8 @@ pub use spacetimedb_bindings_macro::view; pub struct QueryBuilder {} pub use query_builder::{Query, RawQuery}; +use crate::http::{HandlerContext, HttpClient}; + /// One of two possible types that can be passed as the first argument to a `#[view]`. /// The other is [`ViewContext`]. /// Use this type if the view does not depend on the caller's identity. @@ -1532,7 +1534,8 @@ impl Local { } /// This trait allows you to be generic over all contexts that allow you to read from the db. -/// including views, reducers and event procedures and http handlers through [TxContext]. +/// including views, reducers and even procedures and http handlers through [TxContext]. +/// Useful when trying to encapsulate logic in reusable parts. pub trait CtxDbRead { fn db(&self) -> &LocalReadOnly; } @@ -1578,7 +1581,7 @@ impl CtxDbWrite for ReducerContext { } } -/// This trait allows you to be generic over all contexts that allow to retrieve the caller identity. +/// This trait allows you to be generic over all contexts that allow to retrieve the caller [Identity]. pub trait CtxWithSender { fn sender(&self) -> Identity; } @@ -1601,6 +1604,40 @@ impl CtxWithSender for TxContext { } } +/// This trait allows you to be generic over all contexts that allow to retrieve the [Timestamp] of calling them. +pub trait CtxWithTimestamp { + fn timestamp(&self) -> Timestamp; +} + +impl CtxWithTimestamp for ReducerContext { + fn timestamp(&self) -> Timestamp { + self.timestamp + } +} + +impl CtxWithTimestamp for TxContext { + fn timestamp(&self) -> Timestamp { + self.timestamp + } +} + +/// This trait allows you to be generic over all contexts that allow to use the [HttpClient]. +pub trait CtxWithHttp { + fn http(&self) -> &HttpClient; +} + +impl CtxWithHttp for HandlerContext { + fn http(&self) -> &HttpClient { + &self.http + } +} + +impl CtxWithHttp for ProcedureContext { + fn http(&self) -> &HttpClient { + &self.http + } +} + /// The [JWT] of an [`AuthCtx`]. /// /// [JWT]: https://en.wikipedia.org/wiki/JSON_Web_Token From 0bd8d52069d28534dd2616bfbfee3b76d72e652b Mon Sep 17 00:00:00 2001 From: "kistz (Kilian Strunz)" Date: Sun, 14 Jun 2026 16:08:44 +0200 Subject: [PATCH 03/15] fix wrong deprecation message --- crates/bindings/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index f31de2c1237..146d85f4164 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1442,7 +1442,7 @@ impl ProcedureContext { } /// A handle on a database with a particular table schema. -#[deprecated(note = "Use the capability based traits (CtxDbRead, CtxDbWrite, CtxWithSender) instead!")] +#[deprecated(note = "Use the capability based traits (CtxDbRead, CtxDbWrite) instead!")] pub trait DbContext { /// A view into the tables of a database. /// From 43f789e05aad120917016a6c9fe9d6bdd7d2c320 Mon Sep 17 00:00:00 2001 From: "kistz (Kilian Strunz)" Date: Sun, 14 Jun 2026 16:35:20 +0200 Subject: [PATCH 04/15] fix impl --- crates/bindings/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 146d85f4164..f079c9a99d4 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1537,35 +1537,35 @@ impl Local { /// including views, reducers and even procedures and http handlers through [TxContext]. /// Useful when trying to encapsulate logic in reusable parts. pub trait CtxDbRead { - fn db(&self) -> &LocalReadOnly; + fn db_read_only(&self) -> &LocalReadOnly; } impl CtxDbRead for TxContext { - fn db(&self) -> &LocalReadOnly { + fn db_read_only(&self) -> &LocalReadOnly { &LocalReadOnly {} } } impl CtxDbRead for ReducerContext { - fn db(&self) -> &LocalReadOnly { + fn db_read_only(&self) -> &LocalReadOnly { &LocalReadOnly {} } } impl CtxDbRead for ViewContext { - fn db(&self) -> &LocalReadOnly { + fn db_read_only(&self) -> &LocalReadOnly { &LocalReadOnly {} } } impl CtxDbRead for AnonymousViewContext { - fn db(&self) -> &LocalReadOnly { + fn db_read_only(&self) -> &LocalReadOnly { &LocalReadOnly {} } } /// This trait allows you to be generic over all contexts that allow read/write access to the db. -pub trait CtxDbWrite { +pub trait CtxDbWrite: CtxDbRead { fn db(&self) -> &Local; } From 1190071fa4ede8aa6eaa402b10d3b9ab9da89169 Mon Sep 17 00:00:00 2001 From: Kilian Strunz <93079615+kistz@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:07:26 +0200 Subject: [PATCH 05/15] Update crates/bindings/src/lib.rs Co-authored-by: Phoebe Goldman Signed-off-by: Kilian Strunz <93079615+kistz@users.noreply.github.com> --- crates/bindings/src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index f079c9a99d4..7c81816e861 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1533,9 +1533,13 @@ impl Local { } } -/// This trait allows you to be generic over all contexts that allow you to read from the db. -/// including views, reducers and even procedures and http handlers through [TxContext]. -/// Useful when trying to encapsulate logic in reusable parts. +/// Contexts which provide read access to the database. +/// +/// This trait is useful for writing reusable logic which is generic over the context type, +/// allowing it to be used from views, reducers, and transactions started by procedures and HTTP handlers. +/// +/// When operating on a concrete-typed [`ViewContext`], [`ReducerContext`] or [`TxContext`], +/// this trait is not necessary, as the context's `db` field provides the same (or greater, read-write) access. pub trait CtxDbRead { fn db_read_only(&self) -> &LocalReadOnly; } From 923939b74cfbeffdf0c1a2182fb8bffa411089d1 Mon Sep 17 00:00:00 2001 From: Kilian Strunz <93079615+kistz@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:07:46 +0200 Subject: [PATCH 06/15] Update crates/bindings/src/lib.rs Co-authored-by: Phoebe Goldman Signed-off-by: Kilian Strunz <93079615+kistz@users.noreply.github.com> --- crates/bindings/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 7c81816e861..dde125413f6 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1568,7 +1568,13 @@ impl CtxDbRead for AnonymousViewContext { } } -/// This trait allows you to be generic over all contexts that allow read/write access to the db. +/// Contexts which provide read-write access to the database. +/// +/// This trait is useful for writing reusable logic which is generic over the context type, +/// allowing it to be used from reducers and from transactions started by procedures and HTTP handlers. +/// +/// When operating on a concrete-typed [`ReducerContext`] or [`TxContext`], this trait is not necessary, +/// as the context's `db` field provides the same access. pub trait CtxDbWrite: CtxDbRead { fn db(&self) -> &Local; } From faa6a9313a5c426809672e73f0792e1559bb75c5 Mon Sep 17 00:00:00 2001 From: Kilian Strunz <93079615+kistz@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:07:58 +0200 Subject: [PATCH 07/15] Update crates/bindings/src/lib.rs Co-authored-by: Phoebe Goldman Signed-off-by: Kilian Strunz <93079615+kistz@users.noreply.github.com> --- crates/bindings/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index dde125413f6..1d448a5d96f 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1614,7 +1614,14 @@ impl CtxWithSender for TxContext { } } -/// This trait allows you to be generic over all contexts that allow to retrieve the [Timestamp] of calling them. +/// Contexts which can retrieve the current [`Timestamp`]. +/// +/// This trait is useful for writing reusable logic which is generic over the context type, +/// allowing it to be used from reducers, procedures, HTTP handlers, +/// and transactions started by procedures and HTTP handlers. +/// +/// When operating on a concrete-typed [`ReducerContext`], [`ProcedureContext`], [`HandlerContext`] or [`TxContext`], +/// this trait is not necessary, as the context's `timestamp` field provides the same access. pub trait CtxWithTimestamp { fn timestamp(&self) -> Timestamp; } From 228165534517fd411d1723af7c16d8af9fdd40e5 Mon Sep 17 00:00:00 2001 From: Kilian Strunz <93079615+kistz@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:08:21 +0200 Subject: [PATCH 08/15] Update crates/bindings/src/lib.rs Co-authored-by: Phoebe Goldman Signed-off-by: Kilian Strunz <93079615+kistz@users.noreply.github.com> --- crates/bindings/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 1d448a5d96f..1a724fc9638 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1638,7 +1638,13 @@ impl CtxWithTimestamp for TxContext { } } -/// This trait allows you to be generic over all contexts that allow to use the [HttpClient]. +/// Contexts which can perform outgoing HTTP requests. +/// +/// This type is useful for writing reusable logic which is generic over the context type, +/// allowing it to be used from procedures and HTTP handlers. +/// +/// When operating on a concrete-typed [`ProcedureContext`] or [`HandlerContext`], this trait is not necessary, +/// as the context's `http` field provides the same access. pub trait CtxWithHttp { fn http(&self) -> &HttpClient; } From f532a7ec6897cd35a3868e049c7244001c4e646b Mon Sep 17 00:00:00 2001 From: Kilian Strunz <93079615+kistz@users.noreply.github.com> Date: Sat, 27 Jun 2026 09:08:33 +0200 Subject: [PATCH 09/15] Update crates/bindings/src/lib.rs Co-authored-by: Phoebe Goldman Signed-off-by: Kilian Strunz <93079615+kistz@users.noreply.github.com> --- crates/bindings/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 1a724fc9638..146aead18b9 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1591,7 +1591,13 @@ impl CtxDbWrite for ReducerContext { } } -/// This trait allows you to be generic over all contexts that allow to retrieve the caller [Identity]. +/// Contexts which can retrieve the sender [`Identity`]. +/// +/// This trait is useful for writing reusable logic which is generic over the context type, +/// allowing it to be used from views, reducers, and transactions started by procedures and HTTP handlers. +/// +/// When operating on a concrete-typed [`ViewContext`], [`ReducerContext`] or [`TxContext`], this trait is not necessary, +/// as the context's inherent `sender` method provides the same access. pub trait CtxWithSender { fn sender(&self) -> Identity; } From 900b6488565da9c717cbe81aebe97a551ae2d947 Mon Sep 17 00:00:00 2001 From: "kistz (Kilian Strunz)" Date: Sat, 27 Jun 2026 09:28:30 +0200 Subject: [PATCH 10/15] apply applicable suggestions --- crates/bindings/src/lib.rs | 110 ++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 146aead18b9..43e207267d1 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1596,7 +1596,7 @@ impl CtxDbWrite for ReducerContext { /// This trait is useful for writing reusable logic which is generic over the context type, /// allowing it to be used from views, reducers, and transactions started by procedures and HTTP handlers. /// -/// When operating on a concrete-typed [`ViewContext`], [`ReducerContext`] or [`TxContext`], this trait is not necessary, +/// When operating on a concrete-typed [`ViewContext`], [`ReducerContext`], [`ProcedureContext`] or [`TxContext`], this trait is not necessary, /// as the context's inherent `sender` method provides the same access. pub trait CtxWithSender { fn sender(&self) -> Identity; @@ -1620,6 +1620,12 @@ impl CtxWithSender for TxContext { } } +impl CtxWithSender for ProcedureContext { + fn sender(&self) -> Identity { + self.sender + } +} + /// Contexts which can retrieve the current [`Timestamp`]. /// /// This trait is useful for writing reusable logic which is generic over the context type, @@ -1644,6 +1650,108 @@ impl CtxWithTimestamp for TxContext { } } +impl CtxWithTimestamp for ProcedureContext { + fn timestamp(&self) -> Timestamp { + self.timestamp + } +} + +impl CtxWithTimestamp for HandlerContext { + fn timestamp(&self) -> Timestamp { + self.timestamp + } +} + +/// Contexts which can retrieve the current [`AuthCtx`]. +/// +/// This trait is useful for writing reusable logic which is generic over the context type, +/// allowing it to be used from reducers and procedures. +/// +/// When operating on a concrete-typed [`ReducerContext`], [`ProcedureContext`], [`TxContext`], +/// this trait is not necessary, as the context's sender_auth method provides the same access. +pub trait CtxWithSenderAuth { + fn sender_auth(&self) -> &AuthCtx; +} + +impl CtxWithSenderAuth for ReducerContext { + fn sender_auth(&self) -> &AuthCtx { + self.sender_auth() + } +} + +impl CtxWithSenderAuth for TxContext { + fn sender_auth(&self) -> &AuthCtx { + self.0.sender_auth() + } +} + +/// Contexts which can enter a start a transaction with a [`TxContext`] inside the function. +/// +/// This trait is useful for writing reusable logic which is generic over the context type, +/// allowing it to be used from reducers and procedures. +/// +/// When operating on a concrete-typed [`HandlerContext`], [`ProcedureContext`], +/// this trait is not necessary, as the context's methods provide the same access. +pub trait CtxWithTxManagement { + fn with_tx(&mut self, body: impl Fn(&TxContext) -> T) -> T; + fn try_with_tx(&mut self, body: impl Fn(&TxContext) -> Result) -> Result; +} + +impl CtxWithTxManagement for ProcedureContext { + fn with_tx(&mut self, body: impl Fn(&TxContext) -> T) -> T { + self.with_tx(body) + } + + fn try_with_tx(&mut self, body: impl Fn(&TxContext) -> Result) -> Result { + self.try_with_tx(body) + } +} + +impl CtxWithTxManagement for HandlerContext { + fn with_tx(&mut self, body: impl Fn(&TxContext) -> T) -> T { + self.with_tx(body) + } + + fn try_with_tx(&mut self, body: impl Fn(&TxContext) -> Result) -> Result { + self.try_with_tx(body) + } +} + +/// Contexts which can retrieve the current [`StdbRng`] state. +/// +/// This trait is useful for writing reusable logic which is generic over the context type, +/// allowing it to be used from reducers and procedures. +/// +/// When operating on a concrete-typed [`HandlerContext`], [`ProcedureContext`], [`ReducerContext`], [`TxContext`] +/// this trait is not necessary, as the context's methods provide the same access. +pub trait CtxWithRng { + fn rng(&self) -> &StdbRng; +} + +impl CtxWithRng for ProcedureContext { + fn rng(&self) -> &StdbRng { + self.rng() + } +} + +impl CtxWithRng for HandlerContext { + fn rng(&self) -> &StdbRng { + self.rng() + } +} + +impl CtxWithRng for ReducerContext { + fn rng(&self) -> &StdbRng { + self.rng() + } +} + +impl CtxWithRng for TxContext { + fn rng(&self) -> &StdbRng { + self.0.rng() + } +} + /// Contexts which can perform outgoing HTTP requests. /// /// This type is useful for writing reusable logic which is generic over the context type, From 6d432277031bc4503b282843c9aada6f89c95b4d Mon Sep 17 00:00:00 2001 From: "kistz (Kilian Strunz)" Date: Sat, 27 Jun 2026 09:37:55 +0200 Subject: [PATCH 11/15] add missing random accessor --- crates/bindings/src/lib.rs | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 43e207267d1..3d826cc7cb1 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -24,11 +24,12 @@ pub use spacetimedb_query_builder as query_builder; #[cfg(feature = "unstable")] pub use client_visibility_filter::Filter; pub use log; +#[cfg(feature = "rand08")] +use rand::distributions::{Distribution, Standard}; #[cfg(feature = "rand")] pub use rand08 as rand; #[cfg(feature = "rand")] use rand08::RngCore; -#[cfg(feature = "rand08")] pub use rng::StdbRng; pub use sats::SpacetimeType; #[doc(hidden)] @@ -1726,30 +1727,61 @@ impl CtxWithTxManagement for HandlerContext { /// this trait is not necessary, as the context's methods provide the same access. pub trait CtxWithRng { fn rng(&self) -> &StdbRng; + fn random(&self) -> T + where + Standard: Distribution; } impl CtxWithRng for ProcedureContext { fn rng(&self) -> &StdbRng { self.rng() } + + fn random(&self) -> T + where + Standard: Distribution, + { + self.random() + } } impl CtxWithRng for HandlerContext { fn rng(&self) -> &StdbRng { self.rng() } + + fn random(&self) -> T + where + Standard: Distribution, + { + self.random() + } } impl CtxWithRng for ReducerContext { fn rng(&self) -> &StdbRng { self.rng() } + + fn random(&self) -> T + where + Standard: Distribution, + { + self.random() + } } impl CtxWithRng for TxContext { fn rng(&self) -> &StdbRng { self.0.rng() } + + fn random(&self) -> T + where + Standard: Distribution, + { + self.0.random() + } } /// Contexts which can perform outgoing HTTP requests. From 7c76554186471b7981103514fc79381c3200324a Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Mon, 29 Jun 2026 11:24:55 -0400 Subject: [PATCH 12/15] Tidy up feature flags Previous commits in this PR inadvertently broke compiling with `--no-default-features`. This was understandable, as our feature flags have been kinda a mess. This commit doesn't dramatically improve them, and they're still sorta a mess, but it does make `cargo check --no-default-features` of the bindings crate pass, with and without manually enabling the `rand` or `unstable` features. As part of this change, I've moved to uniform use of the `rand08` feature everywhere, removing all references to the `rand` feature from our code. The `rand` feature still exists as an alias for `rand08`. --- crates/bindings/src/http.rs | 14 ++++++------- crates/bindings/src/lib.rs | 40 ++++++++++++++++++++++++------------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/crates/bindings/src/http.rs b/crates/bindings/src/http.rs index ec476bba46e..54adbeee949 100644 --- a/crates/bindings/src/http.rs +++ b/crates/bindings/src/http.rs @@ -13,7 +13,7 @@ use crate::StdbRng; #[cfg(feature = "unstable")] use crate::{try_with_tx, with_tx, Timestamp, TxContext}; use bytes::Bytes; -#[cfg(all(feature = "rand", feature = "unstable"))] +#[cfg(all(feature = "rand08", feature = "unstable"))] use rand08::RngCore; #[cfg(feature = "unstable")] use spacetimedb_lib::db::raw_def::v10::MethodOrAny; @@ -22,10 +22,10 @@ use spacetimedb_lib::http as st_http; use spacetimedb_lib::http::{character_is_acceptable_for_route_path, ACCEPTABLE_ROUTE_PATH_CHARS_HUMAN_DESCRIPTION}; #[cfg(feature = "unstable")] use spacetimedb_lib::Identity; -#[cfg(all(feature = "unstable", feature = "rand"))] +#[cfg(all(feature = "unstable", feature = "rand08"))] use spacetimedb_lib::Uuid; use spacetimedb_lib::{bsatn, TimeDuration}; -#[cfg(all(feature = "unstable", feature = "rand"))] +#[cfg(all(feature = "unstable", feature = "rand08"))] use std::cell::Cell; #[cfg(all(feature = "unstable", feature = "rand08"))] use std::cell::OnceCell; @@ -109,7 +109,7 @@ pub struct HandlerContext { /// A counter used for generating UUIDv7 values. /// **Note:** must be 0..=u32::MAX - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] pub(crate) counter_uuid: Cell, } @@ -121,7 +121,7 @@ impl HandlerContext { http: HttpClient {}, #[cfg(feature = "rand08")] rng: OnceCell::new(), - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] counter_uuid: Cell::new(0), } } @@ -142,7 +142,7 @@ impl HandlerContext { } /// Create a new random [`Uuid`] `v4` using the built-in RNG. - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] pub fn new_uuid_v4(&self) -> anyhow::Result { let mut bytes = [0u8; 16]; self.rng().try_fill_bytes(&mut bytes)?; @@ -150,7 +150,7 @@ impl HandlerContext { } /// Create a new sortable [`Uuid`] `v7` using the built-in RNG, counter and timestamp. - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] pub fn new_uuid_v7(&self) -> anyhow::Result { let mut random_bytes = [0u8; 4]; self.rng().try_fill_bytes(&mut random_bytes)?; diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 3d826cc7cb1..0861d500eb3 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1,9 +1,11 @@ #![doc = include_str!("../README.md")] // ^ if you are working on docs, go read the top comment of README.md please. -use core::cell::{Cell, LazyCell, OnceCell, RefCell}; +use core::cell::{LazyCell, OnceCell, RefCell}; use core::ops::Deref; use spacetimedb_lib::bsatn; +#[cfg(feature = "rand08")] +use std::cell::Cell; use std::rc::Rc; #[cfg(feature = "unstable")] @@ -26,10 +28,11 @@ pub use client_visibility_filter::Filter; pub use log; #[cfg(feature = "rand08")] use rand::distributions::{Distribution, Standard}; -#[cfg(feature = "rand")] +#[cfg(feature = "rand08")] pub use rand08 as rand; -#[cfg(feature = "rand")] +#[cfg(feature = "rand08")] use rand08::RngCore; +#[cfg(feature = "rand08")] pub use rng::StdbRng; pub use sats::SpacetimeType; #[doc(hidden)] @@ -43,6 +46,9 @@ pub use spacetimedb_lib::ser::Serialize; pub use spacetimedb_lib::AlgebraicValue; pub use spacetimedb_lib::ConnectionId; // `FilterableValue` re-exported purely for rustdoc. +#[cfg(feature = "unstable")] +use crate::http::HandlerContext; +use crate::http::HttpClient; pub use spacetimedb_lib::FilterableValue; pub use spacetimedb_lib::Identity; pub use spacetimedb_lib::ScheduleAt; @@ -913,8 +919,6 @@ pub use spacetimedb_bindings_macro::view; pub struct QueryBuilder {} pub use query_builder::{Query, RawQuery}; -use crate::http::{HandlerContext, HttpClient}; - /// One of two possible types that can be passed as the first argument to a `#[view]`. /// The other is [`ViewContext`]. /// Use this type if the view does not depend on the caller's identity. @@ -1030,7 +1034,7 @@ pub struct ReducerContext { rng: std::cell::OnceCell, /// A counter used for generating UUIDv7 values. /// **Note:** must be 0..=u32::MAX - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] counter_uuid: Cell, } @@ -1045,7 +1049,7 @@ impl ReducerContext { sender_auth: AuthCtx::internal(), #[cfg(feature = "rand08")] rng: std::cell::OnceCell::new(), - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] counter_uuid: Cell::new(0), } } @@ -1060,7 +1064,7 @@ impl ReducerContext { sender_auth: AuthCtx::from_connection_id_opt(connection_id), #[cfg(feature = "rand08")] rng: std::cell::OnceCell::new(), - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] counter_uuid: Cell::new(0), } } @@ -1126,7 +1130,7 @@ impl ReducerContext { /// } /// # } /// ``` - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] pub fn new_uuid_v4(&self) -> anyhow::Result { let mut bytes = [0u8; 16]; self.rng().try_fill_bytes(&mut bytes)?; @@ -1148,7 +1152,7 @@ impl ReducerContext { /// } /// # } /// ``` - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] pub fn new_uuid_v7(&self) -> anyhow::Result { let mut random_bytes = [0u8; 4]; self.rng().try_fill_bytes(&mut random_bytes)?; @@ -1265,7 +1269,7 @@ pub struct ProcedureContext { /// A counter used for generating UUIDv7 values. /// **Note:** must be 0..=u32::MAX // Disabled when compiling without `rand`, as both v4 and v7 UUIDs have random components. - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] counter_uuid: Cell, } @@ -1278,7 +1282,7 @@ impl ProcedureContext { http: http::HttpClient {}, #[cfg(feature = "rand08")] rng: std::cell::OnceCell::new(), - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] counter_uuid: Cell::new(0), } } @@ -1412,7 +1416,7 @@ impl ProcedureContext { /// } /// # } /// ``` - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] pub fn new_uuid_v4(&self) -> anyhow::Result { let mut bytes = [0u8; 16]; self.rng().try_fill_bytes(&mut bytes)?; @@ -1434,7 +1438,7 @@ impl ProcedureContext { /// } /// # } /// ``` - #[cfg(feature = "rand")] + #[cfg(feature = "rand08")] pub fn new_uuid_v7(&self) -> anyhow::Result { let mut random_bytes = [0u8; 4]; self.rng().try_fill_bytes(&mut random_bytes)?; @@ -1657,6 +1661,7 @@ impl CtxWithTimestamp for ProcedureContext { } } +#[cfg(feature = "unstable")] impl CtxWithTimestamp for HandlerContext { fn timestamp(&self) -> Timestamp { self.timestamp @@ -1708,6 +1713,7 @@ impl CtxWithTxManagement for ProcedureContext { } } +#[cfg(feature = "unstable")] impl CtxWithTxManagement for HandlerContext { fn with_tx(&mut self, body: impl Fn(&TxContext) -> T) -> T { self.with_tx(body) @@ -1725,6 +1731,7 @@ impl CtxWithTxManagement for HandlerContext { /// /// When operating on a concrete-typed [`HandlerContext`], [`ProcedureContext`], [`ReducerContext`], [`TxContext`] /// this trait is not necessary, as the context's methods provide the same access. +#[cfg(feature = "rand08")] pub trait CtxWithRng { fn rng(&self) -> &StdbRng; fn random(&self) -> T @@ -1732,6 +1739,7 @@ pub trait CtxWithRng { Standard: Distribution; } +#[cfg(feature = "rand08")] impl CtxWithRng for ProcedureContext { fn rng(&self) -> &StdbRng { self.rng() @@ -1745,6 +1753,7 @@ impl CtxWithRng for ProcedureContext { } } +#[cfg(all(feature = "unstable", feature = "rand08"))] impl CtxWithRng for HandlerContext { fn rng(&self) -> &StdbRng { self.rng() @@ -1758,6 +1767,7 @@ impl CtxWithRng for HandlerContext { } } +#[cfg(feature = "rand08")] impl CtxWithRng for ReducerContext { fn rng(&self) -> &StdbRng { self.rng() @@ -1771,6 +1781,7 @@ impl CtxWithRng for ReducerContext { } } +#[cfg(feature = "rand08")] impl CtxWithRng for TxContext { fn rng(&self) -> &StdbRng { self.0.rng() @@ -1795,6 +1806,7 @@ pub trait CtxWithHttp { fn http(&self) -> &HttpClient; } +#[cfg(feature = "unstable")] impl CtxWithHttp for HandlerContext { fn http(&self) -> &HttpClient { &self.http From a626224d3b6504e24abbc111a28bc59c6e0360cd Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Mon, 29 Jun 2026 11:50:25 -0400 Subject: [PATCH 13/15] Remove unnecessary uses of `DbContext` trait The `sdk-test-procedure-concurrency` module (which was written by an LLM at my prompting, and in hindsight, under-reviewed), used the `DbContext` trait unnecessarily, rather than the `.db` field of `ReducerContext`. Now that the `DbContext` trait is deprecated in favor of the capability traits, this was raising a warning. I could have replaced with a use of `CtxDbWrite`, but it's not required at all; the code uses a concrete-typed `ReducerContext` and can just access the `.db` field. --- modules/sdk-test-procedure-concurrency/src/lib.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/modules/sdk-test-procedure-concurrency/src/lib.rs b/modules/sdk-test-procedure-concurrency/src/lib.rs index 4f99c3a422e..e8874309f8e 100644 --- a/modules/sdk-test-procedure-concurrency/src/lib.rs +++ b/modules/sdk-test-procedure-concurrency/src/lib.rs @@ -1,6 +1,4 @@ -use spacetimedb::{ - procedure, reducer, table, DbContext, ProcedureContext, ReducerContext, ScheduleAt, Table, TxContext, -}; +use spacetimedb::{procedure, reducer, table, ProcedureContext, ReducerContext, ScheduleAt, Table, TxContext}; use std::time::Duration; #[table(public, accessor = procedure_concurrency_row)] @@ -19,7 +17,7 @@ fn insert_procedure_concurrency_row(ctx: &TxContext, insertion_context: &str) { #[reducer] fn insert_reducer_row(ctx: &ReducerContext) { - ctx.db().procedure_concurrency_row().insert(ProcedureConcurrencyRow { + ctx.db.procedure_concurrency_row().insert(ProcedureConcurrencyRow { insertion_order: 0, insertion_context: "reducer".into(), }); @@ -82,7 +80,7 @@ struct ScheduledReducerRow { #[reducer] fn insert_scheduled_reducer(ctx: &ReducerContext, _schedule: ScheduledReducerRow) { - ctx.db().procedure_concurrency_row().insert(ProcedureConcurrencyRow { + ctx.db.procedure_concurrency_row().insert(ProcedureConcurrencyRow { insertion_order: 0, insertion_context: "scheduled_reducer".into(), }); @@ -130,11 +128,11 @@ fn scheduled_procedure_sleep_between_inserts(ctx: &mut ProcedureContext, _schedu #[reducer] fn schedule_procedure_then_reducer(ctx: &ReducerContext) { - ctx.db().scheduled_procedure_row().insert(ScheduledProcedureRow { + ctx.db.scheduled_procedure_row().insert(ScheduledProcedureRow { scheduled_id: 0, scheduled_at: ctx.timestamp.into(), }); - ctx.db().scheduled_reducer_row().insert(ScheduledReducerRow { + ctx.db.scheduled_reducer_row().insert(ScheduledReducerRow { scheduled_id: 0, scheduled_at: (ctx.timestamp + Duration::from_secs(2)).into(), }); From b7fd3194089b20f614100e65d8f3623e4a1202bd Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Mon, 29 Jun 2026 12:21:09 -0400 Subject: [PATCH 14/15] Remove more unnecessary uses of `DbContext` trait --- modules/keynote-benchmarks/src/lib.rs | 6 +++--- modules/sdk-test-procedure/src/lib.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/keynote-benchmarks/src/lib.rs b/modules/keynote-benchmarks/src/lib.rs index 3ba8394089c..70d68bc0152 100644 --- a/modules/keynote-benchmarks/src/lib.rs +++ b/modules/keynote-benchmarks/src/lib.rs @@ -1,5 +1,5 @@ use core::ops::AddAssign; -use spacetimedb::{log_stopwatch::LogStopwatch, rand::Rng, reducer, table, DbContext, ReducerContext, Table}; +use spacetimedb::{log_stopwatch::LogStopwatch, rand::Rng, reducer, table, ReducerContext, Table}; #[derive(Clone, Copy, Debug)] #[table(accessor = position, public)] @@ -39,7 +39,7 @@ fn init(ctx: &ReducerContext) { // Insert 10^6 randomized positions and velocities, // but with incrementing and corresponding ids. - let db = ctx.db(); + let db = &ctx.db; let mut rng = ctx.rng(); for id in 0..1_000_000 { let (x, y, z) = rng.r#gen(); @@ -71,7 +71,7 @@ fn update_positions_by_collect(ctx: &ReducerContext) { #[reducer] fn roundtrip(ctx: &ReducerContext) { // Warmup the index. - let id = ctx.db().velocity().id(); + let id = ctx.db.velocity().id(); for x in 0..10_000 { id.find(x); } diff --git a/modules/sdk-test-procedure/src/lib.rs b/modules/sdk-test-procedure/src/lib.rs index 95ae9b523b1..817be45ce61 100644 --- a/modules/sdk-test-procedure/src/lib.rs +++ b/modules/sdk-test-procedure/src/lib.rs @@ -1,6 +1,6 @@ use spacetimedb::{ - duration, procedure, reducer, table, DbContext, ProcedureContext, ReducerContext, ScheduleAt, SpacetimeType, Table, - Timestamp, TxContext, Uuid, + duration, procedure, reducer, table, ProcedureContext, ReducerContext, ScheduleAt, SpacetimeType, Table, Timestamp, + TxContext, Uuid, }; #[derive(SpacetimeType)] @@ -106,7 +106,7 @@ fn insert_with_tx_rollback(ctx: &mut ProcedureContext) { #[reducer] fn schedule_proc(ctx: &ReducerContext) { // Schedule the procedure to run in 1s. - ctx.db().scheduled_proc_table().insert(ScheduledProcTable { + ctx.db.scheduled_proc_table().insert(ScheduledProcTable { scheduled_id: 0, scheduled_at: duration!("1000ms").into(), // Store the timestamp at which this reducer was called. @@ -134,7 +134,7 @@ fn scheduled_proc(ctx: &mut ProcedureContext, data: ScheduledProcTable) { let ScheduledProcTable { reducer_ts, x, y, .. } = data; let procedure_ts = ctx.timestamp; ctx.with_tx(|ctx| { - ctx.db().proc_inserts_into().insert(ProcInsertsInto { + ctx.db.proc_inserts_into().insert(ProcInsertsInto { reducer_ts, procedure_ts, x, From 615661f2c4d8fbdedaae8f13de171ecb8f639286 Mon Sep 17 00:00:00 2001 From: Phoebe Goldman Date: Mon, 29 Jun 2026 12:34:08 -0400 Subject: [PATCH 15/15] Also gate doc links to `HandlerContext` behind `unstable` feature --- crates/bindings/src/http.rs | 2 +- crates/bindings/src/lib.rs | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/crates/bindings/src/http.rs b/crates/bindings/src/http.rs index 54adbeee949..7cf60a5af39 100644 --- a/crates/bindings/src/http.rs +++ b/crates/bindings/src/http.rs @@ -72,7 +72,7 @@ pub use spacetimedb_bindings_macro::http_handler as handler; /// Register a [`Router`](struct@Router) to route HTTP requests to handlers. /// -/// This should annotate a function of no arguments which returns a [`Router`](struct@router). +/// This should annotate a function of no arguments which returns a [`Router`](struct@Router). /// /// ```no_run /// # use spacetimedb::http::{handler, router, Request, Response, Body, HandlerContext, Router}; diff --git a/crates/bindings/src/lib.rs b/crates/bindings/src/lib.rs index 0861d500eb3..400d2d197d9 100644 --- a/crates/bindings/src/lib.rs +++ b/crates/bindings/src/lib.rs @@ -1601,8 +1601,8 @@ impl CtxDbWrite for ReducerContext { /// This trait is useful for writing reusable logic which is generic over the context type, /// allowing it to be used from views, reducers, and transactions started by procedures and HTTP handlers. /// -/// When operating on a concrete-typed [`ViewContext`], [`ReducerContext`], [`ProcedureContext`] or [`TxContext`], this trait is not necessary, -/// as the context's inherent `sender` method provides the same access. +/// When operating on a concrete-typed [`ViewContext`], [`ReducerContext`], [`ProcedureContext`] or [`TxContext`], +/// this trait is not necessary, as the context's inherent `sender` method provides the same access. pub trait CtxWithSender { fn sender(&self) -> Identity; } @@ -1637,7 +1637,9 @@ impl CtxWithSender for ProcedureContext { /// allowing it to be used from reducers, procedures, HTTP handlers, /// and transactions started by procedures and HTTP handlers. /// -/// When operating on a concrete-typed [`ReducerContext`], [`ProcedureContext`], [`HandlerContext`] or [`TxContext`], +/// When operating on a concrete-typed [`ReducerContext`], [`ProcedureContext`] +#[cfg_attr(feature = "unstable", doc = ", [`HandlerContext`]")] +/// or [`TxContext`], /// this trait is not necessary, as the context's `timestamp` field provides the same access. pub trait CtxWithTimestamp { fn timestamp(&self) -> Timestamp; @@ -1696,7 +1698,9 @@ impl CtxWithSenderAuth for TxContext { /// This trait is useful for writing reusable logic which is generic over the context type, /// allowing it to be used from reducers and procedures. /// -/// When operating on a concrete-typed [`HandlerContext`], [`ProcedureContext`], +/// When operating on a concrete-typed +#[cfg_attr(feature = "unstable", doc = "[`HandlerContext`] or")] +/// [`ProcedureContext`], /// this trait is not necessary, as the context's methods provide the same access. pub trait CtxWithTxManagement { fn with_tx(&mut self, body: impl Fn(&TxContext) -> T) -> T; @@ -1729,7 +1733,9 @@ impl CtxWithTxManagement for HandlerContext { /// This trait is useful for writing reusable logic which is generic over the context type, /// allowing it to be used from reducers and procedures. /// -/// When operating on a concrete-typed [`HandlerContext`], [`ProcedureContext`], [`ReducerContext`], [`TxContext`] +/// When operating on a concrete-typed +#[cfg_attr(feature = "unstable", doc = "[`HandlerContext`],")] +/// [`ProcedureContext`], [`ReducerContext`], [`TxContext`] /// this trait is not necessary, as the context's methods provide the same access. #[cfg(feature = "rand08")] pub trait CtxWithRng { @@ -1800,7 +1806,9 @@ impl CtxWithRng for TxContext { /// This type is useful for writing reusable logic which is generic over the context type, /// allowing it to be used from procedures and HTTP handlers. /// -/// When operating on a concrete-typed [`ProcedureContext`] or [`HandlerContext`], this trait is not necessary, +/// When operating on a concrete-typed [`ProcedureContext`] +#[cfg_attr(feature = "unstable", doc = "or [`HandlerContext`],")] +/// this trait is not necessary, /// as the context's `http` field provides the same access. pub trait CtxWithHttp { fn http(&self) -> &HttpClient;