From 07ee5d67f3ac8141b737c88243cc6226acc29014 Mon Sep 17 00:00:00 2001 From: Jason Larabie Date: Sat, 15 Nov 2025 11:42:55 -0800 Subject: [PATCH 01/38] Updated to regex for namespace troubles --- crates/bindings-csharp/Codegen/Module.cs | 10 ++++++---- .../client/module_bindings/SpacetimeDBClient.g.cs | 2 +- .../client/module_bindings/SpacetimeDBClient.g.cs | 2 +- .../client/module_bindings/SpacetimeDBClient.g.cs | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index d414d9ad106..80b3704b675 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -5,6 +5,7 @@ namespace SpacetimeDB.Codegen; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Text.RegularExpressions; using SpacetimeDB.Internal; using static Utils; @@ -997,10 +998,11 @@ public string GenerateViewDef(uint Index) => IsPublic: {{{IsPublic.ToString().ToLower()}}}, IsAnonymous: {{{IsAnonymous.ToString().ToLower()}}}, Params: [{{{MemberDeclaration.GenerateDefs(Parameters)}}}], - ReturnType: new {{{ReturnType.BSATNName.Replace( - "Module.", - "global::Module." - )}}}().GetAlgebraicType(registrar) + ReturnType: new {{{Regex.Replace( + ReturnType.BSATNName, + @"(?<=^|[<,\s])Module\.", + "global::Module." + )}}}().GetAlgebraicType(registrar) ); """; diff --git a/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs index e205e194f5e..977372c0e05 100644 --- a/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.7.0 (commit af3f85dcf4e95ec3a33b227dde9407a29871e152). +// This was generated using spacetimedb cli version 1.8.0 (commit 31a8d5f1540fb11aef7db32f62e19b238fdaa797). #nullable enable diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs index 7418a534cc3..e2c4e2755c9 100644 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.7.0 (commit f01df570c478b16793a4d3ab64fa972e270ce858). +// This was generated using spacetimedb cli version 1.8.0 (commit 31a8d5f1540fb11aef7db32f62e19b238fdaa797). #nullable enable diff --git a/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs index a60dd23de85..7267aa9e29a 100644 --- a/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.7.0 (commit f01df570c478b16793a4d3ab64fa972e270ce858). +// This was generated using spacetimedb cli version 1.8.0 (commit 31a8d5f1540fb11aef7db32f62e19b238fdaa797). #nullable enable From 82a3eaf0f4f3df6a20b9987f8a6c3afcaba730fc Mon Sep 17 00:00:00 2001 From: Jason Larabie Date: Sat, 15 Nov 2025 12:15:02 -0800 Subject: [PATCH 02/38] Fix linting issue --- crates/bindings-csharp/Codegen/Module.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 80b3704b675..1f792a6face 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -2,10 +2,10 @@ namespace SpacetimeDB.Codegen; using System.Collections.Immutable; using System.Linq; +using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Text.RegularExpressions; using SpacetimeDB.Internal; using static Utils; From 55e4c932e7bde4dc9e5c21dc04607c7ee2cbea75 Mon Sep 17 00:00:00 2001 From: Jason Larabie Date: Mon, 17 Nov 2025 09:25:44 -0800 Subject: [PATCH 03/38] Reviewed and we can remove the hacky global scope add for return type entirely --- crates/bindings-csharp/Codegen/Module.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 1f792a6face..04b4ce876bb 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -2,7 +2,6 @@ namespace SpacetimeDB.Codegen; using System.Collections.Immutable; using System.Linq; -using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -998,11 +997,7 @@ public string GenerateViewDef(uint Index) => IsPublic: {{{IsPublic.ToString().ToLower()}}}, IsAnonymous: {{{IsAnonymous.ToString().ToLower()}}}, Params: [{{{MemberDeclaration.GenerateDefs(Parameters)}}}], - ReturnType: new {{{Regex.Replace( - ReturnType.BSATNName, - @"(?<=^|[<,\s])Module\.", - "global::Module." - )}}}().GetAlgebraicType(registrar) + ReturnType: new {{{ReturnType.BSATNName}}}().GetAlgebraicType(registrar) ); """; From 594c4d100b7767320c96bc5512e29b2a24bfc91c Mon Sep 17 00:00:00 2001 From: rekhoff Date: Tue, 18 Nov 2025 10:05:11 -0800 Subject: [PATCH 04/38] Added a Views tests to the C# regression tests --- .../regression-tests/client/Program.cs | 9 +++ .../Reducers/ClientConnected.g.cs | 48 ++++++++++++ .../Reducers/CreateNewUser.g.cs | 73 +++++++++++++++++++ .../module_bindings/SpacetimeDBClient.g.cs | 7 +- .../Tables/GetUserByContext.g.cs | 27 +++++++ .../Tables/GetUserByString.g.cs | 27 +++++++ .../client/module_bindings/Tables/User.g.cs | 39 ++++++++++ .../client/module_bindings/Types/User.g.cs | 35 +++++++++ .../examples~/regression-tests/server/Lib.cs | 54 ++++++++++++++ 9 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/ClientConnected.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/CreateNewUser.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByContext.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByString.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/User.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Types/User.g.cs diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index 6db727e65cf..849d9a27d64 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -133,6 +133,15 @@ void OnSubscriptionApplied(SubscriptionEventContext context) ValidateBTreeIndexes(ctx); waiting--; }); + + // Views test + Log.Debug("Calling View"); + var viewRows = context.Db.GetUserByContext.Iter().FirstOrDefault(); + Debug.Assert(viewRows != null && viewRows.IdentityString == context.Identity?.ToString()); + + Log.Debug("Calling Anonymous View"); + var anonViewRows = context.Db.GetUserByString.RemoteQuery("Where IdentityString = identityStringExample"); + Debug.Assert(anonViewRows != null && anonViewRows.Result.Length > 0); } System.AppDomain.CurrentDomain.UnhandledException += (sender, args) => diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/ClientConnected.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/ClientConnected.g.cs new file mode 100644 index 00000000000..a23c796730b --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/ClientConnected.g.cs @@ -0,0 +1,48 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteReducers : RemoteBase + { + public delegate void ClientConnectedHandler(ReducerEventContext ctx); + public event ClientConnectedHandler? OnClientConnected; + + public bool InvokeClientConnected(ReducerEventContext ctx, Reducer.ClientConnected args) + { + if (OnClientConnected == null) + { + if (InternalOnUnhandledReducerError != null) + { + switch (ctx.Event.Status) + { + case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break; + case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break; + } + } + return false; + } + OnClientConnected( + ctx + ); + return true; + } + } + + public abstract partial class Reducer + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class ClientConnected : Reducer, IReducerArgs + { + string IReducerArgs.ReducerName => "ClientConnected"; + } + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/CreateNewUser.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/CreateNewUser.g.cs new file mode 100644 index 00000000000..c9b279fc4ec --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/CreateNewUser.g.cs @@ -0,0 +1,73 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteReducers : RemoteBase + { + public delegate void CreateNewUserHandler(ReducerEventContext ctx, string identityString); + public event CreateNewUserHandler? OnCreateNewUser; + + public void CreateNewUser(string identityString) + { + conn.InternalCallReducer(new Reducer.CreateNewUser(identityString), this.SetCallReducerFlags.CreateNewUserFlags); + } + + public bool InvokeCreateNewUser(ReducerEventContext ctx, Reducer.CreateNewUser args) + { + if (OnCreateNewUser == null) + { + if (InternalOnUnhandledReducerError != null) + { + switch (ctx.Event.Status) + { + case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break; + case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break; + } + } + return false; + } + OnCreateNewUser( + ctx, + args.IdentityString + ); + return true; + } + } + + public abstract partial class Reducer + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class CreateNewUser : Reducer, IReducerArgs + { + [DataMember(Name = "identityString")] + public string IdentityString; + + public CreateNewUser(string IdentityString) + { + this.IdentityString = IdentityString; + } + + public CreateNewUser() + { + this.IdentityString = ""; + } + + string IReducerArgs.ReducerName => "CreateNewUser"; + } + } + + public sealed partial class SetReducerFlags + { + internal CallReducerFlags CreateNewUserFlags; + public void CreateNewUser(CallReducerFlags flags) => CreateNewUserFlags = flags; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs index e2c4e2755c9..2eee0e4aa9e 100644 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.8.0 (commit 31a8d5f1540fb11aef7db32f62e19b238fdaa797). +// This was generated using spacetimedb cli version 1.8.0 (commit 9d57c4d4ea4b3fa5bcd2bad3cbd270d0b4c0bf2f). #nullable enable @@ -24,6 +24,7 @@ public sealed partial class RemoteTables : RemoteTablesBase public RemoteTables(DbConnection conn) { AddTable(ExampleData = new(conn)); + AddTable(User = new(conn)); } } @@ -468,6 +469,8 @@ protected override Reducer ToReducer(TransactionUpdate update) return update.ReducerCall.ReducerName switch { "Add" => BSATNHelpers.Decode(encodedArgs), + "ClientConnected" => BSATNHelpers.Decode(encodedArgs), + "CreateNewUser" => BSATNHelpers.Decode(encodedArgs), "Delete" => BSATNHelpers.Decode(encodedArgs), "ThrowError" => BSATNHelpers.Decode(encodedArgs), "" => throw new SpacetimeDBEmptyReducerNameException("Reducer name is empty"), @@ -493,6 +496,8 @@ protected override bool Dispatch(IReducerEventContext context, Reducer reducer) return reducer switch { Reducer.Add args => Reducers.InvokeAdd(eventContext, args), + Reducer.ClientConnected args => Reducers.InvokeClientConnected(eventContext, args), + Reducer.CreateNewUser args => Reducers.InvokeCreateNewUser(eventContext, args), Reducer.Delete args => Reducers.InvokeDelete(eventContext, args), Reducer.ThrowError args => Reducers.InvokeThrowError(eventContext, args), _ => throw new ArgumentOutOfRangeException("Reducer", $"Unknown reducer {reducer}") diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByContext.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByContext.g.cs new file mode 100644 index 00000000000..80e7389bbf6 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByContext.g.cs @@ -0,0 +1,27 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.BSATN; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteTables + { + public sealed class GetUserByContextHandle : RemoteTableHandle + { + protected override string RemoteTableName => "GetUserByContext"; + + internal GetUserByContextHandle(DbConnection conn) : base(conn) + { + } + } + + public readonly GetUserByContextHandle GetUserByContext; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByString.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByString.g.cs new file mode 100644 index 00000000000..2bea2f1e1b5 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByString.g.cs @@ -0,0 +1,27 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.BSATN; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteTables + { + public sealed class GetUserByStringHandle : RemoteTableHandle + { + protected override string RemoteTableName => "GetUserByString"; + + internal GetUserByStringHandle(DbConnection conn) : base(conn) + { + } + } + + public readonly GetUserByStringHandle GetUserByString; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/User.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/User.g.cs new file mode 100644 index 00000000000..c29f27e2d59 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/User.g.cs @@ -0,0 +1,39 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.BSATN; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteTables + { + public sealed class UserHandle : RemoteTableHandle + { + protected override string RemoteTableName => "User"; + + public sealed class IdentityStringUniqueIndex : UniqueIndexBase + { + protected override string GetKey(User row) => row.IdentityString; + + public IdentityStringUniqueIndex(UserHandle table) : base(table) { } + } + + public readonly IdentityStringUniqueIndex IdentityString; + + internal UserHandle(DbConnection conn) : base(conn) + { + IdentityString = new(this); + } + + protected override object GetPrimaryKey(User row) => row.IdentityString; + } + + public readonly UserHandle User; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/User.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/User.g.cs new file mode 100644 index 00000000000..502d00c010e --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/User.g.cs @@ -0,0 +1,35 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + [SpacetimeDB.Type] + [DataContract] + public sealed partial class User + { + [DataMember(Name = "IdentityString")] + public string IdentityString; + [DataMember(Name = "GeneratedByConnectedClient")] + public bool GeneratedByConnectedClient; + + public User( + string IdentityString, + bool GeneratedByConnectedClient + ) + { + this.IdentityString = IdentityString; + this.GeneratedByConnectedClient = GeneratedByConnectedClient; + } + + public User() + { + this.IdentityString = ""; + } + } +} diff --git a/sdks/csharp/examples~/regression-tests/server/Lib.cs b/sdks/csharp/examples~/regression-tests/server/Lib.cs index 0cf7f3cfef0..259ce739fcd 100644 --- a/sdks/csharp/examples~/regression-tests/server/Lib.cs +++ b/sdks/csharp/examples~/regression-tests/server/Lib.cs @@ -14,6 +14,14 @@ public partial struct ExampleData [SpacetimeDB.Index.BTree] public uint Indexed; } + + [SpacetimeDB.Table(Name = "User", Public = true)] + public partial struct User + { + [PrimaryKey] + public string IdentityString; + public bool GeneratedByConnectedClient; + } [SpacetimeDB.View(Name = "GetExampleDataById", Public = true)] public static ExampleData? GetExampleDataById(ViewContext ctx)//, uint id) @@ -26,6 +34,18 @@ public partial struct ExampleData { return ctx.Db.ExampleData.Id.Find(0); } + + [SpacetimeDB.View(Name = "GetUserByContext", Public = true)] + public static User? GetUserByContext(ViewContext ctx) + { + return ctx.Db.User.IdentityString.Find(ctx.Sender.ToString()); + } + + [SpacetimeDB.View(Name = "GetUserByString", Public = true)] + public static User? GetUserByString(AnonymousViewContext ctx) //, string identityString) + { + return ctx.Db.User.IdentityString.Find("identityStringExample"); + } [SpacetimeDB.Reducer] public static void Delete(ReducerContext ctx, uint id) @@ -44,4 +64,38 @@ public static void ThrowError(ReducerContext ctx, string error) { throw new Exception(error); } + + [SpacetimeDB.Reducer] + public static void CreateNewUser(ReducerContext ctx, string identityString) + { + ctx.Db.User.Insert( + new User + { + IdentityString = identityString, + GeneratedByConnectedClient = false, + } + ); + } + + [SpacetimeDB.Reducer(ReducerKind.ClientConnected)] + public static void ClientConnected(ReducerContext ctx) + { + Log.Info($"Connect {ctx.Sender}"); + + if (ctx.Db.User.IdentityString.Find(ctx.Sender.ToString()!) is User thisUser) + { + ctx.Db.User.IdentityString.Update(thisUser); + } + else + { + // If this is a new User, create a `User` object for the `IdentityString`, + ctx.Db.User.Insert( + new User + { + IdentityString = ctx.Sender.ToString()!, + GeneratedByConnectedClient = true, + } + ); + } + } } From 7a9c0fe83a2ee3bb60ccb7de3cfee35db3f33b11 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Tue, 18 Nov 2025 12:41:15 -0800 Subject: [PATCH 05/38] client codegen fixes for views --- crates/codegen/src/csharp.rs | 8 +- crates/codegen/src/rust.rs | 74 ++++++++------- crates/codegen/src/typescript.rs | 45 ++++++--- crates/codegen/src/unrealcpp.rs | 44 ++++----- crates/codegen/src/util.rs | 21 +++- .../snapshots/codegen__codegen_csharp.snap | 1 + .../snapshots/codegen__codegen_rust.snap | 8 ++ .../codegen__codegen_typescript.snap | 11 +++ modules/sdk-test-cs/Lib.cs | 41 +++++--- modules/sdk-test/src/lib.rs | 7 +- .../test-client/src/module_bindings/mod.rs | 2 +- .../src/module_bindings/my_user_table.rs | 95 +++++++++++++++++++ 12 files changed, 268 insertions(+), 89 deletions(-) create mode 100644 sdks/rust/tests/test-client/src/module_bindings/my_user_table.rs diff --git a/crates/codegen/src/csharp.rs b/crates/codegen/src/csharp.rs index e81035aa594..5e9eab5763e 100644 --- a/crates/codegen/src/csharp.rs +++ b/crates/codegen/src/csharp.rs @@ -7,8 +7,8 @@ use std::ops::Deref; use super::code_indenter::CodeIndenter; use super::Lang; use crate::util::{ - collect_case, is_reducer_invokable, iter_indexes, iter_reducers, iter_tables, print_auto_generated_file_comment, - print_auto_generated_version_comment, type_ref_name, + collect_case, is_reducer_invokable, iter_indexes, iter_reducers, iter_table_names_and_types, + print_auto_generated_file_comment, print_auto_generated_version_comment, type_ref_name, }; use crate::{indent_scope, OutputFile}; use convert_case::{Case, Casing}; @@ -745,11 +745,11 @@ impl Lang for Csharp<'_> { indented_block(&mut output, |output| { writeln!(output, "public RemoteTables(DbConnection conn)"); indented_block(output, |output| { - for table in iter_tables(module) { + for (table_name, _) in iter_table_names_and_types(module) { writeln!( output, "AddTable({} = new(conn));", - table.name.deref().to_case(Case::Pascal) + table_name.deref().to_case(Case::Pascal) ); } }); diff --git a/crates/codegen/src/rust.rs b/crates/codegen/src/rust.rs index a968ec7826a..0d9fa85b973 100644 --- a/crates/codegen/src/rust.rs +++ b/crates/codegen/src/rust.rs @@ -2,8 +2,8 @@ use super::code_indenter::{CodeIndenter, Indenter}; use super::util::{collect_case, iter_reducers, print_lines, type_ref_name}; use super::Lang; use crate::util::{ - iter_procedures, iter_tables, iter_types, iter_unique_cols, print_auto_generated_file_comment, - print_auto_generated_version_comment, + iter_procedures, iter_table_names_and_types, iter_tables, iter_types, iter_unique_cols, iter_views, + print_auto_generated_file_comment, print_auto_generated_version_comment, }; use crate::OutputFile; use convert_case::{Case, Casing}; @@ -930,12 +930,13 @@ fn reducer_flags_trait_name(reducer: &ReducerDef) -> String { format!("set_flags_for_{}", reducer_function_name(reducer)) } -/// Iterate over all of the Rust `mod`s for types, reducers and tables in the `module`. +/// Iterate over all of the Rust `mod`s for types, reducers, views, and tables in the `module`. fn iter_module_names(module: &ModuleDef) -> impl Iterator + '_ { itertools::chain!( iter_types(module).map(|ty| type_module_name(&ty.name)), iter_reducers(module).map(|r| reducer_module_name(&r.name)), iter_tables(module).map(|tbl| table_module_name(&tbl.name)), + iter_views(module).map(|view| table_module_name(&view.name)), iter_procedures(module).map(|proc| procedure_module_name(&proc.name)), ) } @@ -954,8 +955,8 @@ fn print_module_reexports(module: &ModuleDef, out: &mut Indenter) { let type_name = collect_case(Case::Pascal, ty.name.name_segments()); writeln!(out, "pub use {mod_name}::{type_name};") } - for table in iter_tables(module) { - let mod_name = table_module_name(&table.name); + for (table_name, _) in iter_table_names_and_types(module) { + let mod_name = table_module_name(table_name); // TODO: More precise reexport: we want: // - The trait name. // - The insert, delete and possibly update callback ids. @@ -1113,12 +1114,12 @@ fn print_db_update_defn(module: &ModuleDef, out: &mut Indenter) { out.delimited_block( "pub struct DbUpdate {", |out| { - for table in iter_tables(module) { + for (table_name, product_type_ref) in iter_table_names_and_types(module) { writeln!( out, "{}: __sdk::TableUpdate<{}>,", - table_method_name(&table.name), - type_ref_name(module, table.product_type_ref), + table_method_name(table_name), + type_ref_name(module, product_type_ref), ); } }, @@ -1137,13 +1138,13 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate { match &table_update.table_name[..] { ", |out| { - for table in iter_tables(module) { + for (table_name, _) in iter_table_names_and_types(module) { writeln!( out, "{:?} => db_update.{}.append({}::parse_table_update(table_update)?),", - table.name.deref(), - table_method_name(&table.name), - table_module_name(&table.name), + table_name.deref(), + table_method_name(table_name), + table_module_name(table_name), ); } }, @@ -1181,21 +1182,28 @@ impl __sdk::InModule for DbUpdate {{ let mut diff = AppliedDiff::default(); ", |out| { - for table in iter_tables(module) { - let with_updates = table - .primary_key - .map(|col| { - let pk_field = table.get_column(col).unwrap().name.deref().to_case(Case::Snake); - format!(".with_updates_by_pk(|row| &row.{pk_field})") - }) - .unwrap_or_default(); - - let field_name = table_method_name(&table.name); + for (table_name, product_type_ref, with_updates) in itertools::chain!( + iter_tables(module).map(|table| { + ( + &table.name, + table.product_type_ref, + table + .primary_key + .map(|col| { + let pk_field = table.get_column(col).unwrap().name.deref().to_case(Case::Snake); + format!(".with_updates_by_pk(|row| &row.{pk_field})") + }) + .unwrap_or_default(), + ) + }), + iter_views(module).map(|view| (&view.name, view.product_type_ref, "".into())) + ) { + let field_name = table_method_name(table_name); writeln!( out, "diff.{field_name} = cache.apply_diff_to_table::<{}>({:?}, &self.{field_name}){with_updates};", - type_ref_name(module, table.product_type_ref), - table.name.deref(), + type_ref_name(module, product_type_ref), + table_name.deref(), ); } }, @@ -1215,12 +1223,12 @@ fn print_applied_diff_defn(module: &ModuleDef, out: &mut Indenter) { out.delimited_block( "pub struct AppliedDiff<'r> {", |out| { - for table in iter_tables(module) { + for (table_name, product_type_ref) in iter_table_names_and_types(module) { writeln!( out, "{}: __sdk::TableAppliedDiff<'r, {}>,", - table_method_name(&table.name), - type_ref_name(module, table.product_type_ref), + table_method_name(table_name), + type_ref_name(module, product_type_ref), ); } // Also write a `PhantomData` field which uses the lifetime `r`, @@ -1248,13 +1256,13 @@ impl __sdk::InModule for AppliedDiff<'_> {{ out.delimited_block( "fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks) {", |out| { - for table in iter_tables(module) { + for (table_name, product_type_ref) in iter_table_names_and_types(module) { writeln!( out, "callbacks.invoke_table_row_callbacks::<{}>({:?}, &self.{}, event);", - type_ref_name(module, table.product_type_ref), - table.name.deref(), - table_method_name(&table.name), + type_ref_name(module, product_type_ref), + table_name.deref(), + table_method_name(table_name), ); } }, @@ -1290,8 +1298,8 @@ type SubscriptionHandle = SubscriptionHandle; out.delimited_block( "fn register_tables(client_cache: &mut __sdk::ClientCache) {", |out| { - for table in iter_tables(module) { - writeln!(out, "{}::register_table(client_cache);", table_module_name(&table.name)); + for (table_name, _) in iter_table_names_and_types(module) { + writeln!(out, "{}::register_table(client_cache);", table_module_name(table_name)); } }, "}\n", diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index 135c5852c67..1bd20e018d2 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -1,6 +1,6 @@ use crate::util::{ - is_reducer_invokable, iter_reducers, iter_tables, iter_types, iter_unique_cols, - print_auto_generated_version_comment, + is_reducer_invokable, iter_reducers, iter_table_names_and_types, iter_tables, iter_types, iter_unique_cols, + iter_views, print_auto_generated_version_comment, }; use crate::{indent_scope, OutputFile}; @@ -341,10 +341,9 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) writeln!(out); writeln!(out, "// Import and reexport all table handle types"); - for table in iter_tables(module) { - let table_name = &table.name; + for (table_name, _) in iter_table_names_and_types(module) { let table_module_name = table_module_name(table_name) + ".ts"; - let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); + let table_name_pascalcase = table_name.deref().to_case(Case::Pascal); let table_handle = table_name_pascalcase.clone() + "TableHandle"; writeln!(out, "import {{ {table_handle} }} from \"./{table_module_name}\";"); writeln!(out, "export {{ {table_handle} }};"); @@ -366,15 +365,31 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) out.indent(1); writeln!(out, "tables: {{"); out.indent(1); - for table in iter_tables(module) { - let type_ref = table.product_type_ref; + for (table_name, product_type_ref, schema) in itertools::chain!( + iter_tables(module).map(|def| { + ( + &def.name, + def.product_type_ref, + TableSchema::from_module_def(module, def, (), 0.into()), + ) + }), + iter_views(module).map(|def| { + ( + &def.name, + def.product_type_ref, + TableSchema::from_view_def_for_codegen(module, def), + ) + }) + ) { + let table_name = table_name.deref(); + let type_ref = product_type_ref; let row_type = type_ref_name(module, type_ref); - let schema = TableSchema::from_module_def(module, table, (), 0.into()) + let schema = schema .validated() .expect("Failed to generate table due to validation errors"); - writeln!(out, "{}: {{", table.name); + writeln!(out, "{}: {{", table_name); out.indent(1); - writeln!(out, "tableName: \"{}\" as const,", table.name); + writeln!(out, "tableName: \"{}\" as const,", table_name); writeln!(out, "rowType: {row_type}.getTypeScriptAlgebraicType(),"); if let Some(pk) = schema.pk() { // This is left here so we can release the codegen change before releasing a new @@ -612,13 +627,13 @@ fn print_remote_tables(module: &ModuleDef, out: &mut Indenter) { out.indent(1); writeln!(out, "constructor(private connection: __DbConnectionImpl) {{}}"); - for table in iter_tables(module) { + for (table_name, product_type_ref) in iter_table_names_and_types(module) { writeln!(out); - let table_name = table.name.deref(); - let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); - let table_name_camelcase = table.name.deref().to_case(Case::Camel); + let table_name = table_name.deref(); + let table_name_pascalcase = table_name.to_case(Case::Pascal); + let table_name_camelcase = table_name.to_case(Case::Camel); let table_handle = table_name_pascalcase.clone() + "TableHandle"; - let type_ref = table.product_type_ref; + let type_ref = product_type_ref; let row_type = type_ref_name(module, type_ref); writeln!(out, "get {table_name_camelcase}(): {table_handle}<'{table_name}'> {{"); out.with_indent(|out| { diff --git a/crates/codegen/src/unrealcpp.rs b/crates/codegen/src/unrealcpp.rs index 280b1be4475..0c473d9656d 100644 --- a/crates/codegen/src/unrealcpp.rs +++ b/crates/codegen/src/unrealcpp.rs @@ -1,7 +1,8 @@ //! Autogenerated Unreal‑C++ code‑gen backend for SpacetimeDB CLI use crate::code_indenter::CodeIndenter; use crate::util::{ - collect_case, fmt_fn, iter_tables, print_auto_generated_file_comment, print_auto_generated_version_comment, + collect_case, fmt_fn, iter_table_names_and_types, print_auto_generated_file_comment, + print_auto_generated_version_comment, }; use crate::util::{iter_indexes, iter_reducers}; use crate::Lang; @@ -683,8 +684,8 @@ impl Lang for UnrealCpp<'_> { writeln!(client_h); writeln!(client_h, "/** Forward declaration for tables */"); - for table in iter_tables(module) { - let table_pascal = type_ref_name(module, table.product_type_ref); + for (_, product_type_ref) in iter_table_names_and_types(module) { + let table_pascal = type_ref_name(module, product_type_ref); writeln!(client_h, "class U{table_pascal}Table;"); } writeln!(client_h, "/***/"); @@ -774,12 +775,11 @@ impl Lang for UnrealCpp<'_> { }); // Build table includes - let table_includes: Vec = module - .tables() - .map(|table| { + let table_includes: Vec = iter_table_names_and_types(module) + .map(|(_, product_type_ref)| { format!( "ModuleBindings/Tables/{}Table.g.h", - type_ref_name(module, table.product_type_ref) + type_ref_name(module, product_type_ref) ) }) .collect(); @@ -1856,14 +1856,14 @@ fn generate_remote_tables_class(output: &mut UnrealCppAutogen, module: &ModuleDe writeln!(output); // Generate table handle properties - for table in module.tables() { - let table_pascal = type_ref_name(module, table.product_type_ref); + for (table_name, product_type_ref) in iter_table_names_and_types(module) { + let table_pascal = type_ref_name(module, product_type_ref); writeln!(output, " UPROPERTY(BlueprintReadOnly, Category=\"SpacetimeDB\")"); writeln!( output, " U{table_pascal}Table* {};", - table.name.deref().to_case(Case::Pascal) + table_name.deref().to_case(Case::Pascal) ); writeln!(output); } @@ -2357,16 +2357,16 @@ fn generate_client_implementation(output: &mut UnrealCppAutogen, module: &Module writeln!(output, "\tReducers->SetCallReducerFlags = SetReducerFlags;"); writeln!(output, "\tReducers->Conn = this;"); writeln!(output); - for table in module.tables() { - let table_pascal = type_ref_name(module, table.product_type_ref); - let table_name = table.name.deref(); + for (table_name, product_type_ref) in iter_table_names_and_types(module) { + let table_pascal = type_ref_name(module, product_type_ref); + let table_name = table_name.deref(); writeln!( output, "\tRegisterTable(TEXT(\"{}\"), Db->{});", table_pascal, table_pascal, table_name, - table.name.deref().to_case(Case::Pascal) + table_name.to_case(Case::Pascal) ); } writeln!(output, "}}"); @@ -2412,23 +2412,23 @@ fn generate_client_implementation(output: &mut UnrealCppAutogen, module: &Module writeln!(output, "{{"); writeln!(output); writeln!(output, "\t/** Creating tables */"); - for table in module.tables() { - let table_pascal = type_ref_name(module, table.product_type_ref); + for (table_name, product_type_ref) in iter_table_names_and_types(module) { + let table_pascal = type_ref_name(module, product_type_ref); writeln!( output, "\t{} = NewObject(this);", - table.name.deref().to_case(Case::Pascal), + table_name.deref().to_case(Case::Pascal), table_pascal ); } writeln!(output, "\t/**/"); writeln!(output); writeln!(output, "\t/** Initialization */"); - for table in module.tables() { + for (table_name, _) in iter_table_names_and_types(module) { writeln!( output, "\t{}->PostInitialize();", - table.name.deref().to_case(Case::Pascal) + table_name.deref().to_case(Case::Pascal) ); } writeln!(output, "\t/**/"); @@ -3095,10 +3095,8 @@ fn collect_optional_types(module: &ModuleDef) -> HashSet { } // Collect from all tables - for table in module.tables() { - let product_type = module.typespace_for_generate()[table.product_type_ref] - .as_product() - .unwrap(); + for (_, product_type_ref) in iter_table_names_and_types(module) { + let product_type = module.typespace_for_generate()[product_type_ref].as_product().unwrap(); for (_, field_ty) in &product_type.elements { collect_from_type(module, field_ty, &mut optional_types); } diff --git a/crates/codegen/src/util.rs b/crates/codegen/src/util.rs index 1065fc39152..bbe4eac688a 100644 --- a/crates/codegen/src/util.rs +++ b/crates/codegen/src/util.rs @@ -12,8 +12,8 @@ use spacetimedb_lib::sats::layout::PrimitiveType; use spacetimedb_lib::version; use spacetimedb_lib::{db::raw_def::v9::Lifecycle, sats::AlgebraicTypeRef}; use spacetimedb_primitives::ColList; -use spacetimedb_schema::type_for_generate::ProductTypeDef; use spacetimedb_schema::{def::ProcedureDef, schema::TableSchema}; +use spacetimedb_schema::{def::ViewDef, type_for_generate::ProductTypeDef}; use spacetimedb_schema::{ def::{IndexDef, TableDef, TypeDef}, type_for_generate::TypespaceForGenerate, @@ -112,6 +112,25 @@ pub(super) fn iter_tables(module: &ModuleDef) -> impl Iterator module.tables().sorted_by_key(|table| &table.name) } +/// Iterate over all the table names defined by the module (in alphabetical order) and their product types. +/// Note, this also includes view names because from the perspective of codegen, views are tables. +/// +/// Sorting is necessary to have deterministic reproducible codegen. +pub(super) fn iter_table_names_and_types(module: &ModuleDef) -> impl Iterator { + module + .tables() + .map(|def| (&def.name, def.product_type_ref)) + .chain(module.views().map(|def| (&def.name, def.product_type_ref))) + .sorted_by_key(|(name, _)| *name) +} + +/// Iterate over all the [`ViewDef`]s defined by the module, in alphabetical order by name. +/// +/// Sorting is necessary to have deterministic reproducible codegen. +pub(super) fn iter_views(module: &ModuleDef) -> impl Iterator { + module.views().sorted_by_key(|view| &view.name) +} + pub(super) fn iter_unique_cols<'a>( typespace: &'a TypespaceForGenerate, schema: &'a TableSchema, diff --git a/crates/codegen/tests/snapshots/codegen__codegen_csharp.snap b/crates/codegen/tests/snapshots/codegen__codegen_csharp.snap index 4d2796d4811..38e8c9eafac 100644 --- a/crates/codegen/tests/snapshots/codegen__codegen_csharp.snap +++ b/crates/codegen/tests/snapshots/codegen__codegen_csharp.snap @@ -1011,6 +1011,7 @@ namespace SpacetimeDB { AddTable(HasSpecialStuff = new(conn)); AddTable(LoggedOutPlayer = new(conn)); + AddTable(MyPlayer = new(conn)); AddTable(Person = new(conn)); AddTable(PkMultiIdentity = new(conn)); AddTable(Player = new(conn)); diff --git a/crates/codegen/tests/snapshots/codegen__codegen_rust.snap b/crates/codegen/tests/snapshots/codegen__codegen_rust.snap index a334dec7a9c..995543ac4fa 100644 --- a/crates/codegen/tests/snapshots/codegen__codegen_rust.snap +++ b/crates/codegen/tests/snapshots/codegen__codegen_rust.snap @@ -1421,6 +1421,7 @@ pub mod test_a_table; pub mod test_d_table; pub mod test_e_table; pub mod test_f_table; +pub mod my_player_table; pub mod return_value_procedure; pub mod sleep_one_second_procedure; @@ -1442,6 +1443,7 @@ pub use namespace_test_c_type::NamespaceTestC; pub use namespace_test_f_type::NamespaceTestF; pub use has_special_stuff_table::*; pub use logged_out_player_table::*; +pub use my_player_table::*; pub use person_table::*; pub use pk_multi_identity_table::*; pub use player_table::*; @@ -1568,6 +1570,7 @@ fn try_from(value: __ws::ReducerCallInfo<__ws::BsatnFormat>) -> __sdk::Result, logged_out_player: __sdk::TableUpdate, + my_player: __sdk::TableUpdate, person: __sdk::TableUpdate, pk_multi_identity: __sdk::TableUpdate, player: __sdk::TableUpdate, @@ -1590,6 +1593,7 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate { "has_special_stuff" => db_update.has_special_stuff.append(has_special_stuff_table::parse_table_update(table_update)?), "logged_out_player" => db_update.logged_out_player.append(logged_out_player_table::parse_table_update(table_update)?), + "my_player" => db_update.my_player.append(my_player_table::parse_table_update(table_update)?), "person" => db_update.person.append(person_table::parse_table_update(table_update)?), "pk_multi_identity" => db_update.pk_multi_identity.append(pk_multi_identity_table::parse_table_update(table_update)?), "player" => db_update.player.append(player_table::parse_table_update(table_update)?), @@ -1634,6 +1638,7 @@ impl __sdk::DbUpdate for DbUpdate { diff.test_d = cache.apply_diff_to_table::("test_d", &self.test_d); diff.test_e = cache.apply_diff_to_table::("test_e", &self.test_e).with_updates_by_pk(|row| &row.id); diff.test_f = cache.apply_diff_to_table::("test_f", &self.test_f); + diff.my_player = cache.apply_diff_to_table::("my_player", &self.my_player); diff } @@ -1645,6 +1650,7 @@ impl __sdk::DbUpdate for DbUpdate { pub struct AppliedDiff<'r> { has_special_stuff: __sdk::TableAppliedDiff<'r, HasSpecialStuff>, logged_out_player: __sdk::TableAppliedDiff<'r, Player>, + my_player: __sdk::TableAppliedDiff<'r, Player>, person: __sdk::TableAppliedDiff<'r, Person>, pk_multi_identity: __sdk::TableAppliedDiff<'r, PkMultiIdentity>, player: __sdk::TableAppliedDiff<'r, Player>, @@ -1667,6 +1673,7 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks) { callbacks.invoke_table_row_callbacks::("has_special_stuff", &self.has_special_stuff, event); callbacks.invoke_table_row_callbacks::("logged_out_player", &self.logged_out_player, event); + callbacks.invoke_table_row_callbacks::("my_player", &self.my_player, event); callbacks.invoke_table_row_callbacks::("person", &self.person, event); callbacks.invoke_table_row_callbacks::("pk_multi_identity", &self.pk_multi_identity, event); callbacks.invoke_table_row_callbacks::("player", &self.player, event); @@ -2395,6 +2402,7 @@ impl __sdk::SpacetimeModule for RemoteModule { fn register_tables(client_cache: &mut __sdk::ClientCache) { has_special_stuff_table::register_table(client_cache); logged_out_player_table::register_table(client_cache); + my_player_table::register_table(client_cache); person_table::register_table(client_cache); pk_multi_identity_table::register_table(client_cache); player_table::register_table(client_cache); diff --git a/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap b/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap index 799e85147f9..c55a5a64fd6 100644 --- a/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap +++ b/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap @@ -862,6 +862,8 @@ import { HasSpecialStuffTableHandle } from "./has_special_stuff_table.ts"; export { HasSpecialStuffTableHandle }; import { LoggedOutPlayerTableHandle } from "./logged_out_player_table.ts"; export { LoggedOutPlayerTableHandle }; +import { MyPlayerTableHandle } from "./my_player_table.ts"; +export { MyPlayerTableHandle }; import { PersonTableHandle } from "./person_table.ts"; export { PersonTableHandle }; import { PkMultiIdentityTableHandle } from "./pk_multi_identity_table.ts"; @@ -997,6 +999,10 @@ const REMOTE_MODULE = { tableName: "test_f" as const, rowType: TestFoobar.getTypeScriptAlgebraicType(), }, + my_player: { + tableName: "my_player" as const, + rowType: Player.getTypeScriptAlgebraicType(), + }, }, reducers: { add: { @@ -1383,6 +1389,11 @@ export class RemoteTables { return new LoggedOutPlayerTableHandle((this.connection as unknown as { clientCache: __ClientCache }).clientCache.getOrCreateTable(REMOTE_MODULE.tables.logged_out_player)); } + get myPlayer(): MyPlayerTableHandle<'my_player'> { + // clientCache is a private property + return new MyPlayerTableHandle((this.connection as unknown as { clientCache: __ClientCache }).clientCache.getOrCreateTable(REMOTE_MODULE.tables.my_player)); + } + get person(): PersonTableHandle<'person'> { // clientCache is a private property return new PersonTableHandle((this.connection as unknown as { clientCache: __ClientCache }).clientCache.getOrCreateTable(REMOTE_MODULE.tables.person)); diff --git a/modules/sdk-test-cs/Lib.cs b/modules/sdk-test-cs/Lib.cs index 7ece58f476a..e6616e62117 100644 --- a/modules/sdk-test-cs/Lib.cs +++ b/modules/sdk-test-cs/Lib.cs @@ -848,7 +848,12 @@ public static void insert_unique_u32(ReducerContext ctx, uint n, int data) } [SpacetimeDB.Reducer] - public static void insert_unique_u32_update_pk_u32(ReducerContext ctx, uint n, int d_unique, int d_pk) + public static void insert_unique_u32_update_pk_u32( + ReducerContext ctx, + uint n, + int d_unique, + int d_pk + ) { ctx.Db.unique_u32.Insert(new UniqueU32 { n = n, data = d_unique }); ctx.Db.pk_u32.n.Update(new PkU32 { n = n, data = d_pk }); @@ -1715,9 +1720,7 @@ public static void insert_caller_one_identity(ReducerContext ctx) [SpacetimeDB.Reducer] public static void insert_caller_vec_identity(ReducerContext ctx) { - ctx.Db.vec_identity.Insert( - new VecIdentity { i = new List { ctx.Sender } } - ); + ctx.Db.vec_identity.Insert(new VecIdentity { i = new List { ctx.Sender } }); } [SpacetimeDB.Reducer] @@ -1735,7 +1738,9 @@ public static void insert_caller_pk_identity(ReducerContext ctx, int data) [SpacetimeDB.Reducer] public static void insert_caller_one_connection_id(ReducerContext ctx) { - ctx.Db.one_connection_id.Insert(new OneConnectionId { a = (ConnectionId)ctx.ConnectionId! }); + ctx.Db.one_connection_id.Insert( + new OneConnectionId { a = (ConnectionId)ctx.ConnectionId! } + ); } [SpacetimeDB.Reducer] @@ -1753,14 +1758,16 @@ public static void insert_caller_vec_connection_id(ReducerContext ctx) public static void insert_caller_unique_connection_id(ReducerContext ctx, int data) { ctx.Db.unique_connection_id.Insert( - new UniqueConnectionId { a = (ConnectionId)ctx.ConnectionId!, data = data } + new UniqueConnectionId { a = (ConnectionId)ctx.ConnectionId!, data = data } ); } [SpacetimeDB.Reducer] public static void insert_caller_pk_connection_id(ReducerContext ctx, int data) { - ctx.Db.pk_connection_id.Insert(new PkConnectionId { a = (ConnectionId)ctx.ConnectionId!, data = data }); + ctx.Db.pk_connection_id.Insert( + new PkConnectionId { a = (ConnectionId)ctx.ConnectionId!, data = data } + ); } [SpacetimeDB.Reducer] @@ -1782,7 +1789,11 @@ public static void delete_from_btree_u32(ReducerContext ctx, List rows } [SpacetimeDB.Reducer] - public static void insert_into_pk_btree_u32(ReducerContext ctx, List pk_u32, List bt_u32) + public static void insert_into_pk_btree_u32( + ReducerContext ctx, + List pk_u32, + List bt_u32 + ) { foreach (var row in pk_u32) { @@ -1950,7 +1961,6 @@ public static void insert_primitives_as_strings(ReducerContext ctx, EveryPrimiti return value.ToString()!.ToLowerInvariant(); } return value.ToString()!; - }) .ToList(), } @@ -2032,7 +2042,9 @@ public partial struct BTreeU32 } [SpacetimeDB.ClientVisibilityFilter] - public static readonly Filter USERS_FILTER = new Filter.Sql("SELECT * FROM users WHERE identity = :sender"); + public static readonly Filter USERS_FILTER = new Filter.Sql( + "SELECT * FROM users WHERE identity = :sender" + ); [SpacetimeDB.Table(Name = "users", Public = true)] public partial struct Users @@ -2048,8 +2060,15 @@ public static void insert_user(ReducerContext ctx, string name, Identity identit ctx.Db.users.Insert(new Users { name = name, identity = identity }); } + [SpacetimeDB.View(Name = "my_user", Public = true)] + public static Users? MyUser(ViewContext ctx) + { + return ctx.Db.users.identity.Find(ctx.Sender) as Users; + } + [SpacetimeDB.Table(Name = "indexed_simple_enum", Public = true)] - public partial struct IndexedSimpleEnum { + public partial struct IndexedSimpleEnum + { [SpacetimeDB.Index.BTree] public SimpleEnum n; } diff --git a/modules/sdk-test/src/lib.rs b/modules/sdk-test/src/lib.rs index c4dda5aeae3..5180896e152 100644 --- a/modules/sdk-test/src/lib.rs +++ b/modules/sdk-test/src/lib.rs @@ -9,7 +9,7 @@ use anyhow::{anyhow, Context, Result}; use spacetimedb::{ sats::{i256, u256}, - ConnectionId, Identity, ReducerContext, SpacetimeType, Table, TimeDuration, Timestamp, + ConnectionId, Identity, ReducerContext, SpacetimeType, Table, TimeDuration, Timestamp, ViewContext, }; #[derive(PartialEq, Eq, Hash, SpacetimeType)] @@ -796,6 +796,11 @@ fn insert_user(ctx: &ReducerContext, name: String, identity: Identity) -> anyhow Ok(()) } +#[spacetimedb::view(name = my_user, public)] +fn my_user(ctx: &ViewContext) -> Option { + ctx.db.users().identity().find(ctx.sender) +} + #[spacetimedb::table(name = indexed_simple_enum, public)] struct IndexedSimpleEnum { #[index(btree)] diff --git a/sdks/rust/tests/test-client/src/module_bindings/mod.rs b/sdks/rust/tests/test-client/src/module_bindings/mod.rs index 5957aee2af3..7653256bb02 100644 --- a/sdks/rust/tests/test-client/src/module_bindings/mod.rs +++ b/sdks/rust/tests/test-client/src/module_bindings/mod.rs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.6.0 (commit 3f1de9e09651bc412de3cb9daf49cc553ebb81e8). +// This was generated using spacetimedb cli version 1.8.0 (commit 549f84c80846137ec1ef7468d01673a329c0501f). #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; diff --git a/sdks/rust/tests/test-client/src/module_bindings/my_user_table.rs b/sdks/rust/tests/test-client/src/module_bindings/my_user_table.rs new file mode 100644 index 00000000000..adfc1be60db --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/my_user_table.rs @@ -0,0 +1,95 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::users_type::Users; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `my_user`. +/// +/// Obtain a handle from the [`MyUserTableAccess::my_user`] method on [`super::RemoteTables`], +/// like `ctx.db.my_user()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.my_user().on_insert(...)`. +pub struct MyUserTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `my_user`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait MyUserTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`MyUserTableHandle`], which mediates access to the table `my_user`. + fn my_user(&self) -> MyUserTableHandle<'_>; +} + +impl MyUserTableAccess for super::RemoteTables { + fn my_user(&self) -> MyUserTableHandle<'_> { + MyUserTableHandle { + imp: self.imp.get_table::("my_user"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct MyUserInsertCallbackId(__sdk::CallbackId); +pub struct MyUserDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for MyUserTableHandle<'ctx> { + type Row = Users; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = MyUserInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> MyUserInsertCallbackId { + MyUserInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: MyUserInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = MyUserDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> MyUserDeleteCallbackId { + MyUserDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: MyUserDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("my_user"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} From 988e13be2448acde8c93fbb0329f3e7e023ebab3 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Tue, 18 Nov 2025 13:13:16 -0800 Subject: [PATCH 06/38] regen client bindings --- .../tests/test-client/src/module_bindings/mod.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sdks/rust/tests/test-client/src/module_bindings/mod.rs b/sdks/rust/tests/test-client/src/module_bindings/mod.rs index 7653256bb02..6f057a2471c 100644 --- a/sdks/rust/tests/test-client/src/module_bindings/mod.rs +++ b/sdks/rust/tests/test-client/src/module_bindings/mod.rs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.8.0 (commit 549f84c80846137ec1ef7468d01673a329c0501f). +// This was generated using spacetimedb cli version 1.8.0 (commit 798b1c7909306e832723f507f7a3c97d6abc610d). #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; @@ -163,6 +163,7 @@ pub mod insert_vec_u_8_reducer; pub mod insert_vec_unit_struct_reducer; pub mod large_table_table; pub mod large_table_type; +pub mod my_user_table; pub mod no_op_succeeds_reducer; pub mod one_bool_table; pub mod one_bool_type; @@ -681,6 +682,7 @@ pub use insert_vec_unit_struct_reducer::{ }; pub use large_table_table::*; pub use large_table_type::LargeTable; +pub use my_user_table::*; pub use no_op_succeeds_reducer::{no_op_succeeds, set_flags_for_no_op_succeeds, NoOpSucceedsCallbackId}; pub use one_bool_table::*; pub use one_bool_type::OneBool; @@ -2773,6 +2775,7 @@ pub struct DbUpdate { indexed_table: __sdk::TableUpdate, indexed_table_2: __sdk::TableUpdate, large_table: __sdk::TableUpdate, + my_user: __sdk::TableUpdate, one_bool: __sdk::TableUpdate, one_byte_struct: __sdk::TableUpdate, one_connection_id: __sdk::TableUpdate, @@ -2889,6 +2892,9 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate { "large_table" => db_update .large_table .append(large_table_table::parse_table_update(table_update)?), + "my_user" => db_update + .my_user + .append(my_user_table::parse_table_update(table_update)?), "one_bool" => db_update .one_bool .append(one_bool_table::parse_table_update(table_update)?), @@ -3338,6 +3344,7 @@ impl __sdk::DbUpdate for DbUpdate { diff.vec_u_64 = cache.apply_diff_to_table::("vec_u64", &self.vec_u_64); diff.vec_u_8 = cache.apply_diff_to_table::("vec_u8", &self.vec_u_8); diff.vec_unit_struct = cache.apply_diff_to_table::("vec_unit_struct", &self.vec_unit_struct); + diff.my_user = cache.apply_diff_to_table::("my_user", &self.my_user); diff } @@ -3352,6 +3359,7 @@ pub struct AppliedDiff<'r> { indexed_table: __sdk::TableAppliedDiff<'r, IndexedTable>, indexed_table_2: __sdk::TableAppliedDiff<'r, IndexedTable2>, large_table: __sdk::TableAppliedDiff<'r, LargeTable>, + my_user: __sdk::TableAppliedDiff<'r, Users>, one_bool: __sdk::TableAppliedDiff<'r, OneBool>, one_byte_struct: __sdk::TableAppliedDiff<'r, OneByteStruct>, one_connection_id: __sdk::TableAppliedDiff<'r, OneConnectionId>, @@ -3463,6 +3471,7 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { callbacks.invoke_table_row_callbacks::("indexed_table", &self.indexed_table, event); callbacks.invoke_table_row_callbacks::("indexed_table_2", &self.indexed_table_2, event); callbacks.invoke_table_row_callbacks::("large_table", &self.large_table, event); + callbacks.invoke_table_row_callbacks::("my_user", &self.my_user, event); callbacks.invoke_table_row_callbacks::("one_bool", &self.one_bool, event); callbacks.invoke_table_row_callbacks::("one_byte_struct", &self.one_byte_struct, event); callbacks.invoke_table_row_callbacks::("one_connection_id", &self.one_connection_id, event); @@ -4316,6 +4325,7 @@ impl __sdk::SpacetimeModule for RemoteModule { indexed_table_table::register_table(client_cache); indexed_table_2_table::register_table(client_cache); large_table_table::register_table(client_cache); + my_user_table::register_table(client_cache); one_bool_table::register_table(client_cache); one_byte_struct_table::register_table(client_cache); one_connection_id_table::register_table(client_cache); From 837a6a257728bf04f1fc5871016054dfc62375dd Mon Sep 17 00:00:00 2001 From: rekhoff Date: Tue, 18 Nov 2025 19:21:49 -0800 Subject: [PATCH 07/38] Updates the Views test of the C# regression tests --- .../regression-tests/client/Program.cs | 23 +- .../Reducers/CreateNewUser.g.cs | 73 -- .../module_bindings/SpacetimeDBClient.g.cs | 773 +++++++++--------- .../Tables/GetAnonymousExampleDataById.g.cs | 27 - .../Tables/GetExampleDataById.g.cs | 27 - .../{GetUserByContext.g.cs => MyPlayer.g.cs} | 8 +- .../client/module_bindings/Tables/Player.g.cs | 49 ++ .../module_bindings/Tables/PlayerLevel.g.cs | 47 ++ ...UserByString.g.cs => PlayersForLevel.g.cs} | 8 +- .../client/module_bindings/Tables/User.g.cs | 39 - .../client/module_bindings/Types/Player.g.cs | 39 + .../module_bindings/Types/PlayerAndLevel.g.cs | 43 + .../module_bindings/Types/PlayerLevel.g.cs | 34 + .../client/module_bindings/Types/User.g.cs | 35 - .../examples~/regression-tests/server/Lib.cs | 104 ++- 15 files changed, 676 insertions(+), 653 deletions(-) delete mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/CreateNewUser.g.cs delete mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetAnonymousExampleDataById.g.cs delete mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetExampleDataById.g.cs rename sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/{GetUserByContext.g.cs => MyPlayer.g.cs} (58%) create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/Player.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/PlayerLevel.g.cs rename sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/{GetUserByString.g.cs => PlayersForLevel.g.cs} (60%) delete mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/User.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Types/Player.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Types/PlayerAndLevel.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Types/PlayerLevel.g.cs delete mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Types/User.g.cs diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index 849d9a27d64..5ad1e2d1c6c 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -50,7 +50,7 @@ void OnConnected(DbConnection conn, Identity identity, string authToken) { throw err; }) - .Subscribe(["SELECT * FROM ExampleData"]); + .Subscribe(["SELECT * FROM ExampleData", "SELECT * FROM MyPlayer"]); conn.Reducers.OnAdd += (ReducerEventContext ctx, uint id, uint indexed) => { @@ -134,14 +134,23 @@ void OnSubscriptionApplied(SubscriptionEventContext context) waiting--; }); + // Views test - Log.Debug("Calling View"); - var viewRows = context.Db.GetUserByContext.Iter().FirstOrDefault(); - Debug.Assert(viewRows != null && viewRows.IdentityString == context.Identity?.ToString()); + Log.Debug("Calling Iter on View"); + var viewIterRows = context.Db.MyPlayer.Iter(); + Debug.Assert(viewIterRows != null && viewIterRows.Any()); + + Log.Debug("Calling RemoteQuery on View"); + var viewRemoteQueryRows = context.Db.MyPlayer.RemoteQuery("WHERE Id > 0"); + Debug.Assert(viewRemoteQueryRows != null && viewRemoteQueryRows.Result.Length > 0); + + Log.Debug("Calling Iter on Anonymous View"); + var anonViewIterRows = context.Db.PlayersForLevel.Iter(); + Debug.Assert(anonViewIterRows != null && anonViewIterRows.Any()); - Log.Debug("Calling Anonymous View"); - var anonViewRows = context.Db.GetUserByString.RemoteQuery("Where IdentityString = identityStringExample"); - Debug.Assert(anonViewRows != null && anonViewRows.Result.Length > 0); + Log.Debug("Calling RemoteQuery on Anonymous View"); + var anonViewRemoteQueryRows = context.Db.PlayersForLevel.RemoteQuery($"WHERE Level = 1)"); + Debug.Assert(anonViewRemoteQueryRows != null && anonViewRemoteQueryRows.Result.Length > 0); } System.AppDomain.CurrentDomain.UnhandledException += (sender, args) => diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/CreateNewUser.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/CreateNewUser.g.cs deleted file mode 100644 index c9b279fc4ec..00000000000 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/CreateNewUser.g.cs +++ /dev/null @@ -1,73 +0,0 @@ -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. - -#nullable enable - -using System; -using SpacetimeDB.ClientApi; -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace SpacetimeDB.Types -{ - public sealed partial class RemoteReducers : RemoteBase - { - public delegate void CreateNewUserHandler(ReducerEventContext ctx, string identityString); - public event CreateNewUserHandler? OnCreateNewUser; - - public void CreateNewUser(string identityString) - { - conn.InternalCallReducer(new Reducer.CreateNewUser(identityString), this.SetCallReducerFlags.CreateNewUserFlags); - } - - public bool InvokeCreateNewUser(ReducerEventContext ctx, Reducer.CreateNewUser args) - { - if (OnCreateNewUser == null) - { - if (InternalOnUnhandledReducerError != null) - { - switch (ctx.Event.Status) - { - case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break; - case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break; - } - } - return false; - } - OnCreateNewUser( - ctx, - args.IdentityString - ); - return true; - } - } - - public abstract partial class Reducer - { - [SpacetimeDB.Type] - [DataContract] - public sealed partial class CreateNewUser : Reducer, IReducerArgs - { - [DataMember(Name = "identityString")] - public string IdentityString; - - public CreateNewUser(string IdentityString) - { - this.IdentityString = IdentityString; - } - - public CreateNewUser() - { - this.IdentityString = ""; - } - - string IReducerArgs.ReducerName => "CreateNewUser"; - } - } - - public sealed partial class SetReducerFlags - { - internal CallReducerFlags CreateNewUserFlags; - public void CreateNewUser(CallReducerFlags flags) => CreateNewUserFlags = flags; - } -} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs index 2eee0e4aa9e..169af7228b9 100644 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs @@ -24,427 +24,418 @@ public sealed partial class RemoteTables : RemoteTablesBase public RemoteTables(DbConnection conn) { AddTable(ExampleData = new(conn)); - AddTable(User = new(conn)); + AddTable(MyPlayer = new(conn)); + AddTable(Player = new(conn)); + AddTable(PlayerLevel = new(conn)); + AddTable(PlayersForLevel = new(conn)); } } public sealed partial class SetReducerFlags { } - public interface IRemoteDbContext : IDbContext - { - public event Action? OnUnhandledReducerError; - } - - public sealed class EventContext : IEventContext, IRemoteDbContext - { - private readonly DbConnection conn; - - /// - /// The event that caused this callback to run. - /// - public readonly Event Event; - - /// - /// Access to tables in the client cache, which stores a read-only replica of the remote database state. - /// - /// The returned DbView will have a method to access each table defined by the module. - /// - public RemoteTables Db => conn.Db; - /// - /// Access to reducers defined by the module. - /// - /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, - /// plus methods for adding and removing callbacks on each of those reducers. - /// - public RemoteReducers Reducers => conn.Reducers; - /// - /// Access to setters for per-reducer flags. - /// - /// The returned SetReducerFlags will have a method to invoke, - /// for each reducer defined by the module, - /// which call-flags for the reducer can be set. - /// - public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; - /// - /// Returns true if the connection is active, i.e. has not yet disconnected. - /// - public bool IsActive => conn.IsActive; - /// - /// Close the connection. - /// - /// Throws an error if the connection is already closed. - /// - public void Disconnect() - { - conn.Disconnect(); - } - /// - /// Start building a subscription. - /// - /// A builder-pattern constructor for subscribing to queries, - /// causing matching rows to be replicated into the client cache. - public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); - /// - /// Get the Identity of this connection. - /// - /// This method returns null if the connection was constructed anonymously - /// and we have not yet received our newly-generated Identity from the host. - /// - public Identity? Identity => conn.Identity; - /// - /// Get this connection's ConnectionId. - /// - public ConnectionId ConnectionId => conn.ConnectionId; - /// - /// Register a callback to be called when a reducer with no handler returns an error. - /// - public event Action? OnUnhandledReducerError - { - add => Reducers.InternalOnUnhandledReducerError += value; - remove => Reducers.InternalOnUnhandledReducerError -= value; + public interface IRemoteDbContext : IDbContext { + public event Action? OnUnhandledReducerError; } - internal EventContext(DbConnection conn, Event Event) + public sealed class EventContext : IEventContext, IRemoteDbContext { - this.conn = conn; - this.Event = Event; - } - } - - public sealed class ReducerEventContext : IReducerEventContext, IRemoteDbContext - { - private readonly DbConnection conn; - /// - /// The reducer event that caused this callback to run. - /// - public readonly ReducerEvent Event; + private readonly DbConnection conn; + + /// + /// The event that caused this callback to run. + /// + public readonly Event Event; + + /// + /// Access to tables in the client cache, which stores a read-only replica of the remote database state. + /// + /// The returned DbView will have a method to access each table defined by the module. + /// + public RemoteTables Db => conn.Db; + /// + /// Access to reducers defined by the module. + /// + /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, + /// plus methods for adding and removing callbacks on each of those reducers. + /// + public RemoteReducers Reducers => conn.Reducers; + /// + /// Access to setters for per-reducer flags. + /// + /// The returned SetReducerFlags will have a method to invoke, + /// for each reducer defined by the module, + /// which call-flags for the reducer can be set. + /// + public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; + /// + /// Returns true if the connection is active, i.e. has not yet disconnected. + /// + public bool IsActive => conn.IsActive; + /// + /// Close the connection. + /// + /// Throws an error if the connection is already closed. + /// + public void Disconnect() { + conn.Disconnect(); + } + /// + /// Start building a subscription. + /// + /// A builder-pattern constructor for subscribing to queries, + /// causing matching rows to be replicated into the client cache. + public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); + /// + /// Get the Identity of this connection. + /// + /// This method returns null if the connection was constructed anonymously + /// and we have not yet received our newly-generated Identity from the host. + /// + public Identity? Identity => conn.Identity; + /// + /// Get this connection's ConnectionId. + /// + public ConnectionId ConnectionId => conn.ConnectionId; + /// + /// Register a callback to be called when a reducer with no handler returns an error. + /// + public event Action? OnUnhandledReducerError { + add => Reducers.InternalOnUnhandledReducerError += value; + remove => Reducers.InternalOnUnhandledReducerError -= value; + } - /// - /// Access to tables in the client cache, which stores a read-only replica of the remote database state. - /// - /// The returned DbView will have a method to access each table defined by the module. - /// - public RemoteTables Db => conn.Db; - /// - /// Access to reducers defined by the module. - /// - /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, - /// plus methods for adding and removing callbacks on each of those reducers. - /// - public RemoteReducers Reducers => conn.Reducers; - /// - /// Access to setters for per-reducer flags. - /// - /// The returned SetReducerFlags will have a method to invoke, - /// for each reducer defined by the module, - /// which call-flags for the reducer can be set. - /// - public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; - /// - /// Returns true if the connection is active, i.e. has not yet disconnected. - /// - public bool IsActive => conn.IsActive; - /// - /// Close the connection. - /// - /// Throws an error if the connection is already closed. - /// - public void Disconnect() - { - conn.Disconnect(); - } - /// - /// Start building a subscription. - /// - /// A builder-pattern constructor for subscribing to queries, - /// causing matching rows to be replicated into the client cache. - public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); - /// - /// Get the Identity of this connection. - /// - /// This method returns null if the connection was constructed anonymously - /// and we have not yet received our newly-generated Identity from the host. - /// - public Identity? Identity => conn.Identity; - /// - /// Get this connection's ConnectionId. - /// - public ConnectionId ConnectionId => conn.ConnectionId; - /// - /// Register a callback to be called when a reducer with no handler returns an error. - /// - public event Action? OnUnhandledReducerError - { - add => Reducers.InternalOnUnhandledReducerError += value; - remove => Reducers.InternalOnUnhandledReducerError -= value; + internal EventContext(DbConnection conn, Event Event) + { + this.conn = conn; + this.Event = Event; + } } - internal ReducerEventContext(DbConnection conn, ReducerEvent reducerEvent) + public sealed class ReducerEventContext : IReducerEventContext, IRemoteDbContext { - this.conn = conn; - Event = reducerEvent; - } - } + private readonly DbConnection conn; + /// + /// The reducer event that caused this callback to run. + /// + public readonly ReducerEvent Event; + + /// + /// Access to tables in the client cache, which stores a read-only replica of the remote database state. + /// + /// The returned DbView will have a method to access each table defined by the module. + /// + public RemoteTables Db => conn.Db; + /// + /// Access to reducers defined by the module. + /// + /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, + /// plus methods for adding and removing callbacks on each of those reducers. + /// + public RemoteReducers Reducers => conn.Reducers; + /// + /// Access to setters for per-reducer flags. + /// + /// The returned SetReducerFlags will have a method to invoke, + /// for each reducer defined by the module, + /// which call-flags for the reducer can be set. + /// + public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; + /// + /// Returns true if the connection is active, i.e. has not yet disconnected. + /// + public bool IsActive => conn.IsActive; + /// + /// Close the connection. + /// + /// Throws an error if the connection is already closed. + /// + public void Disconnect() { + conn.Disconnect(); + } + /// + /// Start building a subscription. + /// + /// A builder-pattern constructor for subscribing to queries, + /// causing matching rows to be replicated into the client cache. + public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); + /// + /// Get the Identity of this connection. + /// + /// This method returns null if the connection was constructed anonymously + /// and we have not yet received our newly-generated Identity from the host. + /// + public Identity? Identity => conn.Identity; + /// + /// Get this connection's ConnectionId. + /// + public ConnectionId ConnectionId => conn.ConnectionId; + /// + /// Register a callback to be called when a reducer with no handler returns an error. + /// + public event Action? OnUnhandledReducerError { + add => Reducers.InternalOnUnhandledReducerError += value; + remove => Reducers.InternalOnUnhandledReducerError -= value; + } - public sealed class ErrorContext : IErrorContext, IRemoteDbContext - { - private readonly DbConnection conn; - /// - /// The Exception that caused this error callback to be run. - /// - public readonly Exception Event; - Exception IErrorContext.Event - { - get + internal ReducerEventContext(DbConnection conn, ReducerEvent reducerEvent) { - return Event; + this.conn = conn; + Event = reducerEvent; } } - /// - /// Access to tables in the client cache, which stores a read-only replica of the remote database state. - /// - /// The returned DbView will have a method to access each table defined by the module. - /// - public RemoteTables Db => conn.Db; - /// - /// Access to reducers defined by the module. - /// - /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, - /// plus methods for adding and removing callbacks on each of those reducers. - /// - public RemoteReducers Reducers => conn.Reducers; - /// - /// Access to setters for per-reducer flags. - /// - /// The returned SetReducerFlags will have a method to invoke, - /// for each reducer defined by the module, - /// which call-flags for the reducer can be set. - /// - public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; - /// - /// Returns true if the connection is active, i.e. has not yet disconnected. - /// - public bool IsActive => conn.IsActive; - /// - /// Close the connection. - /// - /// Throws an error if the connection is already closed. - /// - public void Disconnect() + public sealed class ErrorContext : IErrorContext, IRemoteDbContext { - conn.Disconnect(); - } - /// - /// Start building a subscription. - /// - /// A builder-pattern constructor for subscribing to queries, - /// causing matching rows to be replicated into the client cache. - public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); - /// - /// Get the Identity of this connection. - /// - /// This method returns null if the connection was constructed anonymously - /// and we have not yet received our newly-generated Identity from the host. - /// - public Identity? Identity => conn.Identity; - /// - /// Get this connection's ConnectionId. - /// - public ConnectionId ConnectionId => conn.ConnectionId; - /// - /// Register a callback to be called when a reducer with no handler returns an error. - /// - public event Action? OnUnhandledReducerError - { - add => Reducers.InternalOnUnhandledReducerError += value; - remove => Reducers.InternalOnUnhandledReducerError -= value; - } - - internal ErrorContext(DbConnection conn, Exception error) - { - this.conn = conn; - Event = error; - } - } + private readonly DbConnection conn; + /// + /// The Exception that caused this error callback to be run. + /// + public readonly Exception Event; + Exception IErrorContext.Event { + get { + return Event; + } + } - public sealed class SubscriptionEventContext : ISubscriptionEventContext, IRemoteDbContext - { - private readonly DbConnection conn; + /// + /// Access to tables in the client cache, which stores a read-only replica of the remote database state. + /// + /// The returned DbView will have a method to access each table defined by the module. + /// + public RemoteTables Db => conn.Db; + /// + /// Access to reducers defined by the module. + /// + /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, + /// plus methods for adding and removing callbacks on each of those reducers. + /// + public RemoteReducers Reducers => conn.Reducers; + /// + /// Access to setters for per-reducer flags. + /// + /// The returned SetReducerFlags will have a method to invoke, + /// for each reducer defined by the module, + /// which call-flags for the reducer can be set. + /// + public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; + /// + /// Returns true if the connection is active, i.e. has not yet disconnected. + /// + public bool IsActive => conn.IsActive; + /// + /// Close the connection. + /// + /// Throws an error if the connection is already closed. + /// + public void Disconnect() { + conn.Disconnect(); + } + /// + /// Start building a subscription. + /// + /// A builder-pattern constructor for subscribing to queries, + /// causing matching rows to be replicated into the client cache. + public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); + /// + /// Get the Identity of this connection. + /// + /// This method returns null if the connection was constructed anonymously + /// and we have not yet received our newly-generated Identity from the host. + /// + public Identity? Identity => conn.Identity; + /// + /// Get this connection's ConnectionId. + /// + public ConnectionId ConnectionId => conn.ConnectionId; + /// + /// Register a callback to be called when a reducer with no handler returns an error. + /// + public event Action? OnUnhandledReducerError { + add => Reducers.InternalOnUnhandledReducerError += value; + remove => Reducers.InternalOnUnhandledReducerError -= value; + } - /// - /// Access to tables in the client cache, which stores a read-only replica of the remote database state. - /// - /// The returned DbView will have a method to access each table defined by the module. - /// - public RemoteTables Db => conn.Db; - /// - /// Access to reducers defined by the module. - /// - /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, - /// plus methods for adding and removing callbacks on each of those reducers. - /// - public RemoteReducers Reducers => conn.Reducers; - /// - /// Access to setters for per-reducer flags. - /// - /// The returned SetReducerFlags will have a method to invoke, - /// for each reducer defined by the module, - /// which call-flags for the reducer can be set. - /// - public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; - /// - /// Returns true if the connection is active, i.e. has not yet disconnected. - /// - public bool IsActive => conn.IsActive; - /// - /// Close the connection. - /// - /// Throws an error if the connection is already closed. - /// - public void Disconnect() - { - conn.Disconnect(); - } - /// - /// Start building a subscription. - /// - /// A builder-pattern constructor for subscribing to queries, - /// causing matching rows to be replicated into the client cache. - public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); - /// - /// Get the Identity of this connection. - /// - /// This method returns null if the connection was constructed anonymously - /// and we have not yet received our newly-generated Identity from the host. - /// - public Identity? Identity => conn.Identity; - /// - /// Get this connection's ConnectionId. - /// - public ConnectionId ConnectionId => conn.ConnectionId; - /// - /// Register a callback to be called when a reducer with no handler returns an error. - /// - public event Action? OnUnhandledReducerError - { - add => Reducers.InternalOnUnhandledReducerError += value; - remove => Reducers.InternalOnUnhandledReducerError -= value; + internal ErrorContext(DbConnection conn, Exception error) + { + this.conn = conn; + Event = error; + } } - internal SubscriptionEventContext(DbConnection conn) + public sealed class SubscriptionEventContext : ISubscriptionEventContext, IRemoteDbContext { - this.conn = conn; - } - } - - /// - /// Builder-pattern constructor for subscription queries. - /// - public sealed class SubscriptionBuilder - { - private readonly IDbConnection conn; - - private event Action? Applied; - private event Action? Error; + private readonly DbConnection conn; + + /// + /// Access to tables in the client cache, which stores a read-only replica of the remote database state. + /// + /// The returned DbView will have a method to access each table defined by the module. + /// + public RemoteTables Db => conn.Db; + /// + /// Access to reducers defined by the module. + /// + /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, + /// plus methods for adding and removing callbacks on each of those reducers. + /// + public RemoteReducers Reducers => conn.Reducers; + /// + /// Access to setters for per-reducer flags. + /// + /// The returned SetReducerFlags will have a method to invoke, + /// for each reducer defined by the module, + /// which call-flags for the reducer can be set. + /// + public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; + /// + /// Returns true if the connection is active, i.e. has not yet disconnected. + /// + public bool IsActive => conn.IsActive; + /// + /// Close the connection. + /// + /// Throws an error if the connection is already closed. + /// + public void Disconnect() { + conn.Disconnect(); + } + /// + /// Start building a subscription. + /// + /// A builder-pattern constructor for subscribing to queries, + /// causing matching rows to be replicated into the client cache. + public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); + /// + /// Get the Identity of this connection. + /// + /// This method returns null if the connection was constructed anonymously + /// and we have not yet received our newly-generated Identity from the host. + /// + public Identity? Identity => conn.Identity; + /// + /// Get this connection's ConnectionId. + /// + public ConnectionId ConnectionId => conn.ConnectionId; + /// + /// Register a callback to be called when a reducer with no handler returns an error. + /// + public event Action? OnUnhandledReducerError { + add => Reducers.InternalOnUnhandledReducerError += value; + remove => Reducers.InternalOnUnhandledReducerError -= value; + } - /// - /// Private API, use conn.SubscriptionBuilder() instead. - /// - public SubscriptionBuilder(IDbConnection conn) - { - this.conn = conn; + internal SubscriptionEventContext(DbConnection conn) + { + this.conn = conn; + } } /// - /// Register a callback to run when the subscription is applied. + /// Builder-pattern constructor for subscription queries. /// - public SubscriptionBuilder OnApplied( - Action callback - ) + public sealed class SubscriptionBuilder { - Applied += callback; - return this; - } + private readonly IDbConnection conn; - /// - /// Register a callback to run when the subscription fails. - /// - /// Note that this callback may run either when attempting to apply the subscription, - /// in which case Self::on_applied will never run, - /// or later during the subscription's lifetime if the module's interface changes, - /// in which case Self::on_applied may have already run. - /// - public SubscriptionBuilder OnError( - Action callback - ) - { - Error += callback; - return this; - } + private event Action? Applied; + private event Action? Error; - /// - /// Subscribe to the following SQL queries. - /// - /// This method returns immediately, with the data not yet added to the DbConnection. - /// The provided callbacks will be invoked once the data is returned from the remote server. - /// Data from all the provided queries will be returned at the same time. - /// - /// See the SpacetimeDB SQL docs for more information on SQL syntax: - /// https://spacetimedb.com/docs/sql - /// - public SubscriptionHandle Subscribe( - string[] querySqls - ) => new(conn, Applied, Error, querySqls); + /// + /// Private API, use conn.SubscriptionBuilder() instead. + /// + public SubscriptionBuilder(IDbConnection conn) + { + this.conn = conn; + } - /// - /// Subscribe to all rows from all tables. - /// - /// This method is intended as a convenience - /// for applications where client-side memory use and network bandwidth are not concerns. - /// Applications where these resources are a constraint - /// should register more precise queries via Self.Subscribe - /// in order to replicate only the subset of data which the client needs to function. - /// - /// This method should not be combined with Self.Subscribe on the same DbConnection. - /// A connection may either Self.Subscribe to particular queries, - /// or Self.SubscribeToAllTables, but not both. - /// Attempting to call Self.Subscribe - /// on a DbConnection that has previously used Self.SubscribeToAllTables, - /// or vice versa, may misbehave in any number of ways, - /// including dropping subscriptions, corrupting the client cache, or panicking. - /// - public void SubscribeToAllTables() - { - // Make sure we use the legacy handle constructor here, even though there's only 1 query. - // We drop the error handler, since it can't be called for legacy subscriptions. - new SubscriptionHandle( - conn, - Applied, - new string[] { "SELECT * FROM *" } - ); - } - } + /// + /// Register a callback to run when the subscription is applied. + /// + public SubscriptionBuilder OnApplied( + Action callback + ) + { + Applied += callback; + return this; + } - public sealed class SubscriptionHandle : SubscriptionHandleBase - { - /// - /// Internal API. Construct SubscriptionHandles using conn.SubscriptionBuilder. - /// - public SubscriptionHandle(IDbConnection conn, Action? onApplied, string[] querySqls) : base(conn, onApplied, querySqls) - { } + /// + /// Register a callback to run when the subscription fails. + /// + /// Note that this callback may run either when attempting to apply the subscription, + /// in which case Self::on_applied will never run, + /// or later during the subscription's lifetime if the module's interface changes, + /// in which case Self::on_applied may have already run. + /// + public SubscriptionBuilder OnError( + Action callback + ) + { + Error += callback; + return this; + } - /// - /// Internal API. Construct SubscriptionHandles using conn.SubscriptionBuilder. - /// - public SubscriptionHandle( - IDbConnection conn, - Action? onApplied, - Action? onError, - string[] querySqls - ) : base(conn, onApplied, onError, querySqls) - { } - } + /// + /// Subscribe to the following SQL queries. + /// + /// This method returns immediately, with the data not yet added to the DbConnection. + /// The provided callbacks will be invoked once the data is returned from the remote server. + /// Data from all the provided queries will be returned at the same time. + /// + /// See the SpacetimeDB SQL docs for more information on SQL syntax: + /// https://spacetimedb.com/docs/sql + /// + public SubscriptionHandle Subscribe( + string[] querySqls + ) => new(conn, Applied, Error, querySqls); + + /// + /// Subscribe to all rows from all tables. + /// + /// This method is intended as a convenience + /// for applications where client-side memory use and network bandwidth are not concerns. + /// Applications where these resources are a constraint + /// should register more precise queries via Self.Subscribe + /// in order to replicate only the subset of data which the client needs to function. + /// + /// This method should not be combined with Self.Subscribe on the same DbConnection. + /// A connection may either Self.Subscribe to particular queries, + /// or Self.SubscribeToAllTables, but not both. + /// Attempting to call Self.Subscribe + /// on a DbConnection that has previously used Self.SubscribeToAllTables, + /// or vice versa, may misbehave in any number of ways, + /// including dropping subscriptions, corrupting the client cache, or panicking. + /// + public void SubscribeToAllTables() + { + // Make sure we use the legacy handle constructor here, even though there's only 1 query. + // We drop the error handler, since it can't be called for legacy subscriptions. + new SubscriptionHandle( + conn, + Applied, + new string[] { "SELECT * FROM *" } + ); + } + } + + public sealed class SubscriptionHandle : SubscriptionHandleBase { + /// + /// Internal API. Construct SubscriptionHandles using conn.SubscriptionBuilder. + /// + public SubscriptionHandle(IDbConnection conn, Action? onApplied, string[] querySqls) : base(conn, onApplied, querySqls) + { } + + /// + /// Internal API. Construct SubscriptionHandles using conn.SubscriptionBuilder. + /// + public SubscriptionHandle( + IDbConnection conn, + Action? onApplied, + Action? onError, + string[] querySqls + ) : base(conn, onApplied, onError, querySqls) + { } + } public abstract partial class Reducer { @@ -466,11 +457,9 @@ public DbConnection() protected override Reducer ToReducer(TransactionUpdate update) { var encodedArgs = update.ReducerCall.Args; - return update.ReducerCall.ReducerName switch - { + return update.ReducerCall.ReducerName switch { "Add" => BSATNHelpers.Decode(encodedArgs), "ClientConnected" => BSATNHelpers.Decode(encodedArgs), - "CreateNewUser" => BSATNHelpers.Decode(encodedArgs), "Delete" => BSATNHelpers.Decode(encodedArgs), "ThrowError" => BSATNHelpers.Decode(encodedArgs), "" => throw new SpacetimeDBEmptyReducerNameException("Reducer name is empty"), @@ -493,11 +482,9 @@ protected override IErrorContext ToErrorContext(Exception exception) => protected override bool Dispatch(IReducerEventContext context, Reducer reducer) { var eventContext = (ReducerEventContext)context; - return reducer switch - { + return reducer switch { Reducer.Add args => Reducers.InvokeAdd(eventContext, args), Reducer.ClientConnected args => Reducers.InvokeClientConnected(eventContext, args), - Reducer.CreateNewUser args => Reducers.InvokeCreateNewUser(eventContext, args), Reducer.Delete args => Reducers.InvokeDelete(eventContext, args), Reducer.ThrowError args => Reducers.InvokeThrowError(eventContext, args), _ => throw new ArgumentOutOfRangeException("Reducer", $"Unknown reducer {reducer}") diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetAnonymousExampleDataById.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetAnonymousExampleDataById.g.cs deleted file mode 100644 index 0d9910a0fa8..00000000000 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetAnonymousExampleDataById.g.cs +++ /dev/null @@ -1,27 +0,0 @@ -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. - -#nullable enable - -using System; -using SpacetimeDB.BSATN; -using SpacetimeDB.ClientApi; -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace SpacetimeDB.Types -{ - public sealed partial class RemoteTables - { - public sealed class GetAnonymousExampleDataByIdHandle : RemoteTableHandle - { - protected override string RemoteTableName => "GetAnonymousExampleDataById"; - - internal GetAnonymousExampleDataByIdHandle(DbConnection conn) : base(conn) - { - } - } - - public readonly GetAnonymousExampleDataByIdHandle GetAnonymousExampleDataById; - } -} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetExampleDataById.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetExampleDataById.g.cs deleted file mode 100644 index 7832fe7b6d7..00000000000 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetExampleDataById.g.cs +++ /dev/null @@ -1,27 +0,0 @@ -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. - -#nullable enable - -using System; -using SpacetimeDB.BSATN; -using SpacetimeDB.ClientApi; -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace SpacetimeDB.Types -{ - public sealed partial class RemoteTables - { - public sealed class GetExampleDataByIdHandle : RemoteTableHandle - { - protected override string RemoteTableName => "GetExampleDataById"; - - internal GetExampleDataByIdHandle(DbConnection conn) : base(conn) - { - } - } - - public readonly GetExampleDataByIdHandle GetExampleDataById; - } -} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByContext.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyPlayer.g.cs similarity index 58% rename from sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByContext.g.cs rename to sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyPlayer.g.cs index 80e7389bbf6..86292847f92 100644 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByContext.g.cs +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyPlayer.g.cs @@ -13,15 +13,15 @@ namespace SpacetimeDB.Types { public sealed partial class RemoteTables { - public sealed class GetUserByContextHandle : RemoteTableHandle + public sealed class MyPlayerHandle : RemoteTableHandle { - protected override string RemoteTableName => "GetUserByContext"; + protected override string RemoteTableName => "MyPlayer"; - internal GetUserByContextHandle(DbConnection conn) : base(conn) + internal MyPlayerHandle(DbConnection conn) : base(conn) { } } - public readonly GetUserByContextHandle GetUserByContext; + public readonly MyPlayerHandle MyPlayer; } } diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/Player.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/Player.g.cs new file mode 100644 index 00000000000..7cc2b9b3316 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/Player.g.cs @@ -0,0 +1,49 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.BSATN; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteTables + { + public sealed class PlayerHandle : RemoteTableHandle + { + protected override string RemoteTableName => "Player"; + + public sealed class IdUniqueIndex : UniqueIndexBase + { + protected override ulong GetKey(Player row) => row.Id; + + public IdUniqueIndex(PlayerHandle table) : base(table) { } + } + + public readonly IdUniqueIndex Id; + + public sealed class IdentityUniqueIndex : UniqueIndexBase + { + protected override SpacetimeDB.Identity GetKey(Player row) => row.Identity; + + public IdentityUniqueIndex(PlayerHandle table) : base(table) { } + } + + public readonly IdentityUniqueIndex Identity; + + internal PlayerHandle(DbConnection conn) : base(conn) + { + Id = new(this); + Identity = new(this); + } + + protected override object GetPrimaryKey(Player row) => row.Id; + } + + public readonly PlayerHandle Player; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/PlayerLevel.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/PlayerLevel.g.cs new file mode 100644 index 00000000000..01e5169593a --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/PlayerLevel.g.cs @@ -0,0 +1,47 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.BSATN; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteTables + { + public sealed class PlayerLevelHandle : RemoteTableHandle + { + protected override string RemoteTableName => "PlayerLevel"; + + public sealed class LevelIndex : BTreeIndexBase + { + protected override ulong GetKey(PlayerLevel row) => row.Level; + + public LevelIndex(PlayerLevelHandle table) : base(table) { } + } + + public readonly LevelIndex Level; + + public sealed class PlayerIdUniqueIndex : UniqueIndexBase + { + protected override ulong GetKey(PlayerLevel row) => row.PlayerId; + + public PlayerIdUniqueIndex(PlayerLevelHandle table) : base(table) { } + } + + public readonly PlayerIdUniqueIndex PlayerId; + + internal PlayerLevelHandle(DbConnection conn) : base(conn) + { + Level = new(this); + PlayerId = new(this); + } + } + + public readonly PlayerLevelHandle PlayerLevel; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByString.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/PlayersForLevel.g.cs similarity index 60% rename from sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByString.g.cs rename to sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/PlayersForLevel.g.cs index 2bea2f1e1b5..5758613ad31 100644 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/GetUserByString.g.cs +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/PlayersForLevel.g.cs @@ -13,15 +13,15 @@ namespace SpacetimeDB.Types { public sealed partial class RemoteTables { - public sealed class GetUserByStringHandle : RemoteTableHandle + public sealed class PlayersForLevelHandle : RemoteTableHandle { - protected override string RemoteTableName => "GetUserByString"; + protected override string RemoteTableName => "PlayersForLevel"; - internal GetUserByStringHandle(DbConnection conn) : base(conn) + internal PlayersForLevelHandle(DbConnection conn) : base(conn) { } } - public readonly GetUserByStringHandle GetUserByString; + public readonly PlayersForLevelHandle PlayersForLevel; } } diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/User.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/User.g.cs deleted file mode 100644 index c29f27e2d59..00000000000 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/User.g.cs +++ /dev/null @@ -1,39 +0,0 @@ -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. - -#nullable enable - -using System; -using SpacetimeDB.BSATN; -using SpacetimeDB.ClientApi; -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace SpacetimeDB.Types -{ - public sealed partial class RemoteTables - { - public sealed class UserHandle : RemoteTableHandle - { - protected override string RemoteTableName => "User"; - - public sealed class IdentityStringUniqueIndex : UniqueIndexBase - { - protected override string GetKey(User row) => row.IdentityString; - - public IdentityStringUniqueIndex(UserHandle table) : base(table) { } - } - - public readonly IdentityStringUniqueIndex IdentityString; - - internal UserHandle(DbConnection conn) : base(conn) - { - IdentityString = new(this); - } - - protected override object GetPrimaryKey(User row) => row.IdentityString; - } - - public readonly UserHandle User; - } -} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/Player.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/Player.g.cs new file mode 100644 index 00000000000..443f51cf68f --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/Player.g.cs @@ -0,0 +1,39 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + [SpacetimeDB.Type] + [DataContract] + public sealed partial class Player + { + [DataMember(Name = "Id")] + public ulong Id; + [DataMember(Name = "Identity")] + public SpacetimeDB.Identity Identity; + [DataMember(Name = "Name")] + public string Name; + + public Player( + ulong Id, + SpacetimeDB.Identity Identity, + string Name + ) + { + this.Id = Id; + this.Identity = Identity; + this.Name = Name; + } + + public Player() + { + this.Name = ""; + } + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/PlayerAndLevel.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/PlayerAndLevel.g.cs new file mode 100644 index 00000000000..f87a401f421 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/PlayerAndLevel.g.cs @@ -0,0 +1,43 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + [SpacetimeDB.Type] + [DataContract] + public sealed partial class PlayerAndLevel + { + [DataMember(Name = "Id")] + public ulong Id; + [DataMember(Name = "Identity")] + public SpacetimeDB.Identity Identity; + [DataMember(Name = "Name")] + public string Name; + [DataMember(Name = "Level")] + public ulong Level; + + public PlayerAndLevel( + ulong Id, + SpacetimeDB.Identity Identity, + string Name, + ulong Level + ) + { + this.Id = Id; + this.Identity = Identity; + this.Name = Name; + this.Level = Level; + } + + public PlayerAndLevel() + { + this.Name = ""; + } + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/PlayerLevel.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/PlayerLevel.g.cs new file mode 100644 index 00000000000..8f9fb43e7f3 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/PlayerLevel.g.cs @@ -0,0 +1,34 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + [SpacetimeDB.Type] + [DataContract] + public sealed partial class PlayerLevel + { + [DataMember(Name = "PlayerId")] + public ulong PlayerId; + [DataMember(Name = "Level")] + public ulong Level; + + public PlayerLevel( + ulong PlayerId, + ulong Level + ) + { + this.PlayerId = PlayerId; + this.Level = Level; + } + + public PlayerLevel() + { + } + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/User.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/User.g.cs deleted file mode 100644 index 502d00c010e..00000000000 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/User.g.cs +++ /dev/null @@ -1,35 +0,0 @@ -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. - -#nullable enable - -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace SpacetimeDB.Types -{ - [SpacetimeDB.Type] - [DataContract] - public sealed partial class User - { - [DataMember(Name = "IdentityString")] - public string IdentityString; - [DataMember(Name = "GeneratedByConnectedClient")] - public bool GeneratedByConnectedClient; - - public User( - string IdentityString, - bool GeneratedByConnectedClient - ) - { - this.IdentityString = IdentityString; - this.GeneratedByConnectedClient = GeneratedByConnectedClient; - } - - public User() - { - this.IdentityString = ""; - } - } -} diff --git a/sdks/csharp/examples~/regression-tests/server/Lib.cs b/sdks/csharp/examples~/regression-tests/server/Lib.cs index 259ce739fcd..7a8c8440a12 100644 --- a/sdks/csharp/examples~/regression-tests/server/Lib.cs +++ b/sdks/csharp/examples~/regression-tests/server/Lib.cs @@ -15,36 +15,68 @@ public partial struct ExampleData public uint Indexed; } - [SpacetimeDB.Table(Name = "User", Public = true)] - public partial struct User - { - [PrimaryKey] - public string IdentityString; - public bool GeneratedByConnectedClient; - } + [SpacetimeDB.Table(Name = "Player", Public = true)] + public partial struct Player { + [SpacetimeDB.PrimaryKey] + [SpacetimeDB.AutoInc] + public ulong Id; - [SpacetimeDB.View(Name = "GetExampleDataById", Public = true)] - public static ExampleData? GetExampleDataById(ViewContext ctx)//, uint id) - { - return ctx.Db.ExampleData.Id.Find(0); + [SpacetimeDB.Unique] + public Identity Identity; + + public string Name; } - [SpacetimeDB.View(Name = "GetAnonymousExampleDataById", Public = true)] - public static ExampleData? GetAnonymousExampleDataById(AnonymousViewContext ctx) //, uint id) - { - return ctx.Db.ExampleData.Id.Find(0); + [SpacetimeDB.Table(Name = "PlayerLevel", Public = true)] + public partial struct PlayerLevel { + [SpacetimeDB.Unique] + public ulong PlayerId; + + [SpacetimeDB.Index.BTree] + public ulong Level; } - [SpacetimeDB.View(Name = "GetUserByContext", Public = true)] - public static User? GetUserByContext(ViewContext ctx) + [Type] + public partial struct TransportData { + public int TroopCount; + } + + [SpacetimeDB.Type] + public partial struct PlayerAndLevel { - return ctx.Db.User.IdentityString.Find(ctx.Sender.ToString()); + public ulong Id; + public Identity Identity; + public string Name; + public ulong Level; } - [SpacetimeDB.View(Name = "GetUserByString", Public = true)] - public static User? GetUserByString(AnonymousViewContext ctx) //, string identityString) + // At-most-one row: return T? + [SpacetimeDB.View(Name = "MyPlayer", Public = true)] + public static Player? MyPlayer(ViewContext ctx) { - return ctx.Db.User.IdentityString.Find("identityStringExample"); + return ctx.Db.Player.Identity.Find(ctx.Sender) as Player?; + } + + // Multiple rows: return a list + [SpacetimeDB.View(Name = "PlayersForLevel", Public = true)] + public static List PlayersForLevel(AnonymousViewContext ctx) + { + var rows = new List(); + foreach (var player in ctx.Db.PlayerLevel.Level.Filter(1)) + { + if (ctx.Db.Player.Id.Find(player.PlayerId) is Player p) + { + var row = new PlayerAndLevel + { + Id = p.Id, + Identity = p.Identity, + Name = p.Name, + Level = player.Level + }; + rows.Add(row); + } + } + return rows; } [SpacetimeDB.Reducer] @@ -65,37 +97,21 @@ public static void ThrowError(ReducerContext ctx, string error) throw new Exception(error); } - [SpacetimeDB.Reducer] - public static void CreateNewUser(ReducerContext ctx, string identityString) - { - ctx.Db.User.Insert( - new User - { - IdentityString = identityString, - GeneratedByConnectedClient = false, - } - ); - } - - [SpacetimeDB.Reducer(ReducerKind.ClientConnected)] + [Reducer(ReducerKind.ClientConnected)] public static void ClientConnected(ReducerContext ctx) { Log.Info($"Connect {ctx.Sender}"); - if (ctx.Db.User.IdentityString.Find(ctx.Sender.ToString()!) is User thisUser) + if (ctx.Db.Player.Identity.Find(ctx.Sender) is Player player) { - ctx.Db.User.IdentityString.Update(thisUser); + // We are not logging player login status, so do nothing } else { - // If this is a new User, create a `User` object for the `IdentityString`, - ctx.Db.User.Insert( - new User - { - IdentityString = ctx.Sender.ToString()!, - GeneratedByConnectedClient = true, - } - ); + // Lets setup a new player with a level of 1 + ctx.Db.Player.Insert(new Player { Identity = ctx.Sender, Name = "NewPlayer"}); + var playerId = (ctx.Db.Player.Identity.Find(ctx.Sender)!).Value.Id; + ctx.Db.PlayerLevel.Insert(new PlayerLevel { PlayerId = playerId, Level = 1}); } } } From 4f7296d0377f1133cf4d8ae4fa91d9ab47d509ec Mon Sep 17 00:00:00 2001 From: rekhoff Date: Tue, 18 Nov 2025 19:32:08 -0800 Subject: [PATCH 08/38] Minor formatting update --- .../examples~/regression-tests/server/Lib.cs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/sdks/csharp/examples~/regression-tests/server/Lib.cs b/sdks/csharp/examples~/regression-tests/server/Lib.cs index 7a8c8440a12..f28711ec12e 100644 --- a/sdks/csharp/examples~/regression-tests/server/Lib.cs +++ b/sdks/csharp/examples~/regression-tests/server/Lib.cs @@ -14,9 +14,10 @@ public partial struct ExampleData [SpacetimeDB.Index.BTree] public uint Indexed; } - + [SpacetimeDB.Table(Name = "Player", Public = true)] - public partial struct Player { + public partial struct Player + { [SpacetimeDB.PrimaryKey] [SpacetimeDB.AutoInc] public ulong Id; @@ -28,16 +29,18 @@ public partial struct Player { } [SpacetimeDB.Table(Name = "PlayerLevel", Public = true)] - public partial struct PlayerLevel { + public partial struct PlayerLevel + { [SpacetimeDB.Unique] public ulong PlayerId; [SpacetimeDB.Index.BTree] public ulong Level; } - + [Type] - public partial struct TransportData { + public partial struct TransportData + { public int TroopCount; } @@ -56,7 +59,7 @@ public partial struct PlayerAndLevel { return ctx.Db.Player.Identity.Find(ctx.Sender) as Player?; } - + // Multiple rows: return a list [SpacetimeDB.View(Name = "PlayersForLevel", Public = true)] public static List PlayersForLevel(AnonymousViewContext ctx) @@ -96,7 +99,7 @@ public static void ThrowError(ReducerContext ctx, string error) { throw new Exception(error); } - + [Reducer(ReducerKind.ClientConnected)] public static void ClientConnected(ReducerContext ctx) { @@ -109,9 +112,9 @@ public static void ClientConnected(ReducerContext ctx) else { // Lets setup a new player with a level of 1 - ctx.Db.Player.Insert(new Player { Identity = ctx.Sender, Name = "NewPlayer"}); + ctx.Db.Player.Insert(new Player { Identity = ctx.Sender, Name = "NewPlayer" }); var playerId = (ctx.Db.Player.Identity.Find(ctx.Sender)!).Value.Id; - ctx.Db.PlayerLevel.Insert(new PlayerLevel { PlayerId = playerId, Level = 1}); + ctx.Db.PlayerLevel.Insert(new PlayerLevel { PlayerId = playerId, Level = 1 }); } } } From 9a10dc14aeb7e6a9f8d14ee91dbf6d0469a60328 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Tue, 18 Nov 2025 19:53:36 -0800 Subject: [PATCH 09/38] fix client codegen for views --- crates/codegen/src/csharp.rs | 8 +- crates/codegen/src/rust.rs | 74 ++++++++++--------- crates/codegen/src/typescript.rs | 45 +++++++---- crates/codegen/src/unrealcpp.rs | 44 ++++++----- crates/codegen/src/util.rs | 21 +++++- .../snapshots/codegen__codegen_csharp.snap | 1 + .../snapshots/codegen__codegen_rust.snap | 8 ++ .../codegen__codegen_typescript.snap | 11 +++ 8 files changed, 136 insertions(+), 76 deletions(-) diff --git a/crates/codegen/src/csharp.rs b/crates/codegen/src/csharp.rs index e81035aa594..5e9eab5763e 100644 --- a/crates/codegen/src/csharp.rs +++ b/crates/codegen/src/csharp.rs @@ -7,8 +7,8 @@ use std::ops::Deref; use super::code_indenter::CodeIndenter; use super::Lang; use crate::util::{ - collect_case, is_reducer_invokable, iter_indexes, iter_reducers, iter_tables, print_auto_generated_file_comment, - print_auto_generated_version_comment, type_ref_name, + collect_case, is_reducer_invokable, iter_indexes, iter_reducers, iter_table_names_and_types, + print_auto_generated_file_comment, print_auto_generated_version_comment, type_ref_name, }; use crate::{indent_scope, OutputFile}; use convert_case::{Case, Casing}; @@ -745,11 +745,11 @@ impl Lang for Csharp<'_> { indented_block(&mut output, |output| { writeln!(output, "public RemoteTables(DbConnection conn)"); indented_block(output, |output| { - for table in iter_tables(module) { + for (table_name, _) in iter_table_names_and_types(module) { writeln!( output, "AddTable({} = new(conn));", - table.name.deref().to_case(Case::Pascal) + table_name.deref().to_case(Case::Pascal) ); } }); diff --git a/crates/codegen/src/rust.rs b/crates/codegen/src/rust.rs index a968ec7826a..0d9fa85b973 100644 --- a/crates/codegen/src/rust.rs +++ b/crates/codegen/src/rust.rs @@ -2,8 +2,8 @@ use super::code_indenter::{CodeIndenter, Indenter}; use super::util::{collect_case, iter_reducers, print_lines, type_ref_name}; use super::Lang; use crate::util::{ - iter_procedures, iter_tables, iter_types, iter_unique_cols, print_auto_generated_file_comment, - print_auto_generated_version_comment, + iter_procedures, iter_table_names_and_types, iter_tables, iter_types, iter_unique_cols, iter_views, + print_auto_generated_file_comment, print_auto_generated_version_comment, }; use crate::OutputFile; use convert_case::{Case, Casing}; @@ -930,12 +930,13 @@ fn reducer_flags_trait_name(reducer: &ReducerDef) -> String { format!("set_flags_for_{}", reducer_function_name(reducer)) } -/// Iterate over all of the Rust `mod`s for types, reducers and tables in the `module`. +/// Iterate over all of the Rust `mod`s for types, reducers, views, and tables in the `module`. fn iter_module_names(module: &ModuleDef) -> impl Iterator + '_ { itertools::chain!( iter_types(module).map(|ty| type_module_name(&ty.name)), iter_reducers(module).map(|r| reducer_module_name(&r.name)), iter_tables(module).map(|tbl| table_module_name(&tbl.name)), + iter_views(module).map(|view| table_module_name(&view.name)), iter_procedures(module).map(|proc| procedure_module_name(&proc.name)), ) } @@ -954,8 +955,8 @@ fn print_module_reexports(module: &ModuleDef, out: &mut Indenter) { let type_name = collect_case(Case::Pascal, ty.name.name_segments()); writeln!(out, "pub use {mod_name}::{type_name};") } - for table in iter_tables(module) { - let mod_name = table_module_name(&table.name); + for (table_name, _) in iter_table_names_and_types(module) { + let mod_name = table_module_name(table_name); // TODO: More precise reexport: we want: // - The trait name. // - The insert, delete and possibly update callback ids. @@ -1113,12 +1114,12 @@ fn print_db_update_defn(module: &ModuleDef, out: &mut Indenter) { out.delimited_block( "pub struct DbUpdate {", |out| { - for table in iter_tables(module) { + for (table_name, product_type_ref) in iter_table_names_and_types(module) { writeln!( out, "{}: __sdk::TableUpdate<{}>,", - table_method_name(&table.name), - type_ref_name(module, table.product_type_ref), + table_method_name(table_name), + type_ref_name(module, product_type_ref), ); } }, @@ -1137,13 +1138,13 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate { match &table_update.table_name[..] { ", |out| { - for table in iter_tables(module) { + for (table_name, _) in iter_table_names_and_types(module) { writeln!( out, "{:?} => db_update.{}.append({}::parse_table_update(table_update)?),", - table.name.deref(), - table_method_name(&table.name), - table_module_name(&table.name), + table_name.deref(), + table_method_name(table_name), + table_module_name(table_name), ); } }, @@ -1181,21 +1182,28 @@ impl __sdk::InModule for DbUpdate {{ let mut diff = AppliedDiff::default(); ", |out| { - for table in iter_tables(module) { - let with_updates = table - .primary_key - .map(|col| { - let pk_field = table.get_column(col).unwrap().name.deref().to_case(Case::Snake); - format!(".with_updates_by_pk(|row| &row.{pk_field})") - }) - .unwrap_or_default(); - - let field_name = table_method_name(&table.name); + for (table_name, product_type_ref, with_updates) in itertools::chain!( + iter_tables(module).map(|table| { + ( + &table.name, + table.product_type_ref, + table + .primary_key + .map(|col| { + let pk_field = table.get_column(col).unwrap().name.deref().to_case(Case::Snake); + format!(".with_updates_by_pk(|row| &row.{pk_field})") + }) + .unwrap_or_default(), + ) + }), + iter_views(module).map(|view| (&view.name, view.product_type_ref, "".into())) + ) { + let field_name = table_method_name(table_name); writeln!( out, "diff.{field_name} = cache.apply_diff_to_table::<{}>({:?}, &self.{field_name}){with_updates};", - type_ref_name(module, table.product_type_ref), - table.name.deref(), + type_ref_name(module, product_type_ref), + table_name.deref(), ); } }, @@ -1215,12 +1223,12 @@ fn print_applied_diff_defn(module: &ModuleDef, out: &mut Indenter) { out.delimited_block( "pub struct AppliedDiff<'r> {", |out| { - for table in iter_tables(module) { + for (table_name, product_type_ref) in iter_table_names_and_types(module) { writeln!( out, "{}: __sdk::TableAppliedDiff<'r, {}>,", - table_method_name(&table.name), - type_ref_name(module, table.product_type_ref), + table_method_name(table_name), + type_ref_name(module, product_type_ref), ); } // Also write a `PhantomData` field which uses the lifetime `r`, @@ -1248,13 +1256,13 @@ impl __sdk::InModule for AppliedDiff<'_> {{ out.delimited_block( "fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks) {", |out| { - for table in iter_tables(module) { + for (table_name, product_type_ref) in iter_table_names_and_types(module) { writeln!( out, "callbacks.invoke_table_row_callbacks::<{}>({:?}, &self.{}, event);", - type_ref_name(module, table.product_type_ref), - table.name.deref(), - table_method_name(&table.name), + type_ref_name(module, product_type_ref), + table_name.deref(), + table_method_name(table_name), ); } }, @@ -1290,8 +1298,8 @@ type SubscriptionHandle = SubscriptionHandle; out.delimited_block( "fn register_tables(client_cache: &mut __sdk::ClientCache) {", |out| { - for table in iter_tables(module) { - writeln!(out, "{}::register_table(client_cache);", table_module_name(&table.name)); + for (table_name, _) in iter_table_names_and_types(module) { + writeln!(out, "{}::register_table(client_cache);", table_module_name(table_name)); } }, "}\n", diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index 135c5852c67..1bd20e018d2 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -1,6 +1,6 @@ use crate::util::{ - is_reducer_invokable, iter_reducers, iter_tables, iter_types, iter_unique_cols, - print_auto_generated_version_comment, + is_reducer_invokable, iter_reducers, iter_table_names_and_types, iter_tables, iter_types, iter_unique_cols, + iter_views, print_auto_generated_version_comment, }; use crate::{indent_scope, OutputFile}; @@ -341,10 +341,9 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) writeln!(out); writeln!(out, "// Import and reexport all table handle types"); - for table in iter_tables(module) { - let table_name = &table.name; + for (table_name, _) in iter_table_names_and_types(module) { let table_module_name = table_module_name(table_name) + ".ts"; - let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); + let table_name_pascalcase = table_name.deref().to_case(Case::Pascal); let table_handle = table_name_pascalcase.clone() + "TableHandle"; writeln!(out, "import {{ {table_handle} }} from \"./{table_module_name}\";"); writeln!(out, "export {{ {table_handle} }};"); @@ -366,15 +365,31 @@ removeOnUpdate = (cb: (ctx: EventContext, onRow: {row_type}, newRow: {row_type}) out.indent(1); writeln!(out, "tables: {{"); out.indent(1); - for table in iter_tables(module) { - let type_ref = table.product_type_ref; + for (table_name, product_type_ref, schema) in itertools::chain!( + iter_tables(module).map(|def| { + ( + &def.name, + def.product_type_ref, + TableSchema::from_module_def(module, def, (), 0.into()), + ) + }), + iter_views(module).map(|def| { + ( + &def.name, + def.product_type_ref, + TableSchema::from_view_def_for_codegen(module, def), + ) + }) + ) { + let table_name = table_name.deref(); + let type_ref = product_type_ref; let row_type = type_ref_name(module, type_ref); - let schema = TableSchema::from_module_def(module, table, (), 0.into()) + let schema = schema .validated() .expect("Failed to generate table due to validation errors"); - writeln!(out, "{}: {{", table.name); + writeln!(out, "{}: {{", table_name); out.indent(1); - writeln!(out, "tableName: \"{}\" as const,", table.name); + writeln!(out, "tableName: \"{}\" as const,", table_name); writeln!(out, "rowType: {row_type}.getTypeScriptAlgebraicType(),"); if let Some(pk) = schema.pk() { // This is left here so we can release the codegen change before releasing a new @@ -612,13 +627,13 @@ fn print_remote_tables(module: &ModuleDef, out: &mut Indenter) { out.indent(1); writeln!(out, "constructor(private connection: __DbConnectionImpl) {{}}"); - for table in iter_tables(module) { + for (table_name, product_type_ref) in iter_table_names_and_types(module) { writeln!(out); - let table_name = table.name.deref(); - let table_name_pascalcase = table.name.deref().to_case(Case::Pascal); - let table_name_camelcase = table.name.deref().to_case(Case::Camel); + let table_name = table_name.deref(); + let table_name_pascalcase = table_name.to_case(Case::Pascal); + let table_name_camelcase = table_name.to_case(Case::Camel); let table_handle = table_name_pascalcase.clone() + "TableHandle"; - let type_ref = table.product_type_ref; + let type_ref = product_type_ref; let row_type = type_ref_name(module, type_ref); writeln!(out, "get {table_name_camelcase}(): {table_handle}<'{table_name}'> {{"); out.with_indent(|out| { diff --git a/crates/codegen/src/unrealcpp.rs b/crates/codegen/src/unrealcpp.rs index 280b1be4475..0c473d9656d 100644 --- a/crates/codegen/src/unrealcpp.rs +++ b/crates/codegen/src/unrealcpp.rs @@ -1,7 +1,8 @@ //! Autogenerated Unreal‑C++ code‑gen backend for SpacetimeDB CLI use crate::code_indenter::CodeIndenter; use crate::util::{ - collect_case, fmt_fn, iter_tables, print_auto_generated_file_comment, print_auto_generated_version_comment, + collect_case, fmt_fn, iter_table_names_and_types, print_auto_generated_file_comment, + print_auto_generated_version_comment, }; use crate::util::{iter_indexes, iter_reducers}; use crate::Lang; @@ -683,8 +684,8 @@ impl Lang for UnrealCpp<'_> { writeln!(client_h); writeln!(client_h, "/** Forward declaration for tables */"); - for table in iter_tables(module) { - let table_pascal = type_ref_name(module, table.product_type_ref); + for (_, product_type_ref) in iter_table_names_and_types(module) { + let table_pascal = type_ref_name(module, product_type_ref); writeln!(client_h, "class U{table_pascal}Table;"); } writeln!(client_h, "/***/"); @@ -774,12 +775,11 @@ impl Lang for UnrealCpp<'_> { }); // Build table includes - let table_includes: Vec = module - .tables() - .map(|table| { + let table_includes: Vec = iter_table_names_and_types(module) + .map(|(_, product_type_ref)| { format!( "ModuleBindings/Tables/{}Table.g.h", - type_ref_name(module, table.product_type_ref) + type_ref_name(module, product_type_ref) ) }) .collect(); @@ -1856,14 +1856,14 @@ fn generate_remote_tables_class(output: &mut UnrealCppAutogen, module: &ModuleDe writeln!(output); // Generate table handle properties - for table in module.tables() { - let table_pascal = type_ref_name(module, table.product_type_ref); + for (table_name, product_type_ref) in iter_table_names_and_types(module) { + let table_pascal = type_ref_name(module, product_type_ref); writeln!(output, " UPROPERTY(BlueprintReadOnly, Category=\"SpacetimeDB\")"); writeln!( output, " U{table_pascal}Table* {};", - table.name.deref().to_case(Case::Pascal) + table_name.deref().to_case(Case::Pascal) ); writeln!(output); } @@ -2357,16 +2357,16 @@ fn generate_client_implementation(output: &mut UnrealCppAutogen, module: &Module writeln!(output, "\tReducers->SetCallReducerFlags = SetReducerFlags;"); writeln!(output, "\tReducers->Conn = this;"); writeln!(output); - for table in module.tables() { - let table_pascal = type_ref_name(module, table.product_type_ref); - let table_name = table.name.deref(); + for (table_name, product_type_ref) in iter_table_names_and_types(module) { + let table_pascal = type_ref_name(module, product_type_ref); + let table_name = table_name.deref(); writeln!( output, "\tRegisterTable(TEXT(\"{}\"), Db->{});", table_pascal, table_pascal, table_name, - table.name.deref().to_case(Case::Pascal) + table_name.to_case(Case::Pascal) ); } writeln!(output, "}}"); @@ -2412,23 +2412,23 @@ fn generate_client_implementation(output: &mut UnrealCppAutogen, module: &Module writeln!(output, "{{"); writeln!(output); writeln!(output, "\t/** Creating tables */"); - for table in module.tables() { - let table_pascal = type_ref_name(module, table.product_type_ref); + for (table_name, product_type_ref) in iter_table_names_and_types(module) { + let table_pascal = type_ref_name(module, product_type_ref); writeln!( output, "\t{} = NewObject(this);", - table.name.deref().to_case(Case::Pascal), + table_name.deref().to_case(Case::Pascal), table_pascal ); } writeln!(output, "\t/**/"); writeln!(output); writeln!(output, "\t/** Initialization */"); - for table in module.tables() { + for (table_name, _) in iter_table_names_and_types(module) { writeln!( output, "\t{}->PostInitialize();", - table.name.deref().to_case(Case::Pascal) + table_name.deref().to_case(Case::Pascal) ); } writeln!(output, "\t/**/"); @@ -3095,10 +3095,8 @@ fn collect_optional_types(module: &ModuleDef) -> HashSet { } // Collect from all tables - for table in module.tables() { - let product_type = module.typespace_for_generate()[table.product_type_ref] - .as_product() - .unwrap(); + for (_, product_type_ref) in iter_table_names_and_types(module) { + let product_type = module.typespace_for_generate()[product_type_ref].as_product().unwrap(); for (_, field_ty) in &product_type.elements { collect_from_type(module, field_ty, &mut optional_types); } diff --git a/crates/codegen/src/util.rs b/crates/codegen/src/util.rs index 1065fc39152..bbe4eac688a 100644 --- a/crates/codegen/src/util.rs +++ b/crates/codegen/src/util.rs @@ -12,8 +12,8 @@ use spacetimedb_lib::sats::layout::PrimitiveType; use spacetimedb_lib::version; use spacetimedb_lib::{db::raw_def::v9::Lifecycle, sats::AlgebraicTypeRef}; use spacetimedb_primitives::ColList; -use spacetimedb_schema::type_for_generate::ProductTypeDef; use spacetimedb_schema::{def::ProcedureDef, schema::TableSchema}; +use spacetimedb_schema::{def::ViewDef, type_for_generate::ProductTypeDef}; use spacetimedb_schema::{ def::{IndexDef, TableDef, TypeDef}, type_for_generate::TypespaceForGenerate, @@ -112,6 +112,25 @@ pub(super) fn iter_tables(module: &ModuleDef) -> impl Iterator module.tables().sorted_by_key(|table| &table.name) } +/// Iterate over all the table names defined by the module (in alphabetical order) and their product types. +/// Note, this also includes view names because from the perspective of codegen, views are tables. +/// +/// Sorting is necessary to have deterministic reproducible codegen. +pub(super) fn iter_table_names_and_types(module: &ModuleDef) -> impl Iterator { + module + .tables() + .map(|def| (&def.name, def.product_type_ref)) + .chain(module.views().map(|def| (&def.name, def.product_type_ref))) + .sorted_by_key(|(name, _)| *name) +} + +/// Iterate over all the [`ViewDef`]s defined by the module, in alphabetical order by name. +/// +/// Sorting is necessary to have deterministic reproducible codegen. +pub(super) fn iter_views(module: &ModuleDef) -> impl Iterator { + module.views().sorted_by_key(|view| &view.name) +} + pub(super) fn iter_unique_cols<'a>( typespace: &'a TypespaceForGenerate, schema: &'a TableSchema, diff --git a/crates/codegen/tests/snapshots/codegen__codegen_csharp.snap b/crates/codegen/tests/snapshots/codegen__codegen_csharp.snap index 4d2796d4811..38e8c9eafac 100644 --- a/crates/codegen/tests/snapshots/codegen__codegen_csharp.snap +++ b/crates/codegen/tests/snapshots/codegen__codegen_csharp.snap @@ -1011,6 +1011,7 @@ namespace SpacetimeDB { AddTable(HasSpecialStuff = new(conn)); AddTable(LoggedOutPlayer = new(conn)); + AddTable(MyPlayer = new(conn)); AddTable(Person = new(conn)); AddTable(PkMultiIdentity = new(conn)); AddTable(Player = new(conn)); diff --git a/crates/codegen/tests/snapshots/codegen__codegen_rust.snap b/crates/codegen/tests/snapshots/codegen__codegen_rust.snap index a334dec7a9c..995543ac4fa 100644 --- a/crates/codegen/tests/snapshots/codegen__codegen_rust.snap +++ b/crates/codegen/tests/snapshots/codegen__codegen_rust.snap @@ -1421,6 +1421,7 @@ pub mod test_a_table; pub mod test_d_table; pub mod test_e_table; pub mod test_f_table; +pub mod my_player_table; pub mod return_value_procedure; pub mod sleep_one_second_procedure; @@ -1442,6 +1443,7 @@ pub use namespace_test_c_type::NamespaceTestC; pub use namespace_test_f_type::NamespaceTestF; pub use has_special_stuff_table::*; pub use logged_out_player_table::*; +pub use my_player_table::*; pub use person_table::*; pub use pk_multi_identity_table::*; pub use player_table::*; @@ -1568,6 +1570,7 @@ fn try_from(value: __ws::ReducerCallInfo<__ws::BsatnFormat>) -> __sdk::Result, logged_out_player: __sdk::TableUpdate, + my_player: __sdk::TableUpdate, person: __sdk::TableUpdate, pk_multi_identity: __sdk::TableUpdate, player: __sdk::TableUpdate, @@ -1590,6 +1593,7 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate { "has_special_stuff" => db_update.has_special_stuff.append(has_special_stuff_table::parse_table_update(table_update)?), "logged_out_player" => db_update.logged_out_player.append(logged_out_player_table::parse_table_update(table_update)?), + "my_player" => db_update.my_player.append(my_player_table::parse_table_update(table_update)?), "person" => db_update.person.append(person_table::parse_table_update(table_update)?), "pk_multi_identity" => db_update.pk_multi_identity.append(pk_multi_identity_table::parse_table_update(table_update)?), "player" => db_update.player.append(player_table::parse_table_update(table_update)?), @@ -1634,6 +1638,7 @@ impl __sdk::DbUpdate for DbUpdate { diff.test_d = cache.apply_diff_to_table::("test_d", &self.test_d); diff.test_e = cache.apply_diff_to_table::("test_e", &self.test_e).with_updates_by_pk(|row| &row.id); diff.test_f = cache.apply_diff_to_table::("test_f", &self.test_f); + diff.my_player = cache.apply_diff_to_table::("my_player", &self.my_player); diff } @@ -1645,6 +1650,7 @@ impl __sdk::DbUpdate for DbUpdate { pub struct AppliedDiff<'r> { has_special_stuff: __sdk::TableAppliedDiff<'r, HasSpecialStuff>, logged_out_player: __sdk::TableAppliedDiff<'r, Player>, + my_player: __sdk::TableAppliedDiff<'r, Player>, person: __sdk::TableAppliedDiff<'r, Person>, pk_multi_identity: __sdk::TableAppliedDiff<'r, PkMultiIdentity>, player: __sdk::TableAppliedDiff<'r, Player>, @@ -1667,6 +1673,7 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks) { callbacks.invoke_table_row_callbacks::("has_special_stuff", &self.has_special_stuff, event); callbacks.invoke_table_row_callbacks::("logged_out_player", &self.logged_out_player, event); + callbacks.invoke_table_row_callbacks::("my_player", &self.my_player, event); callbacks.invoke_table_row_callbacks::("person", &self.person, event); callbacks.invoke_table_row_callbacks::("pk_multi_identity", &self.pk_multi_identity, event); callbacks.invoke_table_row_callbacks::("player", &self.player, event); @@ -2395,6 +2402,7 @@ impl __sdk::SpacetimeModule for RemoteModule { fn register_tables(client_cache: &mut __sdk::ClientCache) { has_special_stuff_table::register_table(client_cache); logged_out_player_table::register_table(client_cache); + my_player_table::register_table(client_cache); person_table::register_table(client_cache); pk_multi_identity_table::register_table(client_cache); player_table::register_table(client_cache); diff --git a/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap b/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap index 799e85147f9..c55a5a64fd6 100644 --- a/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap +++ b/crates/codegen/tests/snapshots/codegen__codegen_typescript.snap @@ -862,6 +862,8 @@ import { HasSpecialStuffTableHandle } from "./has_special_stuff_table.ts"; export { HasSpecialStuffTableHandle }; import { LoggedOutPlayerTableHandle } from "./logged_out_player_table.ts"; export { LoggedOutPlayerTableHandle }; +import { MyPlayerTableHandle } from "./my_player_table.ts"; +export { MyPlayerTableHandle }; import { PersonTableHandle } from "./person_table.ts"; export { PersonTableHandle }; import { PkMultiIdentityTableHandle } from "./pk_multi_identity_table.ts"; @@ -997,6 +999,10 @@ const REMOTE_MODULE = { tableName: "test_f" as const, rowType: TestFoobar.getTypeScriptAlgebraicType(), }, + my_player: { + tableName: "my_player" as const, + rowType: Player.getTypeScriptAlgebraicType(), + }, }, reducers: { add: { @@ -1383,6 +1389,11 @@ export class RemoteTables { return new LoggedOutPlayerTableHandle((this.connection as unknown as { clientCache: __ClientCache }).clientCache.getOrCreateTable(REMOTE_MODULE.tables.logged_out_player)); } + get myPlayer(): MyPlayerTableHandle<'my_player'> { + // clientCache is a private property + return new MyPlayerTableHandle((this.connection as unknown as { clientCache: __ClientCache }).clientCache.getOrCreateTable(REMOTE_MODULE.tables.my_player)); + } + get person(): PersonTableHandle<'person'> { // clientCache is a private property return new PersonTableHandle((this.connection as unknown as { clientCache: __ClientCache }).clientCache.getOrCreateTable(REMOTE_MODULE.tables.person)); From 1afba755a12c8580f665e597ae7fe4ad42cc859b Mon Sep 17 00:00:00 2001 From: rekhoff Date: Tue, 18 Nov 2025 20:02:58 -0800 Subject: [PATCH 10/38] Minor fixes --- modules/sdk-test-cs/Lib.cs | 2 +- .../examples~/regression-tests/client/Program.cs | 12 ++++++------ sdks/csharp/examples~/regression-tests/server/Lib.cs | 6 ------ 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/modules/sdk-test-cs/Lib.cs b/modules/sdk-test-cs/Lib.cs index e6616e62117..577ffc8cf50 100644 --- a/modules/sdk-test-cs/Lib.cs +++ b/modules/sdk-test-cs/Lib.cs @@ -2063,7 +2063,7 @@ public static void insert_user(ReducerContext ctx, string name, Identity identit [SpacetimeDB.View(Name = "my_user", Public = true)] public static Users? MyUser(ViewContext ctx) { - return ctx.Db.users.identity.Find(ctx.Sender) as Users; + return ctx.Db.users.identity.Find(ctx.Sender) as Users?; } [SpacetimeDB.Table(Name = "indexed_simple_enum", Public = true)] diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index 5ad1e2d1c6c..0942784f8b3 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -38,7 +38,7 @@ DbConnection ConnectToDB() } uint waiting = 0; -bool applied = false; +var applied = false; SubscriptionHandle? handle = null; void OnConnected(DbConnection conn, Identity identity, string authToken) @@ -133,21 +133,21 @@ void OnSubscriptionApplied(SubscriptionEventContext context) ValidateBTreeIndexes(ctx); waiting--; }); - - + + // Views test Log.Debug("Calling Iter on View"); var viewIterRows = context.Db.MyPlayer.Iter(); Debug.Assert(viewIterRows != null && viewIterRows.Any()); - + Log.Debug("Calling RemoteQuery on View"); var viewRemoteQueryRows = context.Db.MyPlayer.RemoteQuery("WHERE Id > 0"); Debug.Assert(viewRemoteQueryRows != null && viewRemoteQueryRows.Result.Length > 0); - + Log.Debug("Calling Iter on Anonymous View"); var anonViewIterRows = context.Db.PlayersForLevel.Iter(); Debug.Assert(anonViewIterRows != null && anonViewIterRows.Any()); - + Log.Debug("Calling RemoteQuery on Anonymous View"); var anonViewRemoteQueryRows = context.Db.PlayersForLevel.RemoteQuery($"WHERE Level = 1)"); Debug.Assert(anonViewRemoteQueryRows != null && anonViewRemoteQueryRows.Result.Length > 0); diff --git a/sdks/csharp/examples~/regression-tests/server/Lib.cs b/sdks/csharp/examples~/regression-tests/server/Lib.cs index f28711ec12e..303f293d82c 100644 --- a/sdks/csharp/examples~/regression-tests/server/Lib.cs +++ b/sdks/csharp/examples~/regression-tests/server/Lib.cs @@ -38,12 +38,6 @@ public partial struct PlayerLevel public ulong Level; } - [Type] - public partial struct TransportData - { - public int TroopCount; - } - [SpacetimeDB.Type] public partial struct PlayerAndLevel { From cd36a957862ff4efc6086c28d04773f461406e6f Mon Sep 17 00:00:00 2001 From: rekhoff Date: Wed, 19 Nov 2025 09:12:17 -0800 Subject: [PATCH 11/38] Update sdks/csharp/examples~/regression-tests/client/Program.cs Co-authored-by: joshua-spacetime Signed-off-by: rekhoff --- sdks/csharp/examples~/regression-tests/client/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index 0942784f8b3..684e6314c71 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -50,7 +50,7 @@ void OnConnected(DbConnection conn, Identity identity, string authToken) { throw err; }) - .Subscribe(["SELECT * FROM ExampleData", "SELECT * FROM MyPlayer"]); + .Subscribe(["SELECT * FROM ExampleData", "SELECT * FROM MyPlayer", "SELECT * FROM PlayersForLevel"]); conn.Reducers.OnAdd += (ReducerEventContext ctx, uint id, uint indexed) => { From 6340f61a2b4fead44f2a22b4343c0e0b4d6f4f70 Mon Sep 17 00:00:00 2001 From: Jason Larabie Date: Wed, 19 Nov 2025 15:28:49 -0800 Subject: [PATCH 12/38] Fixed up the C# module bindings for Views --- .../BSATN.Runtime/BSATN/Runtime.cs | 6 + .../diag/snapshots/Module#FFI.verified.cs | 285 ++++--- .../server/snapshots/Module#FFI.verified.cs | 82 +- crates/bindings-csharp/Codegen/Module.cs | 92 ++- .../Runtime/Internal/Module.cs | 2 +- crates/bindings-csharp/Runtime/bindings.c | 12 + .../module_bindings/SpacetimeDBClient.g.cs | 2 +- .../regression-tests/client/Program.cs | 3 +- .../module_bindings/SpacetimeDBClient.g.cs | 768 +++++++++--------- .../module_bindings/SpacetimeDBClient.g.cs | 2 +- 10 files changed, 703 insertions(+), 551 deletions(-) diff --git a/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs b/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs index bce87b273bc..9a7a617a2ab 100644 --- a/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs +++ b/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs @@ -228,6 +228,12 @@ public void Write(BinaryWriter writer, Inner? value) public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) => AlgebraicType.MakeOption(innerRW.GetAlgebraicType(registrar)); + + // Return a List BSATN serializer that can serialize this option as an array + public static List GetListSerializer() + { + return new List(); + } } public readonly struct Bool : IReadWrite diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs index f682dc6c61b..c345d1b13c9 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs @@ -1030,32 +1030,31 @@ public sealed class Local } } -sealed class ViewDefIndexNoMutationViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView +sealed class ViewDefNoContextViewDispatcher : global::SpacetimeDB.Internal.IView { - private static readonly SpacetimeDB.BSATN.ValueOption returnRW = new(); - - public SpacetimeDB.Internal.RawViewDefV9 MakeAnonymousViewDef( + public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "ViewDefIndexNoMutation", - Index: 1, + Name: "ViewDefNoContext", + Index: 0, IsPublic: true, - IsAnonymous: true, + IsAnonymous: false, Params: [], - ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType( + ReturnType: new SpacetimeDB.BSATN.List().GetAlgebraicType( registrar ) ); public byte[] Invoke( System.IO.BinaryReader reader, - global::SpacetimeDB.Internal.IAnonymousViewContext ctx + global::SpacetimeDB.Internal.IViewContext ctx ) { try { - var returnValue = Module.ViewDefIndexNoMutation((SpacetimeDB.AnonymousViewContext)ctx); + var returnValue = Module.ViewDefNoContext((SpacetimeDB.ViewContext)ctx); + SpacetimeDB.BSATN.List returnRW = new(); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); returnRW.Write(writer, returnValue); @@ -1063,38 +1062,37 @@ public byte[] Invoke( } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'ViewDefIndexNoMutation': " + e); + global::SpacetimeDB.Log.Error("Error in view 'ViewDefNoContext': " + e); throw; } } } -sealed class ViewDefNoAnonIdentityViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView +sealed class ViewDefNoPublicViewDispatcher : global::SpacetimeDB.Internal.IView { - private static readonly SpacetimeDB.BSATN.ValueOption returnRW = new(); - - public SpacetimeDB.Internal.RawViewDefV9 MakeAnonymousViewDef( + public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "ViewDefNoAnonIdentity", - Index: 2, - IsPublic: true, - IsAnonymous: true, + Name: "ViewDefNoPublic", + Index: 1, + IsPublic: false, + IsAnonymous: false, Params: [], - ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType( + ReturnType: new SpacetimeDB.BSATN.List().GetAlgebraicType( registrar ) ); public byte[] Invoke( System.IO.BinaryReader reader, - global::SpacetimeDB.Internal.IAnonymousViewContext ctx + global::SpacetimeDB.Internal.IViewContext ctx ) { try { - var returnValue = Module.ViewDefNoAnonIdentity((SpacetimeDB.AnonymousViewContext)ctx); + var returnValue = Module.ViewDefNoPublic((SpacetimeDB.ViewContext)ctx); + SpacetimeDB.BSATN.List returnRW = new(); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); returnRW.Write(writer, returnValue); @@ -1102,22 +1100,20 @@ public byte[] Invoke( } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'ViewDefNoAnonIdentity': " + e); + global::SpacetimeDB.Log.Error("Error in view 'ViewDefNoPublic': " + e); throw; } } } -sealed class ViewDefNoContextViewDispatcher : global::SpacetimeDB.Internal.IView +sealed class ViewDefWrongContextViewDispatcher : global::SpacetimeDB.Internal.IView { - private static readonly SpacetimeDB.BSATN.List returnRW = new(); - public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "ViewDefNoContext", - Index: 1, + Name: "ViewDefWrongContext", + Index: 2, IsPublic: true, IsAnonymous: false, Params: [], @@ -1133,7 +1129,8 @@ public byte[] Invoke( { try { - var returnValue = Module.ViewDefNoContext((SpacetimeDB.ViewContext)ctx); + var returnValue = Module.ViewDefWrongContext((SpacetimeDB.ViewContext)ctx); + SpacetimeDB.BSATN.List returnRW = new(); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); returnRW.Write(writer, returnValue); @@ -1141,38 +1138,35 @@ public byte[] Invoke( } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'ViewDefNoContext': " + e); + global::SpacetimeDB.Log.Error("Error in view 'ViewDefWrongContext': " + e); throw; } } } -sealed class ViewDefNoIterViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView +sealed class ViewDefWrongReturnViewDispatcher : global::SpacetimeDB.Internal.IView { - private static readonly SpacetimeDB.BSATN.ValueOption returnRW = new(); - - public SpacetimeDB.Internal.RawViewDefV9 MakeAnonymousViewDef( + public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "ViewDefNoIter", + Name: "ViewDefWrongReturn", Index: 3, IsPublic: true, - IsAnonymous: true, + IsAnonymous: false, Params: [], - ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType( - registrar - ) + ReturnType: new Player.BSATN().GetAlgebraicType(registrar) ); public byte[] Invoke( System.IO.BinaryReader reader, - global::SpacetimeDB.Internal.IAnonymousViewContext ctx + global::SpacetimeDB.Internal.IViewContext ctx ) { try { - var returnValue = Module.ViewDefNoIter((SpacetimeDB.AnonymousViewContext)ctx); + var returnValue = Module.ViewDefWrongReturn((SpacetimeDB.ViewContext)ctx); + Player.BSATN returnRW = new(); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); returnRW.Write(writer, returnValue); @@ -1180,26 +1174,24 @@ public byte[] Invoke( } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'ViewDefNoIter': " + e); + global::SpacetimeDB.Log.Error("Error in view 'ViewDefWrongReturn': " + e); throw; } } } -sealed class ViewDefNoPublicViewDispatcher : global::SpacetimeDB.Internal.IView +sealed class ViewNoDeleteViewDispatcher : global::SpacetimeDB.Internal.IView { - private static readonly SpacetimeDB.BSATN.List returnRW = new(); - public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "ViewDefNoPublic", - Index: 2, - IsPublic: false, + Name: "ViewNoDelete", + Index: 4, + IsPublic: true, IsAnonymous: false, Params: [], - ReturnType: new SpacetimeDB.BSATN.List().GetAlgebraicType( + ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType( registrar ) ); @@ -1211,154 +1203,161 @@ public byte[] Invoke( { try { - var returnValue = Module.ViewDefNoPublic((SpacetimeDB.ViewContext)ctx); + var returnValue = Module.ViewNoDelete((SpacetimeDB.ViewContext)ctx); + var listSerializer = SpacetimeDB.BSATN.ValueOption< + Player, + Player.BSATN + >.GetListSerializer(); + var listValue = ModuleRegistration.ToListOrEmpty(returnValue); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); - returnRW.Write(writer, returnValue); + listSerializer.Write(writer, listValue); return output.ToArray(); } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'ViewDefNoPublic': " + e); + global::SpacetimeDB.Log.Error("Error in view 'ViewNoDelete': " + e); throw; } } } -sealed class ViewDefReturnsNotASpacetimeTypeViewDispatcher - : global::SpacetimeDB.Internal.IAnonymousView +sealed class ViewNoInsertViewDispatcher : global::SpacetimeDB.Internal.IView { - private static readonly SpacetimeDB.BSATN.ValueOption< - NotSpacetimeType, - NotSpacetimeType.BSATN - > returnRW = new(); - - public SpacetimeDB.Internal.RawViewDefV9 MakeAnonymousViewDef( + public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "ViewDefReturnsNotASpacetimeType", - Index: 4, + Name: "ViewNoInsert", + Index: 5, IsPublic: true, - IsAnonymous: true, + IsAnonymous: false, Params: [], - ReturnType: new SpacetimeDB.BSATN.ValueOption< - NotSpacetimeType, - NotSpacetimeType.BSATN - >().GetAlgebraicType(registrar) + ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType( + registrar + ) ); public byte[] Invoke( System.IO.BinaryReader reader, - global::SpacetimeDB.Internal.IAnonymousViewContext ctx + global::SpacetimeDB.Internal.IViewContext ctx ) { try { - var returnValue = Module.ViewDefReturnsNotASpacetimeType( - (SpacetimeDB.AnonymousViewContext)ctx - ); + var returnValue = Module.ViewNoInsert((SpacetimeDB.ViewContext)ctx); + var listSerializer = SpacetimeDB.BSATN.ValueOption< + Player, + Player.BSATN + >.GetListSerializer(); + var listValue = ModuleRegistration.ToListOrEmpty(returnValue); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); - returnRW.Write(writer, returnValue); + listSerializer.Write(writer, listValue); return output.ToArray(); } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'ViewDefReturnsNotASpacetimeType': " + e); + global::SpacetimeDB.Log.Error("Error in view 'ViewNoInsert': " + e); throw; } } } -sealed class ViewDefWrongContextViewDispatcher : global::SpacetimeDB.Internal.IView +sealed class ViewDefIndexNoMutationViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView { - private static readonly SpacetimeDB.BSATN.List returnRW = new(); - - public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( + public SpacetimeDB.Internal.RawViewDefV9 MakeAnonymousViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "ViewDefWrongContext", - Index: 3, + Name: "ViewDefIndexNoMutation", + Index: 0, IsPublic: true, - IsAnonymous: false, + IsAnonymous: true, Params: [], - ReturnType: new SpacetimeDB.BSATN.List().GetAlgebraicType( + ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType( registrar ) ); public byte[] Invoke( System.IO.BinaryReader reader, - global::SpacetimeDB.Internal.IViewContext ctx + global::SpacetimeDB.Internal.IAnonymousViewContext ctx ) { try { - var returnValue = Module.ViewDefWrongContext((SpacetimeDB.ViewContext)ctx); + var returnValue = Module.ViewDefIndexNoMutation((SpacetimeDB.AnonymousViewContext)ctx); + var listSerializer = SpacetimeDB.BSATN.ValueOption< + Player, + Player.BSATN + >.GetListSerializer(); + var listValue = ModuleRegistration.ToListOrEmpty(returnValue); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); - returnRW.Write(writer, returnValue); + listSerializer.Write(writer, listValue); return output.ToArray(); } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'ViewDefWrongContext': " + e); + global::SpacetimeDB.Log.Error("Error in view 'ViewDefIndexNoMutation': " + e); throw; } } } -sealed class ViewDefWrongReturnViewDispatcher : global::SpacetimeDB.Internal.IView +sealed class ViewDefNoAnonIdentityViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView { - private static readonly Player.BSATN returnRW = new(); - - public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( + public SpacetimeDB.Internal.RawViewDefV9 MakeAnonymousViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "ViewDefWrongReturn", - Index: 4, + Name: "ViewDefNoAnonIdentity", + Index: 1, IsPublic: true, - IsAnonymous: false, + IsAnonymous: true, Params: [], - ReturnType: new Player.BSATN().GetAlgebraicType(registrar) + ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType( + registrar + ) ); public byte[] Invoke( System.IO.BinaryReader reader, - global::SpacetimeDB.Internal.IViewContext ctx + global::SpacetimeDB.Internal.IAnonymousViewContext ctx ) { try { - var returnValue = Module.ViewDefWrongReturn((SpacetimeDB.ViewContext)ctx); + var returnValue = Module.ViewDefNoAnonIdentity((SpacetimeDB.AnonymousViewContext)ctx); + var listSerializer = SpacetimeDB.BSATN.ValueOption< + Player, + Player.BSATN + >.GetListSerializer(); + var listValue = ModuleRegistration.ToListOrEmpty(returnValue); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); - returnRW.Write(writer, returnValue); + listSerializer.Write(writer, listValue); return output.ToArray(); } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'ViewDefWrongReturn': " + e); + global::SpacetimeDB.Log.Error("Error in view 'ViewDefNoAnonIdentity': " + e); throw; } } } -sealed class ViewNoDeleteViewDispatcher : global::SpacetimeDB.Internal.IView +sealed class ViewDefNoIterViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView { - private static readonly SpacetimeDB.BSATN.ValueOption returnRW = new(); - - public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( + public SpacetimeDB.Internal.RawViewDefV9 MakeAnonymousViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "ViewNoDelete", - Index: 5, + Name: "ViewDefNoIter", + Index: 2, IsPublic: true, - IsAnonymous: false, + IsAnonymous: true, Params: [], ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType( registrar @@ -1367,59 +1366,71 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public byte[] Invoke( System.IO.BinaryReader reader, - global::SpacetimeDB.Internal.IViewContext ctx + global::SpacetimeDB.Internal.IAnonymousViewContext ctx ) { try { - var returnValue = Module.ViewNoDelete((SpacetimeDB.ViewContext)ctx); + var returnValue = Module.ViewDefNoIter((SpacetimeDB.AnonymousViewContext)ctx); + var listSerializer = SpacetimeDB.BSATN.ValueOption< + Player, + Player.BSATN + >.GetListSerializer(); + var listValue = ModuleRegistration.ToListOrEmpty(returnValue); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); - returnRW.Write(writer, returnValue); + listSerializer.Write(writer, listValue); return output.ToArray(); } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'ViewNoDelete': " + e); + global::SpacetimeDB.Log.Error("Error in view 'ViewDefNoIter': " + e); throw; } } } -sealed class ViewNoInsertViewDispatcher : global::SpacetimeDB.Internal.IView +sealed class ViewDefReturnsNotASpacetimeTypeViewDispatcher + : global::SpacetimeDB.Internal.IAnonymousView { - private static readonly SpacetimeDB.BSATN.ValueOption returnRW = new(); - - public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( + public SpacetimeDB.Internal.RawViewDefV9 MakeAnonymousViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "ViewNoInsert", - Index: 6, + Name: "ViewDefReturnsNotASpacetimeType", + Index: 3, IsPublic: true, - IsAnonymous: false, + IsAnonymous: true, Params: [], - ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType( - registrar - ) + ReturnType: new SpacetimeDB.BSATN.ValueOption< + NotSpacetimeType, + NotSpacetimeType.BSATN + >().GetAlgebraicType(registrar) ); public byte[] Invoke( System.IO.BinaryReader reader, - global::SpacetimeDB.Internal.IViewContext ctx + global::SpacetimeDB.Internal.IAnonymousViewContext ctx ) { try { - var returnValue = Module.ViewNoInsert((SpacetimeDB.ViewContext)ctx); + var returnValue = Module.ViewDefReturnsNotASpacetimeType( + (SpacetimeDB.AnonymousViewContext)ctx + ); + var listSerializer = SpacetimeDB.BSATN.ValueOption< + NotSpacetimeType, + NotSpacetimeType.BSATN + >.GetListSerializer(); + var listValue = ModuleRegistration.ToListOrEmpty(returnValue); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); - returnRW.Write(writer, returnValue); + listSerializer.Write(writer, listValue); return output.ToArray(); } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'ViewNoInsert': " + e); + global::SpacetimeDB.Log.Error("Error in view 'ViewDefReturnsNotASpacetimeType': " + e); throw; } } @@ -1812,6 +1823,9 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } + public static List ToListOrEmpty(T? value) + where T : struct => value is null ? new List() : new List { value.Value }; + #if EXPERIMENTAL_WASM_AOT // In AOT mode we're building a library. // Main method won't be called automatically, so we need to export it as a preinit function. @@ -2147,6 +2161,33 @@ SpacetimeDB.Internal.BytesSink error args, error ); + + [UnmanagedCallersOnly(EntryPoint = "__call_view__")] + public static SpacetimeDB.Internal.Errno __call_view__( + uint id, + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) => + SpacetimeDB.Internal.Module.__call_view__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ); + + [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] + public static SpacetimeDB.Internal.Errno __call_view_anon__( + uint id, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink); #endif } diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs index 2ec9fd783d4..157f182a610 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs @@ -1055,19 +1055,16 @@ public sealed class Local } } -sealed class FindPublicTableByIdentityViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView +sealed class PublicTableByIdentityViewDispatcher : global::SpacetimeDB.Internal.IView { - private static readonly SpacetimeDB.BSATN.ValueOption returnRW = - new(); - - public SpacetimeDB.Internal.RawViewDefV9 MakeAnonymousViewDef( + public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "FindPublicTableByIdentity", + Name: "PublicTableByIdentity", Index: 0, IsPublic: true, - IsAnonymous: true, + IsAnonymous: false, Params: [], ReturnType: new SpacetimeDB.BSATN.ValueOption< PublicTable, @@ -1077,40 +1074,40 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public byte[] Invoke( System.IO.BinaryReader reader, - global::SpacetimeDB.Internal.IAnonymousViewContext ctx + global::SpacetimeDB.Internal.IViewContext ctx ) { try { - var returnValue = Module.FindPublicTableByIdentity( - (SpacetimeDB.AnonymousViewContext)ctx - ); + var returnValue = Module.PublicTableByIdentity((SpacetimeDB.ViewContext)ctx); + var listSerializer = SpacetimeDB.BSATN.ValueOption< + PublicTable, + PublicTable.BSATN + >.GetListSerializer(); + var listValue = ModuleRegistration.ToListOrEmpty(returnValue); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); - returnRW.Write(writer, returnValue); + listSerializer.Write(writer, listValue); return output.ToArray(); } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'FindPublicTableByIdentity': " + e); + global::SpacetimeDB.Log.Error("Error in view 'PublicTableByIdentity': " + e); throw; } } } -sealed class PublicTableByIdentityViewDispatcher : global::SpacetimeDB.Internal.IView +sealed class FindPublicTableByIdentityViewDispatcher : global::SpacetimeDB.Internal.IAnonymousView { - private static readonly SpacetimeDB.BSATN.ValueOption returnRW = - new(); - - public SpacetimeDB.Internal.RawViewDefV9 MakeViewDef( + public SpacetimeDB.Internal.RawViewDefV9 MakeAnonymousViewDef( SpacetimeDB.BSATN.ITypeRegistrar registrar ) => new global::SpacetimeDB.Internal.RawViewDefV9( - Name: "PublicTableByIdentity", + Name: "FindPublicTableByIdentity", Index: 0, IsPublic: true, - IsAnonymous: false, + IsAnonymous: true, Params: [], ReturnType: new SpacetimeDB.BSATN.ValueOption< PublicTable, @@ -1120,20 +1117,27 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar public byte[] Invoke( System.IO.BinaryReader reader, - global::SpacetimeDB.Internal.IViewContext ctx + global::SpacetimeDB.Internal.IAnonymousViewContext ctx ) { try { - var returnValue = Module.PublicTableByIdentity((SpacetimeDB.ViewContext)ctx); + var returnValue = Module.FindPublicTableByIdentity( + (SpacetimeDB.AnonymousViewContext)ctx + ); + var listSerializer = SpacetimeDB.BSATN.ValueOption< + PublicTable, + PublicTable.BSATN + >.GetListSerializer(); + var listValue = ModuleRegistration.ToListOrEmpty(returnValue); using var output = new System.IO.MemoryStream(); using var writer = new System.IO.BinaryWriter(output); - returnRW.Write(writer, returnValue); + listSerializer.Write(writer, listValue); return output.ToArray(); } catch (System.Exception e) { - global::SpacetimeDB.Log.Error("Error in view 'PublicTableByIdentity': " + e); + global::SpacetimeDB.Log.Error("Error in view 'FindPublicTableByIdentity': " + e); throw; } } @@ -1613,6 +1617,9 @@ public void Invoke(BinaryReader reader, SpacetimeDB.Internal.IReducerContext ctx } } + public static List ToListOrEmpty(T? value) + where T : struct => value is null ? new List() : new List { value.Value }; + #if EXPERIMENTAL_WASM_AOT // In AOT mode we're building a library. // Main method won't be called automatically, so we need to export it as a preinit function. @@ -1718,6 +1725,33 @@ SpacetimeDB.Internal.BytesSink error args, error ); + + [UnmanagedCallersOnly(EntryPoint = "__call_view__")] + public static SpacetimeDB.Internal.Errno __call_view__( + uint id, + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) => + SpacetimeDB.Internal.Module.__call_view__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ); + + [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] + public static SpacetimeDB.Internal.Errno __call_view_anon__( + uint id, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) => SpacetimeDB.Internal.Module.__call_view_anon__(id, args, sink); #endif } diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 04b4ce876bb..b3e9c8e6e85 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -921,14 +921,6 @@ record ViewDeclaration public readonly EquatableArray Parameters; public readonly Scope Scope; - public static uint ViewIndexCounter = 0; - - public static uint GetNextViewIndex() => ViewIndexCounter++; - - public static uint AnonViewIndexCounter = 0; - - public static uint GetNextAnonViewIndex() => AnonViewIndexCounter++; - public ViewDeclaration(GeneratorAttributeSyntaxContext context, DiagReporter diag) { var methodSyntax = (MethodDeclarationSyntax)context.TargetNode; @@ -1001,7 +993,7 @@ public string GenerateViewDef(uint Index) => ); """; - public string GenerateDispatcherClass() + public string GenerateDispatcherClass(uint index) { var paramReads = string.Join( "\n ", @@ -1022,15 +1014,30 @@ public string GenerateDispatcherClass() ? "SpacetimeDB.AnonymousViewContext" : "SpacetimeDB.ViewContext"; + var isValueOption = ReturnType.BSATNName.StartsWith("SpacetimeDB.BSATN.ValueOption"); + var writeOutput = isValueOption + ? $$$""" + var listSerializer = {{{ReturnType.BSATNName}}}.GetListSerializer(); + var listValue = ModuleRegistration.ToListOrEmpty(returnValue); + using var output = new System.IO.MemoryStream(); + using var writer = new System.IO.BinaryWriter(output); + listSerializer.Write(writer, listValue); + return output.ToArray(); + """ + : $$$""" + {{{ReturnType.BSATNName}}} returnRW = new(); + using var output = new System.IO.MemoryStream(); + using var writer = new System.IO.BinaryWriter(output); + returnRW.Write(writer, returnValue); + return output.ToArray(); + """; + var invocationArgs = Parameters.Length == 0 ? "" : ", " + string.Join(", ", Parameters.Select(p => p.Name)); - var index = IsAnonymous ? GetNextAnonViewIndex() : GetNextViewIndex(); return $$$""" sealed class {{{Name}}}ViewDispatcher : {{{interfaceName}}} { {{{MemberDeclaration.GenerateBsatnFields(Accessibility.Private, Parameters)}}} - private static readonly {{{ReturnType.BSATNName}}} returnRW = new(); - public SpacetimeDB.Internal.RawViewDefV9 {{{makeViewDefMethod}}}(SpacetimeDB.BSATN.ITypeRegistrar registrar) => {{{GenerateViewDef(index)}}} @@ -1041,10 +1048,7 @@ public byte[] Invoke( try { {{{paramReads}}} var returnValue = {{{FullName}}}(({{{concreteContext}}})ctx{{{invocationArgs}}}); - using var output = new System.IO.MemoryStream(); - using var writer = new System.IO.BinaryWriter(output); - returnRW.Write(writer, returnValue); - return output.ToArray(); + {{{writeOutput}}} } catch (System.Exception e) { global::SpacetimeDB.Log.Error("Error in view '{{{Name}}}': " + e); throw; @@ -1054,12 +1058,12 @@ public byte[] Invoke( """; } - public string GenerateClass() - { - var builder = new Scope.Extensions(Scope, FullName); - builder.Contents.Append(GenerateDispatcherClass()); - return builder.ToString(); - } + // public string GenerateClass() + // { + // var builder = new Scope.Extensions(Scope, FullName); + // builder.Contents.Append(GenerateDispatcherClass()); + // return builder.ToString(); + // } } /// @@ -1489,8 +1493,15 @@ public sealed class Local { } } - {{string.Join("\n", views.Array.Select(v => v.GenerateDispatcherClass()))}} - + {{string.Join("\n", + views.Array.Where(v => !v.IsAnonymous) + .Select((v, i) => v.GenerateDispatcherClass((uint)i)) + .Concat( + views.Array.Where(v => v.IsAnonymous) + .Select((v, i) => v.GenerateDispatcherClass((uint)i)) + ) + )}} + namespace SpacetimeDB.Internal.ViewHandles { {{string.Join("\n", readOnlyAccessors.Array.Select(v => v.readOnlyAccessor))}} } @@ -1504,6 +1515,9 @@ public sealed partial class LocalReadOnly { static class ModuleRegistration { {{string.Join("\n", addReducers.Select(r => r.Class))}} + public static List ToListOrEmpty(T? value) where T : struct + => value is null ? new List() : new List { value.Value }; + #if EXPERIMENTAL_WASM_AOT // In AOT mode we're building a library. // Main method won't be called automatically, so we need to export it as a preinit function. @@ -1584,6 +1598,36 @@ SpacetimeDB.Internal.BytesSink error args, error ); + + [UnmanagedCallersOnly(EntryPoint = "__call_view__")] + public static SpacetimeDB.Internal.Errno __call_view__( + uint id, + ulong sender_0, + ulong sender_1, + ulong sender_2, + ulong sender_3, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) => SpacetimeDB.Internal.Module.__call_view__( + id, + sender_0, + sender_1, + sender_2, + sender_3, + args, + sink + ); + + [UnmanagedCallersOnly(EntryPoint = "__call_view_anon__")] + public static SpacetimeDB.Internal.Errno __call_view_anon__( + uint id, + SpacetimeDB.Internal.BytesSource args, + SpacetimeDB.Internal.BytesSink sink + ) => SpacetimeDB.Internal.Module.__call_view_anon__( + id, + args, + sink + ); #endif } diff --git a/crates/bindings-csharp/Runtime/Internal/Module.cs b/crates/bindings-csharp/Runtime/Internal/Module.cs index f0afea9f0df..849b2510fff 100644 --- a/crates/bindings-csharp/Runtime/Internal/Module.cs +++ b/crates/bindings-csharp/Runtime/Internal/Module.cs @@ -326,7 +326,7 @@ BytesSink rows } } - public static Errno __call_anonymous_view__(uint id, BytesSource args, BytesSink rows) + public static Errno __call_view_anon__(uint id, BytesSource args, BytesSink rows) { try { diff --git a/crates/bindings-csharp/Runtime/bindings.c b/crates/bindings-csharp/Runtime/bindings.c index 1756325dfdc..f256c03bd2e 100644 --- a/crates/bindings-csharp/Runtime/bindings.c +++ b/crates/bindings-csharp/Runtime/bindings.c @@ -159,6 +159,18 @@ EXPORT(int16_t, __call_reducer__, &sender_0, &sender_1, &sender_2, &sender_3, &conn_id_0, &conn_id_1, ×tamp, &args, &error); + +EXPORT(int16_t, __call_view__, + (uint32_t id, + uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3, + BytesSource args, BytesSink rows), + &id, + &sender_0, &sender_1, &sender_2, &sender_3, + &args, &rows); + +EXPORT(int16_t, __call_view_anon__, + (uint32_t id, BytesSource args, BytesSink rows), + &id, &args, &rows); #endif // Shims to avoid dependency on WASI in the generated Wasm file. diff --git a/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs index 977372c0e05..87d5432b30f 100644 --- a/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.8.0 (commit 31a8d5f1540fb11aef7db32f62e19b238fdaa797). +// This was generated using spacetimedb cli version 1.8.0 (commit cd36a957862ff4efc6086c28d04773f461406e6f). #nullable enable diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index 684e6314c71..ba816bd0294 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -138,6 +138,7 @@ void OnSubscriptionApplied(SubscriptionEventContext context) // Views test Log.Debug("Calling Iter on View"); var viewIterRows = context.Db.MyPlayer.Iter(); + Log.Debug("MyPlayer Iter count: " + (viewIterRows != null ? viewIterRows.Count().ToString() : "null")); Debug.Assert(viewIterRows != null && viewIterRows.Any()); Log.Debug("Calling RemoteQuery on View"); @@ -149,7 +150,7 @@ void OnSubscriptionApplied(SubscriptionEventContext context) Debug.Assert(anonViewIterRows != null && anonViewIterRows.Any()); Log.Debug("Calling RemoteQuery on Anonymous View"); - var anonViewRemoteQueryRows = context.Db.PlayersForLevel.RemoteQuery($"WHERE Level = 1)"); + var anonViewRemoteQueryRows = context.Db.PlayersForLevel.RemoteQuery("WHERE Level = 1"); Debug.Assert(anonViewRemoteQueryRows != null && anonViewRemoteQueryRows.Result.Length > 0); } diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs index 169af7228b9..ab2eb58021f 100644 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.8.0 (commit 9d57c4d4ea4b3fa5bcd2bad3cbd270d0b4c0bf2f). +// This was generated using spacetimedb cli version 1.8.0 (commit cd36a957862ff4efc6086c28d04773f461406e6f). #nullable enable @@ -33,409 +33,421 @@ public RemoteTables(DbConnection conn) public sealed partial class SetReducerFlags { } - public interface IRemoteDbContext : IDbContext { - public event Action? OnUnhandledReducerError; - } + public interface IRemoteDbContext : IDbContext + { + public event Action? OnUnhandledReducerError; + } - public sealed class EventContext : IEventContext, IRemoteDbContext - { - private readonly DbConnection conn; - - /// - /// The event that caused this callback to run. - /// - public readonly Event Event; - - /// - /// Access to tables in the client cache, which stores a read-only replica of the remote database state. - /// - /// The returned DbView will have a method to access each table defined by the module. - /// - public RemoteTables Db => conn.Db; - /// - /// Access to reducers defined by the module. - /// - /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, - /// plus methods for adding and removing callbacks on each of those reducers. - /// - public RemoteReducers Reducers => conn.Reducers; - /// - /// Access to setters for per-reducer flags. - /// - /// The returned SetReducerFlags will have a method to invoke, - /// for each reducer defined by the module, - /// which call-flags for the reducer can be set. - /// - public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; - /// - /// Returns true if the connection is active, i.e. has not yet disconnected. - /// - public bool IsActive => conn.IsActive; - /// - /// Close the connection. - /// - /// Throws an error if the connection is already closed. - /// - public void Disconnect() { - conn.Disconnect(); - } - /// - /// Start building a subscription. - /// - /// A builder-pattern constructor for subscribing to queries, - /// causing matching rows to be replicated into the client cache. - public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); - /// - /// Get the Identity of this connection. - /// - /// This method returns null if the connection was constructed anonymously - /// and we have not yet received our newly-generated Identity from the host. - /// - public Identity? Identity => conn.Identity; - /// - /// Get this connection's ConnectionId. - /// - public ConnectionId ConnectionId => conn.ConnectionId; - /// - /// Register a callback to be called when a reducer with no handler returns an error. - /// - public event Action? OnUnhandledReducerError { - add => Reducers.InternalOnUnhandledReducerError += value; - remove => Reducers.InternalOnUnhandledReducerError -= value; - } + public sealed class EventContext : IEventContext, IRemoteDbContext + { + private readonly DbConnection conn; - internal EventContext(DbConnection conn, Event Event) - { - this.conn = conn; - this.Event = Event; - } - } + /// + /// The event that caused this callback to run. + /// + public readonly Event Event; - public sealed class ReducerEventContext : IReducerEventContext, IRemoteDbContext + /// + /// Access to tables in the client cache, which stores a read-only replica of the remote database state. + /// + /// The returned DbView will have a method to access each table defined by the module. + /// + public RemoteTables Db => conn.Db; + /// + /// Access to reducers defined by the module. + /// + /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, + /// plus methods for adding and removing callbacks on each of those reducers. + /// + public RemoteReducers Reducers => conn.Reducers; + /// + /// Access to setters for per-reducer flags. + /// + /// The returned SetReducerFlags will have a method to invoke, + /// for each reducer defined by the module, + /// which call-flags for the reducer can be set. + /// + public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; + /// + /// Returns true if the connection is active, i.e. has not yet disconnected. + /// + public bool IsActive => conn.IsActive; + /// + /// Close the connection. + /// + /// Throws an error if the connection is already closed. + /// + public void Disconnect() { - private readonly DbConnection conn; - /// - /// The reducer event that caused this callback to run. - /// - public readonly ReducerEvent Event; - - /// - /// Access to tables in the client cache, which stores a read-only replica of the remote database state. - /// - /// The returned DbView will have a method to access each table defined by the module. - /// - public RemoteTables Db => conn.Db; - /// - /// Access to reducers defined by the module. - /// - /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, - /// plus methods for adding and removing callbacks on each of those reducers. - /// - public RemoteReducers Reducers => conn.Reducers; - /// - /// Access to setters for per-reducer flags. - /// - /// The returned SetReducerFlags will have a method to invoke, - /// for each reducer defined by the module, - /// which call-flags for the reducer can be set. - /// - public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; - /// - /// Returns true if the connection is active, i.e. has not yet disconnected. - /// - public bool IsActive => conn.IsActive; - /// - /// Close the connection. - /// - /// Throws an error if the connection is already closed. - /// - public void Disconnect() { - conn.Disconnect(); - } - /// - /// Start building a subscription. - /// - /// A builder-pattern constructor for subscribing to queries, - /// causing matching rows to be replicated into the client cache. - public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); - /// - /// Get the Identity of this connection. - /// - /// This method returns null if the connection was constructed anonymously - /// and we have not yet received our newly-generated Identity from the host. - /// - public Identity? Identity => conn.Identity; - /// - /// Get this connection's ConnectionId. - /// - public ConnectionId ConnectionId => conn.ConnectionId; - /// - /// Register a callback to be called when a reducer with no handler returns an error. - /// - public event Action? OnUnhandledReducerError { - add => Reducers.InternalOnUnhandledReducerError += value; - remove => Reducers.InternalOnUnhandledReducerError -= value; - } + conn.Disconnect(); + } + /// + /// Start building a subscription. + /// + /// A builder-pattern constructor for subscribing to queries, + /// causing matching rows to be replicated into the client cache. + public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); + /// + /// Get the Identity of this connection. + /// + /// This method returns null if the connection was constructed anonymously + /// and we have not yet received our newly-generated Identity from the host. + /// + public Identity? Identity => conn.Identity; + /// + /// Get this connection's ConnectionId. + /// + public ConnectionId ConnectionId => conn.ConnectionId; + /// + /// Register a callback to be called when a reducer with no handler returns an error. + /// + public event Action? OnUnhandledReducerError + { + add => Reducers.InternalOnUnhandledReducerError += value; + remove => Reducers.InternalOnUnhandledReducerError -= value; + } - internal ReducerEventContext(DbConnection conn, ReducerEvent reducerEvent) - { - this.conn = conn; - Event = reducerEvent; - } + internal EventContext(DbConnection conn, Event Event) + { + this.conn = conn; + this.Event = Event; } + } + + public sealed class ReducerEventContext : IReducerEventContext, IRemoteDbContext + { + private readonly DbConnection conn; + /// + /// The reducer event that caused this callback to run. + /// + public readonly ReducerEvent Event; - public sealed class ErrorContext : IErrorContext, IRemoteDbContext + /// + /// Access to tables in the client cache, which stores a read-only replica of the remote database state. + /// + /// The returned DbView will have a method to access each table defined by the module. + /// + public RemoteTables Db => conn.Db; + /// + /// Access to reducers defined by the module. + /// + /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, + /// plus methods for adding and removing callbacks on each of those reducers. + /// + public RemoteReducers Reducers => conn.Reducers; + /// + /// Access to setters for per-reducer flags. + /// + /// The returned SetReducerFlags will have a method to invoke, + /// for each reducer defined by the module, + /// which call-flags for the reducer can be set. + /// + public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; + /// + /// Returns true if the connection is active, i.e. has not yet disconnected. + /// + public bool IsActive => conn.IsActive; + /// + /// Close the connection. + /// + /// Throws an error if the connection is already closed. + /// + public void Disconnect() { - private readonly DbConnection conn; - /// - /// The Exception that caused this error callback to be run. - /// - public readonly Exception Event; - Exception IErrorContext.Event { - get { - return Event; - } - } + conn.Disconnect(); + } + /// + /// Start building a subscription. + /// + /// A builder-pattern constructor for subscribing to queries, + /// causing matching rows to be replicated into the client cache. + public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); + /// + /// Get the Identity of this connection. + /// + /// This method returns null if the connection was constructed anonymously + /// and we have not yet received our newly-generated Identity from the host. + /// + public Identity? Identity => conn.Identity; + /// + /// Get this connection's ConnectionId. + /// + public ConnectionId ConnectionId => conn.ConnectionId; + /// + /// Register a callback to be called when a reducer with no handler returns an error. + /// + public event Action? OnUnhandledReducerError + { + add => Reducers.InternalOnUnhandledReducerError += value; + remove => Reducers.InternalOnUnhandledReducerError -= value; + } - /// - /// Access to tables in the client cache, which stores a read-only replica of the remote database state. - /// - /// The returned DbView will have a method to access each table defined by the module. - /// - public RemoteTables Db => conn.Db; - /// - /// Access to reducers defined by the module. - /// - /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, - /// plus methods for adding and removing callbacks on each of those reducers. - /// - public RemoteReducers Reducers => conn.Reducers; - /// - /// Access to setters for per-reducer flags. - /// - /// The returned SetReducerFlags will have a method to invoke, - /// for each reducer defined by the module, - /// which call-flags for the reducer can be set. - /// - public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; - /// - /// Returns true if the connection is active, i.e. has not yet disconnected. - /// - public bool IsActive => conn.IsActive; - /// - /// Close the connection. - /// - /// Throws an error if the connection is already closed. - /// - public void Disconnect() { - conn.Disconnect(); - } - /// - /// Start building a subscription. - /// - /// A builder-pattern constructor for subscribing to queries, - /// causing matching rows to be replicated into the client cache. - public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); - /// - /// Get the Identity of this connection. - /// - /// This method returns null if the connection was constructed anonymously - /// and we have not yet received our newly-generated Identity from the host. - /// - public Identity? Identity => conn.Identity; - /// - /// Get this connection's ConnectionId. - /// - public ConnectionId ConnectionId => conn.ConnectionId; - /// - /// Register a callback to be called when a reducer with no handler returns an error. - /// - public event Action? OnUnhandledReducerError { - add => Reducers.InternalOnUnhandledReducerError += value; - remove => Reducers.InternalOnUnhandledReducerError -= value; - } + internal ReducerEventContext(DbConnection conn, ReducerEvent reducerEvent) + { + this.conn = conn; + Event = reducerEvent; + } + } - internal ErrorContext(DbConnection conn, Exception error) + public sealed class ErrorContext : IErrorContext, IRemoteDbContext + { + private readonly DbConnection conn; + /// + /// The Exception that caused this error callback to be run. + /// + public readonly Exception Event; + Exception IErrorContext.Event + { + get { - this.conn = conn; - Event = error; + return Event; } } - public sealed class SubscriptionEventContext : ISubscriptionEventContext, IRemoteDbContext + /// + /// Access to tables in the client cache, which stores a read-only replica of the remote database state. + /// + /// The returned DbView will have a method to access each table defined by the module. + /// + public RemoteTables Db => conn.Db; + /// + /// Access to reducers defined by the module. + /// + /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, + /// plus methods for adding and removing callbacks on each of those reducers. + /// + public RemoteReducers Reducers => conn.Reducers; + /// + /// Access to setters for per-reducer flags. + /// + /// The returned SetReducerFlags will have a method to invoke, + /// for each reducer defined by the module, + /// which call-flags for the reducer can be set. + /// + public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; + /// + /// Returns true if the connection is active, i.e. has not yet disconnected. + /// + public bool IsActive => conn.IsActive; + /// + /// Close the connection. + /// + /// Throws an error if the connection is already closed. + /// + public void Disconnect() { - private readonly DbConnection conn; - - /// - /// Access to tables in the client cache, which stores a read-only replica of the remote database state. - /// - /// The returned DbView will have a method to access each table defined by the module. - /// - public RemoteTables Db => conn.Db; - /// - /// Access to reducers defined by the module. - /// - /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, - /// plus methods for adding and removing callbacks on each of those reducers. - /// - public RemoteReducers Reducers => conn.Reducers; - /// - /// Access to setters for per-reducer flags. - /// - /// The returned SetReducerFlags will have a method to invoke, - /// for each reducer defined by the module, - /// which call-flags for the reducer can be set. - /// - public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; - /// - /// Returns true if the connection is active, i.e. has not yet disconnected. - /// - public bool IsActive => conn.IsActive; - /// - /// Close the connection. - /// - /// Throws an error if the connection is already closed. - /// - public void Disconnect() { - conn.Disconnect(); - } - /// - /// Start building a subscription. - /// - /// A builder-pattern constructor for subscribing to queries, - /// causing matching rows to be replicated into the client cache. - public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); - /// - /// Get the Identity of this connection. - /// - /// This method returns null if the connection was constructed anonymously - /// and we have not yet received our newly-generated Identity from the host. - /// - public Identity? Identity => conn.Identity; - /// - /// Get this connection's ConnectionId. - /// - public ConnectionId ConnectionId => conn.ConnectionId; - /// - /// Register a callback to be called when a reducer with no handler returns an error. - /// - public event Action? OnUnhandledReducerError { - add => Reducers.InternalOnUnhandledReducerError += value; - remove => Reducers.InternalOnUnhandledReducerError -= value; - } + conn.Disconnect(); + } + /// + /// Start building a subscription. + /// + /// A builder-pattern constructor for subscribing to queries, + /// causing matching rows to be replicated into the client cache. + public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); + /// + /// Get the Identity of this connection. + /// + /// This method returns null if the connection was constructed anonymously + /// and we have not yet received our newly-generated Identity from the host. + /// + public Identity? Identity => conn.Identity; + /// + /// Get this connection's ConnectionId. + /// + public ConnectionId ConnectionId => conn.ConnectionId; + /// + /// Register a callback to be called when a reducer with no handler returns an error. + /// + public event Action? OnUnhandledReducerError + { + add => Reducers.InternalOnUnhandledReducerError += value; + remove => Reducers.InternalOnUnhandledReducerError -= value; + } - internal SubscriptionEventContext(DbConnection conn) - { - this.conn = conn; - } + internal ErrorContext(DbConnection conn, Exception error) + { + this.conn = conn; + Event = error; } + } + public sealed class SubscriptionEventContext : ISubscriptionEventContext, IRemoteDbContext + { + private readonly DbConnection conn; + + /// + /// Access to tables in the client cache, which stores a read-only replica of the remote database state. + /// + /// The returned DbView will have a method to access each table defined by the module. + /// + public RemoteTables Db => conn.Db; /// - /// Builder-pattern constructor for subscription queries. + /// Access to reducers defined by the module. + /// + /// The returned RemoteReducers will have a method to invoke each reducer defined by the module, + /// plus methods for adding and removing callbacks on each of those reducers. /// - public sealed class SubscriptionBuilder + public RemoteReducers Reducers => conn.Reducers; + /// + /// Access to setters for per-reducer flags. + /// + /// The returned SetReducerFlags will have a method to invoke, + /// for each reducer defined by the module, + /// which call-flags for the reducer can be set. + /// + public SetReducerFlags SetReducerFlags => conn.SetReducerFlags; + /// + /// Returns true if the connection is active, i.e. has not yet disconnected. + /// + public bool IsActive => conn.IsActive; + /// + /// Close the connection. + /// + /// Throws an error if the connection is already closed. + /// + public void Disconnect() { - private readonly IDbConnection conn; + conn.Disconnect(); + } + /// + /// Start building a subscription. + /// + /// A builder-pattern constructor for subscribing to queries, + /// causing matching rows to be replicated into the client cache. + public SubscriptionBuilder SubscriptionBuilder() => conn.SubscriptionBuilder(); + /// + /// Get the Identity of this connection. + /// + /// This method returns null if the connection was constructed anonymously + /// and we have not yet received our newly-generated Identity from the host. + /// + public Identity? Identity => conn.Identity; + /// + /// Get this connection's ConnectionId. + /// + public ConnectionId ConnectionId => conn.ConnectionId; + /// + /// Register a callback to be called when a reducer with no handler returns an error. + /// + public event Action? OnUnhandledReducerError + { + add => Reducers.InternalOnUnhandledReducerError += value; + remove => Reducers.InternalOnUnhandledReducerError -= value; + } - private event Action? Applied; - private event Action? Error; + internal SubscriptionEventContext(DbConnection conn) + { + this.conn = conn; + } + } - /// - /// Private API, use conn.SubscriptionBuilder() instead. - /// - public SubscriptionBuilder(IDbConnection conn) - { - this.conn = conn; - } + /// + /// Builder-pattern constructor for subscription queries. + /// + public sealed class SubscriptionBuilder + { + private readonly IDbConnection conn; - /// - /// Register a callback to run when the subscription is applied. - /// - public SubscriptionBuilder OnApplied( - Action callback - ) - { - Applied += callback; - return this; - } + private event Action? Applied; + private event Action? Error; - /// - /// Register a callback to run when the subscription fails. - /// - /// Note that this callback may run either when attempting to apply the subscription, - /// in which case Self::on_applied will never run, - /// or later during the subscription's lifetime if the module's interface changes, - /// in which case Self::on_applied may have already run. - /// - public SubscriptionBuilder OnError( - Action callback - ) - { - Error += callback; - return this; - } + /// + /// Private API, use conn.SubscriptionBuilder() instead. + /// + public SubscriptionBuilder(IDbConnection conn) + { + this.conn = conn; + } - /// - /// Subscribe to the following SQL queries. - /// - /// This method returns immediately, with the data not yet added to the DbConnection. - /// The provided callbacks will be invoked once the data is returned from the remote server. - /// Data from all the provided queries will be returned at the same time. - /// - /// See the SpacetimeDB SQL docs for more information on SQL syntax: - /// https://spacetimedb.com/docs/sql - /// - public SubscriptionHandle Subscribe( - string[] querySqls - ) => new(conn, Applied, Error, querySqls); - - /// - /// Subscribe to all rows from all tables. - /// - /// This method is intended as a convenience - /// for applications where client-side memory use and network bandwidth are not concerns. - /// Applications where these resources are a constraint - /// should register more precise queries via Self.Subscribe - /// in order to replicate only the subset of data which the client needs to function. - /// - /// This method should not be combined with Self.Subscribe on the same DbConnection. - /// A connection may either Self.Subscribe to particular queries, - /// or Self.SubscribeToAllTables, but not both. - /// Attempting to call Self.Subscribe - /// on a DbConnection that has previously used Self.SubscribeToAllTables, - /// or vice versa, may misbehave in any number of ways, - /// including dropping subscriptions, corrupting the client cache, or panicking. - /// - public void SubscribeToAllTables() - { - // Make sure we use the legacy handle constructor here, even though there's only 1 query. - // We drop the error handler, since it can't be called for legacy subscriptions. - new SubscriptionHandle( - conn, - Applied, - new string[] { "SELECT * FROM *" } - ); - } + /// + /// Register a callback to run when the subscription is applied. + /// + public SubscriptionBuilder OnApplied( + Action callback + ) + { + Applied += callback; + return this; + } + + /// + /// Register a callback to run when the subscription fails. + /// + /// Note that this callback may run either when attempting to apply the subscription, + /// in which case Self::on_applied will never run, + /// or later during the subscription's lifetime if the module's interface changes, + /// in which case Self::on_applied may have already run. + /// + public SubscriptionBuilder OnError( + Action callback + ) + { + Error += callback; + return this; } - public sealed class SubscriptionHandle : SubscriptionHandleBase { - /// - /// Internal API. Construct SubscriptionHandles using conn.SubscriptionBuilder. - /// - public SubscriptionHandle(IDbConnection conn, Action? onApplied, string[] querySqls) : base(conn, onApplied, querySqls) - { } - - /// - /// Internal API. Construct SubscriptionHandles using conn.SubscriptionBuilder. - /// - public SubscriptionHandle( - IDbConnection conn, - Action? onApplied, - Action? onError, - string[] querySqls - ) : base(conn, onApplied, onError, querySqls) - { } + /// + /// Subscribe to the following SQL queries. + /// + /// This method returns immediately, with the data not yet added to the DbConnection. + /// The provided callbacks will be invoked once the data is returned from the remote server. + /// Data from all the provided queries will be returned at the same time. + /// + /// See the SpacetimeDB SQL docs for more information on SQL syntax: + /// https://spacetimedb.com/docs/sql + /// + public SubscriptionHandle Subscribe( + string[] querySqls + ) => new(conn, Applied, Error, querySqls); + + /// + /// Subscribe to all rows from all tables. + /// + /// This method is intended as a convenience + /// for applications where client-side memory use and network bandwidth are not concerns. + /// Applications where these resources are a constraint + /// should register more precise queries via Self.Subscribe + /// in order to replicate only the subset of data which the client needs to function. + /// + /// This method should not be combined with Self.Subscribe on the same DbConnection. + /// A connection may either Self.Subscribe to particular queries, + /// or Self.SubscribeToAllTables, but not both. + /// Attempting to call Self.Subscribe + /// on a DbConnection that has previously used Self.SubscribeToAllTables, + /// or vice versa, may misbehave in any number of ways, + /// including dropping subscriptions, corrupting the client cache, or panicking. + /// + public void SubscribeToAllTables() + { + // Make sure we use the legacy handle constructor here, even though there's only 1 query. + // We drop the error handler, since it can't be called for legacy subscriptions. + new SubscriptionHandle( + conn, + Applied, + new string[] { "SELECT * FROM *" } + ); } + } + + public sealed class SubscriptionHandle : SubscriptionHandleBase + { + /// + /// Internal API. Construct SubscriptionHandles using conn.SubscriptionBuilder. + /// + public SubscriptionHandle(IDbConnection conn, Action? onApplied, string[] querySqls) : base(conn, onApplied, querySqls) + { } + + /// + /// Internal API. Construct SubscriptionHandles using conn.SubscriptionBuilder. + /// + public SubscriptionHandle( + IDbConnection conn, + Action? onApplied, + Action? onError, + string[] querySqls + ) : base(conn, onApplied, onError, querySqls) + { } + } public abstract partial class Reducer { @@ -457,7 +469,8 @@ public DbConnection() protected override Reducer ToReducer(TransactionUpdate update) { var encodedArgs = update.ReducerCall.Args; - return update.ReducerCall.ReducerName switch { + return update.ReducerCall.ReducerName switch + { "Add" => BSATNHelpers.Decode(encodedArgs), "ClientConnected" => BSATNHelpers.Decode(encodedArgs), "Delete" => BSATNHelpers.Decode(encodedArgs), @@ -482,7 +495,8 @@ protected override IErrorContext ToErrorContext(Exception exception) => protected override bool Dispatch(IReducerEventContext context, Reducer reducer) { var eventContext = (ReducerEventContext)context; - return reducer switch { + return reducer switch + { Reducer.Add args => Reducers.InvokeAdd(eventContext, args), Reducer.ClientConnected args => Reducers.InvokeClientConnected(eventContext, args), Reducer.Delete args => Reducers.InvokeDelete(eventContext, args), diff --git a/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs index 7267aa9e29a..be596846a87 100644 --- a/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.8.0 (commit 31a8d5f1540fb11aef7db32f62e19b238fdaa797). +// This was generated using spacetimedb cli version 1.8.0 (commit cd36a957862ff4efc6086c28d04773f461406e6f). #nullable enable From ad7931e7049d0cc782c106c312a09649f0f3fcf6 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Wed, 19 Nov 2025 16:14:38 -0800 Subject: [PATCH 13/38] revert sdk-test changes --- modules/sdk-test-cs/Lib.cs | 6 -- modules/sdk-test/src/lib.rs | 7 +- .../module_bindings/SpacetimeDBClient.g.cs | 6 +- .../test-client/src/module_bindings/mod.rs | 10 -- .../src/module_bindings/my_user_table.rs | 95 ------------------- 5 files changed, 5 insertions(+), 119 deletions(-) delete mode 100644 sdks/rust/tests/test-client/src/module_bindings/my_user_table.rs diff --git a/modules/sdk-test-cs/Lib.cs b/modules/sdk-test-cs/Lib.cs index 577ffc8cf50..b17daa81ad4 100644 --- a/modules/sdk-test-cs/Lib.cs +++ b/modules/sdk-test-cs/Lib.cs @@ -2060,12 +2060,6 @@ public static void insert_user(ReducerContext ctx, string name, Identity identit ctx.Db.users.Insert(new Users { name = name, identity = identity }); } - [SpacetimeDB.View(Name = "my_user", Public = true)] - public static Users? MyUser(ViewContext ctx) - { - return ctx.Db.users.identity.Find(ctx.Sender) as Users?; - } - [SpacetimeDB.Table(Name = "indexed_simple_enum", Public = true)] public partial struct IndexedSimpleEnum { diff --git a/modules/sdk-test/src/lib.rs b/modules/sdk-test/src/lib.rs index 5180896e152..c4dda5aeae3 100644 --- a/modules/sdk-test/src/lib.rs +++ b/modules/sdk-test/src/lib.rs @@ -9,7 +9,7 @@ use anyhow::{anyhow, Context, Result}; use spacetimedb::{ sats::{i256, u256}, - ConnectionId, Identity, ReducerContext, SpacetimeType, Table, TimeDuration, Timestamp, ViewContext, + ConnectionId, Identity, ReducerContext, SpacetimeType, Table, TimeDuration, Timestamp, }; #[derive(PartialEq, Eq, Hash, SpacetimeType)] @@ -796,11 +796,6 @@ fn insert_user(ctx: &ReducerContext, name: String, identity: Identity) -> anyhow Ok(()) } -#[spacetimedb::view(name = my_user, public)] -fn my_user(ctx: &ViewContext) -> Option { - ctx.db.users().identity().find(ctx.sender) -} - #[spacetimedb::table(name = indexed_simple_enum, public)] struct IndexedSimpleEnum { #[index(btree)] diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs index fb691f6fd43..9b5f5c48ef5 100644 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs @@ -24,8 +24,10 @@ public sealed partial class RemoteTables : RemoteTablesBase public RemoteTables(DbConnection conn) { AddTable(ExampleData = new(conn)); - AddTable(GetAnonymousExampleDataById = new(conn)); - AddTable(GetExampleDataById = new(conn)); + AddTable(MyPlayer = new(conn)); + AddTable(Player = new(conn)); + AddTable(PlayerLevel = new(conn)); + AddTable(PlayersForLevel = new(conn)); } } diff --git a/sdks/rust/tests/test-client/src/module_bindings/mod.rs b/sdks/rust/tests/test-client/src/module_bindings/mod.rs index 6f057a2471c..a3fd6ab9c2f 100644 --- a/sdks/rust/tests/test-client/src/module_bindings/mod.rs +++ b/sdks/rust/tests/test-client/src/module_bindings/mod.rs @@ -163,7 +163,6 @@ pub mod insert_vec_u_8_reducer; pub mod insert_vec_unit_struct_reducer; pub mod large_table_table; pub mod large_table_type; -pub mod my_user_table; pub mod no_op_succeeds_reducer; pub mod one_bool_table; pub mod one_bool_type; @@ -682,7 +681,6 @@ pub use insert_vec_unit_struct_reducer::{ }; pub use large_table_table::*; pub use large_table_type::LargeTable; -pub use my_user_table::*; pub use no_op_succeeds_reducer::{no_op_succeeds, set_flags_for_no_op_succeeds, NoOpSucceedsCallbackId}; pub use one_bool_table::*; pub use one_bool_type::OneBool; @@ -2775,7 +2773,6 @@ pub struct DbUpdate { indexed_table: __sdk::TableUpdate, indexed_table_2: __sdk::TableUpdate, large_table: __sdk::TableUpdate, - my_user: __sdk::TableUpdate, one_bool: __sdk::TableUpdate, one_byte_struct: __sdk::TableUpdate, one_connection_id: __sdk::TableUpdate, @@ -2892,9 +2889,6 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate { "large_table" => db_update .large_table .append(large_table_table::parse_table_update(table_update)?), - "my_user" => db_update - .my_user - .append(my_user_table::parse_table_update(table_update)?), "one_bool" => db_update .one_bool .append(one_bool_table::parse_table_update(table_update)?), @@ -3344,7 +3338,6 @@ impl __sdk::DbUpdate for DbUpdate { diff.vec_u_64 = cache.apply_diff_to_table::("vec_u64", &self.vec_u_64); diff.vec_u_8 = cache.apply_diff_to_table::("vec_u8", &self.vec_u_8); diff.vec_unit_struct = cache.apply_diff_to_table::("vec_unit_struct", &self.vec_unit_struct); - diff.my_user = cache.apply_diff_to_table::("my_user", &self.my_user); diff } @@ -3359,7 +3352,6 @@ pub struct AppliedDiff<'r> { indexed_table: __sdk::TableAppliedDiff<'r, IndexedTable>, indexed_table_2: __sdk::TableAppliedDiff<'r, IndexedTable2>, large_table: __sdk::TableAppliedDiff<'r, LargeTable>, - my_user: __sdk::TableAppliedDiff<'r, Users>, one_bool: __sdk::TableAppliedDiff<'r, OneBool>, one_byte_struct: __sdk::TableAppliedDiff<'r, OneByteStruct>, one_connection_id: __sdk::TableAppliedDiff<'r, OneConnectionId>, @@ -3471,7 +3463,6 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { callbacks.invoke_table_row_callbacks::("indexed_table", &self.indexed_table, event); callbacks.invoke_table_row_callbacks::("indexed_table_2", &self.indexed_table_2, event); callbacks.invoke_table_row_callbacks::("large_table", &self.large_table, event); - callbacks.invoke_table_row_callbacks::("my_user", &self.my_user, event); callbacks.invoke_table_row_callbacks::("one_bool", &self.one_bool, event); callbacks.invoke_table_row_callbacks::("one_byte_struct", &self.one_byte_struct, event); callbacks.invoke_table_row_callbacks::("one_connection_id", &self.one_connection_id, event); @@ -4325,7 +4316,6 @@ impl __sdk::SpacetimeModule for RemoteModule { indexed_table_table::register_table(client_cache); indexed_table_2_table::register_table(client_cache); large_table_table::register_table(client_cache); - my_user_table::register_table(client_cache); one_bool_table::register_table(client_cache); one_byte_struct_table::register_table(client_cache); one_connection_id_table::register_table(client_cache); diff --git a/sdks/rust/tests/test-client/src/module_bindings/my_user_table.rs b/sdks/rust/tests/test-client/src/module_bindings/my_user_table.rs deleted file mode 100644 index adfc1be60db..00000000000 --- a/sdks/rust/tests/test-client/src/module_bindings/my_user_table.rs +++ /dev/null @@ -1,95 +0,0 @@ -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. - -#![allow(unused, clippy::all)] -use super::users_type::Users; -use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; - -/// Table handle for the table `my_user`. -/// -/// Obtain a handle from the [`MyUserTableAccess::my_user`] method on [`super::RemoteTables`], -/// like `ctx.db.my_user()`. -/// -/// Users are encouraged not to explicitly reference this type, -/// but to directly chain method calls, -/// like `ctx.db.my_user().on_insert(...)`. -pub struct MyUserTableHandle<'ctx> { - imp: __sdk::TableHandle, - ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, -} - -#[allow(non_camel_case_types)] -/// Extension trait for access to the table `my_user`. -/// -/// Implemented for [`super::RemoteTables`]. -pub trait MyUserTableAccess { - #[allow(non_snake_case)] - /// Obtain a [`MyUserTableHandle`], which mediates access to the table `my_user`. - fn my_user(&self) -> MyUserTableHandle<'_>; -} - -impl MyUserTableAccess for super::RemoteTables { - fn my_user(&self) -> MyUserTableHandle<'_> { - MyUserTableHandle { - imp: self.imp.get_table::("my_user"), - ctx: std::marker::PhantomData, - } - } -} - -pub struct MyUserInsertCallbackId(__sdk::CallbackId); -pub struct MyUserDeleteCallbackId(__sdk::CallbackId); - -impl<'ctx> __sdk::Table for MyUserTableHandle<'ctx> { - type Row = Users; - type EventContext = super::EventContext; - - fn count(&self) -> u64 { - self.imp.count() - } - fn iter(&self) -> impl Iterator + '_ { - self.imp.iter() - } - - type InsertCallbackId = MyUserInsertCallbackId; - - fn on_insert( - &self, - callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, - ) -> MyUserInsertCallbackId { - MyUserInsertCallbackId(self.imp.on_insert(Box::new(callback))) - } - - fn remove_on_insert(&self, callback: MyUserInsertCallbackId) { - self.imp.remove_on_insert(callback.0) - } - - type DeleteCallbackId = MyUserDeleteCallbackId; - - fn on_delete( - &self, - callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, - ) -> MyUserDeleteCallbackId { - MyUserDeleteCallbackId(self.imp.on_delete(Box::new(callback))) - } - - fn remove_on_delete(&self, callback: MyUserDeleteCallbackId) { - self.imp.remove_on_delete(callback.0) - } -} - -#[doc(hidden)] -pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { - let _table = client_cache.get_or_make_table::("my_user"); -} - -#[doc(hidden)] -pub(super) fn parse_table_update( - raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, -) -> __sdk::Result<__sdk::TableUpdate> { - __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { - __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") - .with_cause(e) - .into() - }) -} From 2de6f152841ca9b5f85119cdc6ac1e1249b61f99 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Wed, 19 Nov 2025 16:28:59 -0800 Subject: [PATCH 14/38] revert rust codegen change --- crates/codegen/src/rust.rs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/crates/codegen/src/rust.rs b/crates/codegen/src/rust.rs index 9573a0336c6..9bcb427298c 100644 --- a/crates/codegen/src/rust.rs +++ b/crates/codegen/src/rust.rs @@ -1182,28 +1182,21 @@ impl __sdk::InModule for DbUpdate {{ let mut diff = AppliedDiff::default(); ", |out| { - for (table_name, product_type_ref, with_updates) in itertools::chain!( - iter_tables(module).map(|table| { - ( - &table.name, - table.product_type_ref, - table - .primary_key - .map(|col| { - let pk_field = table.get_column(col).unwrap().name.deref().to_case(Case::Snake); - format!(".with_updates_by_pk(|row| &row.{pk_field})") - }) - .unwrap_or_default(), - ) - }), - iter_views(module).map(|view| (&view.name, view.product_type_ref, "".into())) - ) { - let field_name = table_method_name(table_name); + for table in iter_tables(module) { + let with_updates = table + .primary_key + .map(|col| { + let pk_field = table.get_column(col).unwrap().name.deref().to_case(Case::Snake); + format!(".with_updates_by_pk(|row| &row.{pk_field})") + }) + .unwrap_or_default(); + + let field_name = table_method_name(&table.name); writeln!( out, "diff.{field_name} = cache.apply_diff_to_table::<{}>({:?}, &self.{field_name}){with_updates};", - type_ref_name(module, product_type_ref), - table_name.deref(), + type_ref_name(module, table.product_type_ref), + table.name.deref(), ); } for view in iter_views(module) { From d418bb5f1a403b5ef079bda551288754217ee3cd Mon Sep 17 00:00:00 2001 From: rekhoff Date: Wed, 19 Nov 2025 17:30:41 -0800 Subject: [PATCH 15/38] Update ExtraCompilationErrors.verified.txt --- .../ExtraCompilationErrors.verified.txt | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/ExtraCompilationErrors.verified.txt b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/ExtraCompilationErrors.verified.txt index b369e66f498..df6c2725686 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/ExtraCompilationErrors.verified.txt +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/ExtraCompilationErrors.verified.txt @@ -233,7 +233,7 @@ public partial struct TestDefaultFieldValues var returnValue = Module.ViewDefWrongContext((SpacetimeDB.ViewContext)ctx); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - using var output = new System.IO.MemoryStream(); + SpacetimeDB.BSATN.List returnRW = new(); */ Message: Argument 1: cannot convert from 'SpacetimeDB.ViewContext' to 'SpacetimeDB.ReducerContext', Severity: Error, @@ -279,7 +279,7 @@ SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_THI var returnValue = Module.ViewDefNoContext((SpacetimeDB.ViewContext)ctx); ^^^^^^^^^^^^^^^^ - using var output = new System.IO.MemoryStream(); + SpacetimeDB.BSATN.List returnRW = new(); */ Message: No overload for method 'ViewDefNoContext' takes 1 arguments, Severity: Error, @@ -299,10 +299,10 @@ SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_THI } }, {/* - - private static readonly SpacetimeDB.BSATN.ValueOption returnRW = new(); - ^^^^^ - + Params: [], + ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType(registrar) + ^^^^^ +); */ Message: The type name 'BSATN' does not exist in the type 'NotSpacetimeType', Severity: Error, @@ -322,10 +322,10 @@ SpacetimeDB.Internal.Module.RegisterClientVisibilityFilter(global::Module.MY_THI } }, {/* - Params: [], - ReturnType: new SpacetimeDB.BSATN.ValueOption().GetAlgebraicType(registrar) - ^^^^^ -); + var returnValue = Module.ViewDefReturnsNotASpacetimeType((SpacetimeDB.AnonymousViewContext)ctx); + var listSerializer = SpacetimeDB.BSATN.ValueOption.GetListSerializer(); + ^^^^^ + var listValue = ModuleRegistration.ToListOrEmpty(returnValue); */ Message: The type name 'BSATN' does not exist in the type 'NotSpacetimeType', Severity: Error, From 773ecb066889d209245b323712d521063cb1336d Mon Sep 17 00:00:00 2001 From: rekhoff Date: Wed, 19 Nov 2025 17:56:34 -0800 Subject: [PATCH 16/38] Addressing some comments --- crates/bindings-csharp/Codegen/Module.cs | 9 +-------- sdks/csharp/examples~/regression-tests/client/Program.cs | 7 +++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index b3e9c8e6e85..bdaad048dea 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -1014,7 +1014,7 @@ public string GenerateDispatcherClass(uint index) ? "SpacetimeDB.AnonymousViewContext" : "SpacetimeDB.ViewContext"; - var isValueOption = ReturnType.BSATNName.StartsWith("SpacetimeDB.BSATN.ValueOption"); + var isValueOption = ReturnType.BSATNName.Contains("SpacetimeDB.BSATN.ValueOption"); var writeOutput = isValueOption ? $$$""" var listSerializer = {{{ReturnType.BSATNName}}}.GetListSerializer(); @@ -1057,13 +1057,6 @@ public byte[] Invoke( } """; } - - // public string GenerateClass() - // { - // var builder = new Scope.Extensions(Scope, FullName); - // builder.Contents.Append(GenerateDispatcherClass()); - // return builder.ToString(); - // } } /// diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index ba816bd0294..266f9b98d43 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -136,6 +136,13 @@ void OnSubscriptionApplied(SubscriptionEventContext context) // Views test + + Log.Debug("Checking Views are populated"); + Debug.Assert(context.Db.MyPlayer != null, "context.Db.MyPlayer != null"); + Debug.Assert(context.Db.PlayersForLevel != null, "context.Db.PlayersForLevel != null"); + Debug.Assert(context.Db.MyPlayer.Count > 0, $"context.Db.MyPlayer.Count = {context.Db.MyPlayer.Count}"); + Debug.Assert(context.Db.PlayersForLevel.Count > 0, $"context.Db.MyPlayer.Count = {context.Db.PlayersForLevel.Count}"); + Log.Debug("Calling Iter on View"); var viewIterRows = context.Db.MyPlayer.Iter(); Log.Debug("MyPlayer Iter count: " + (viewIterRows != null ? viewIterRows.Count().ToString() : "null")); From 3e6278e62a33b68f5c2650242b18cb3dfdb2f6ac Mon Sep 17 00:00:00 2001 From: rekhoff Date: Wed, 19 Nov 2025 18:01:37 -0800 Subject: [PATCH 17/38] Updated whitespace of client to match tests --- sdks/csharp/examples~/regression-tests/client/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index 266f9b98d43..cafc9f6aa9b 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -136,13 +136,13 @@ void OnSubscriptionApplied(SubscriptionEventContext context) // Views test - + Log.Debug("Checking Views are populated"); Debug.Assert(context.Db.MyPlayer != null, "context.Db.MyPlayer != null"); Debug.Assert(context.Db.PlayersForLevel != null, "context.Db.PlayersForLevel != null"); Debug.Assert(context.Db.MyPlayer.Count > 0, $"context.Db.MyPlayer.Count = {context.Db.MyPlayer.Count}"); Debug.Assert(context.Db.PlayersForLevel.Count > 0, $"context.Db.MyPlayer.Count = {context.Db.PlayersForLevel.Count}"); - + Log.Debug("Calling Iter on View"); var viewIterRows = context.Db.MyPlayer.Iter(); Log.Debug("MyPlayer Iter count: " + (viewIterRows != null ? viewIterRows.Count().ToString() : "null")); From 75648b04a35f93ecbc045b197d204007b04da718 Mon Sep 17 00:00:00 2001 From: Jason Larabie Date: Wed, 19 Nov 2025 19:26:26 -0800 Subject: [PATCH 18/38] Updated the codegen to have identical generation for RegisterView + ViewDispatchers to make sure the indexes align --- .../diag/snapshots/Module#FFI.verified.cs | 10 ++++++---- .../server/snapshots/Module#FFI.verified.cs | 4 +++- crates/bindings-csharp/Codegen/Module.cs | 18 ++++++++++-------- .../module_bindings/SpacetimeDBClient.g.cs | 2 +- .../regression-tests/client/Program.cs | 11 ++++++++++- .../module_bindings/SpacetimeDBClient.g.cs | 2 +- .../module_bindings/SpacetimeDBClient.g.cs | 2 +- 7 files changed, 32 insertions(+), 17 deletions(-) diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs index c345d1b13c9..457ec0c35e7 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs @@ -1863,16 +1863,18 @@ public static void Main() SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); - SpacetimeDB.Internal.Module.RegisterAnonymousView(); - SpacetimeDB.Internal.Module.RegisterAnonymousView(); + SpacetimeDB.Internal.Module.RegisterView(); - SpacetimeDB.Internal.Module.RegisterAnonymousView(); SpacetimeDB.Internal.Module.RegisterView(); - SpacetimeDB.Internal.Module.RegisterAnonymousView(); SpacetimeDB.Internal.Module.RegisterView(); SpacetimeDB.Internal.Module.RegisterView(); SpacetimeDB.Internal.Module.RegisterView(); SpacetimeDB.Internal.Module.RegisterView(); + SpacetimeDB.Internal.Module.RegisterAnonymousView(); + SpacetimeDB.Internal.Module.RegisterAnonymousView(); + SpacetimeDB.Internal.Module.RegisterAnonymousView(); + SpacetimeDB.Internal.Module.RegisterAnonymousView(); + SpacetimeDB.Internal.Module.RegisterTable< global::Player, SpacetimeDB.Internal.TableHandles.Player diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs index 157f182a610..e8ae4418239 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs @@ -1655,8 +1655,10 @@ public static void Main() SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); - SpacetimeDB.Internal.Module.RegisterAnonymousView(); + SpacetimeDB.Internal.Module.RegisterView(); + SpacetimeDB.Internal.Module.RegisterAnonymousView(); + SpacetimeDB.Internal.Module.RegisterTable< global::BTreeMultiColumn, SpacetimeDB.Internal.TableHandles.BTreeMultiColumn diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index bdaad048dea..b5fe95b76a4 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -1532,14 +1532,16 @@ public static void Main() { $"SpacetimeDB.Internal.Module.RegisterReducer<{r.Name}>();" ) )}} - {{string.Join( - "\n", - views.Array.Select(v => - v.IsAnonymous - ? $"SpacetimeDB.Internal.Module.RegisterAnonymousView<{v.Name}ViewDispatcher>();" - : $"SpacetimeDB.Internal.Module.RegisterView<{v.Name}ViewDispatcher>();" - ) - )}} + + {{string.Join("\n", + views.Array.Where(v => !v.IsAnonymous) + .Select(v => $"SpacetimeDB.Internal.Module.RegisterView<{v.Name}ViewDispatcher>();") + .Concat( + views.Array.Where(v => v.IsAnonymous) + .Select(v => $"SpacetimeDB.Internal.Module.RegisterAnonymousView<{v.Name}ViewDispatcher>();") + ) + )}} + {{string.Join( "\n", tableAccessors.Select(t => $"SpacetimeDB.Internal.Module.RegisterTable<{t.tableName}, SpacetimeDB.Internal.TableHandles.{t.tableAccessorName}>();") diff --git a/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs index 977372c0e05..87d5432b30f 100644 --- a/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/quickstart-chat/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.8.0 (commit 31a8d5f1540fb11aef7db32f62e19b238fdaa797). +// This was generated using spacetimedb cli version 1.8.0 (commit cd36a957862ff4efc6086c28d04773f461406e6f). #nullable enable diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index cafc9f6aa9b..3a56f326389 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -141,12 +141,19 @@ void OnSubscriptionApplied(SubscriptionEventContext context) Debug.Assert(context.Db.MyPlayer != null, "context.Db.MyPlayer != null"); Debug.Assert(context.Db.PlayersForLevel != null, "context.Db.PlayersForLevel != null"); Debug.Assert(context.Db.MyPlayer.Count > 0, $"context.Db.MyPlayer.Count = {context.Db.MyPlayer.Count}"); - Debug.Assert(context.Db.PlayersForLevel.Count > 0, $"context.Db.MyPlayer.Count = {context.Db.PlayersForLevel.Count}"); + Debug.Assert(context.Db.PlayersForLevel.Count > 0, $"context.Db.PlayersForLevel.Count = {context.Db.PlayersForLevel.Count}"); Log.Debug("Calling Iter on View"); var viewIterRows = context.Db.MyPlayer.Iter(); + var expectedPlayer = new Player { Id = 1, Identity = context.Identity!.Value, Name = "NewPlayer" }; Log.Debug("MyPlayer Iter count: " + (viewIterRows != null ? viewIterRows.Count().ToString() : "null")); Debug.Assert(viewIterRows != null && viewIterRows.Any()); + Log.Debug("Validating View row data " + + $"Id={expectedPlayer.Id}, Identity={expectedPlayer.Identity}, Name={expectedPlayer.Name} => " + + $"Id={viewIterRows.First().Id}, Identity={viewIterRows.First().Identity}, Name={viewIterRows.First().Name}"); + Debug.Assert(viewIterRows.First().Id == expectedPlayer.Id && + viewIterRows.First().Identity == expectedPlayer.Identity && + viewIterRows.First().Name == expectedPlayer.Name); Log.Debug("Calling RemoteQuery on View"); var viewRemoteQueryRows = context.Db.MyPlayer.RemoteQuery("WHERE Id > 0"); @@ -154,10 +161,12 @@ void OnSubscriptionApplied(SubscriptionEventContext context) Log.Debug("Calling Iter on Anonymous View"); var anonViewIterRows = context.Db.PlayersForLevel.Iter(); + Log.Debug("PlayersForLevel Iter count: " + (anonViewIterRows != null ? anonViewIterRows.Count().ToString() : "null")); Debug.Assert(anonViewIterRows != null && anonViewIterRows.Any()); Log.Debug("Calling RemoteQuery on Anonymous View"); var anonViewRemoteQueryRows = context.Db.PlayersForLevel.RemoteQuery("WHERE Level = 1"); + Log.Debug("PlayersForLevel RemoteQuery count: " + (anonViewRemoteQueryRows != null ? anonViewRemoteQueryRows.Result.Length.ToString() : "null")); Debug.Assert(anonViewRemoteQueryRows != null && anonViewRemoteQueryRows.Result.Length > 0); } diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs index 9b5f5c48ef5..ab2eb58021f 100644 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.8.0 (commit 798b1c7909306e832723f507f7a3c97d6abc610d). +// This was generated using spacetimedb cli version 1.8.0 (commit cd36a957862ff4efc6086c28d04773f461406e6f). #nullable enable diff --git a/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs index 7267aa9e29a..be596846a87 100644 --- a/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/republishing/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.8.0 (commit 31a8d5f1540fb11aef7db32f62e19b238fdaa797). +// This was generated using spacetimedb cli version 1.8.0 (commit cd36a957862ff4efc6086c28d04773f461406e6f). #nullable enable From d257d9080cc4e71d689ae627ff32923ab1da9aba Mon Sep 17 00:00:00 2001 From: Jason Larabie Date: Thu, 20 Nov 2025 07:30:22 -0800 Subject: [PATCH 19/38] Update crates/bindings-csharp/Codegen/Module.cs Co-authored-by: joshua-spacetime Signed-off-by: Jason Larabie --- crates/bindings-csharp/Codegen/Module.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index b5fe95b76a4..e7fbbd830d1 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -993,6 +993,11 @@ public string GenerateViewDef(uint Index) => ); """; + /// + /// Generates the class responsible for evaluating a view. + /// If this is an anonymous view, the index corresponds to the position of this dispatcher in the `viewDispatchers` list of `RegisterView`. + /// Otherwise it corresponds to the position of this dispatcher in the `anonymousViewDispatchers` list of `RegisterAnonymousView`. + /// public string GenerateDispatcherClass(uint index) { var paramReads = string.Join( From 5a137c5e908fff990ad3748cb9c61bcbfbd774f9 Mon Sep 17 00:00:00 2001 From: Jason Larabie Date: Thu, 20 Nov 2025 07:30:56 -0800 Subject: [PATCH 20/38] Update crates/bindings-csharp/Codegen/Module.cs Co-authored-by: joshua-spacetime Signed-off-by: Jason Larabie --- crates/bindings-csharp/Codegen/Module.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index e7fbbd830d1..23669c6023b 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -1538,6 +1538,9 @@ public static void Main() { ) )}} + // IMPORTANT: The order in which we register views matters. + // It must correspond to the order in which we call `GenerateDispatcherClass`. + // See the comment on `GenerateDispatcherClass` for more explanation. {{string.Join("\n", views.Array.Where(v => !v.IsAnonymous) .Select(v => $"SpacetimeDB.Internal.Module.RegisterView<{v.Name}ViewDispatcher>();") From a64461ea7e72b431e95e3f49a91c23f97497c7ba Mon Sep 17 00:00:00 2001 From: Jason Larabie Date: Thu, 20 Nov 2025 08:00:47 -0800 Subject: [PATCH 21/38] Added more equality checks for the Views data --- .../diag/snapshots/Module#FFI.verified.cs | 3 +++ .../server/snapshots/Module#FFI.verified.cs | 3 +++ .../client/EqualityOperations.cs | 25 +++++++++++++++++++ .../regression-tests/client/Program.cs | 17 ++++++++++--- 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 sdks/csharp/examples~/regression-tests/client/EqualityOperations.cs diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs index 457ec0c35e7..a2916cd3f00 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/diag/snapshots/Module#FFI.verified.cs @@ -1864,6 +1864,9 @@ public static void Main() SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); + // IMPORTANT: The order in which we register views matters. + // It must correspond to the order in which we call `GenerateDispatcherClass`. + // See the comment on `GenerateDispatcherClass` for more explanation. SpacetimeDB.Internal.Module.RegisterView(); SpacetimeDB.Internal.Module.RegisterView(); SpacetimeDB.Internal.Module.RegisterView(); diff --git a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs index e8ae4418239..f0d41da28d4 100644 --- a/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs +++ b/crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs @@ -1656,6 +1656,9 @@ public static void Main() SpacetimeDB.Internal.Module.RegisterReducer(); SpacetimeDB.Internal.Module.RegisterReducer(); + // IMPORTANT: The order in which we register views matters. + // It must correspond to the order in which we call `GenerateDispatcherClass`. + // See the comment on `GenerateDispatcherClass` for more explanation. SpacetimeDB.Internal.Module.RegisterView(); SpacetimeDB.Internal.Module.RegisterAnonymousView(); diff --git a/sdks/csharp/examples~/regression-tests/client/EqualityOperations.cs b/sdks/csharp/examples~/regression-tests/client/EqualityOperations.cs new file mode 100644 index 00000000000..924d0d81b34 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/EqualityOperations.cs @@ -0,0 +1,25 @@ +using SpacetimeDB.Types; + +public static class EqualityOperations +{ + public static bool Equals(this Player lh, Player rh) + { + return lh.Id == rh.Id && + lh.Identity == rh.Identity && + lh.Name == rh.Name; + } + + public static bool Equals(this PlayerLevel lh, PlayerLevel rh) + { + return lh.Level == rh.Level && + lh.PlayerId == rh.PlayerId; + } + + public static bool Equals(this PlayerAndLevel lh, PlayerAndLevel rh) + { + return lh.Id == rh.Id && + lh.Identity == rh.Identity && + lh.Name == rh.Name && + lh.Level == rh.Level; + } +} \ No newline at end of file diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index 3a56f326389..9b9f6d15b19 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -151,23 +151,34 @@ void OnSubscriptionApplied(SubscriptionEventContext context) Log.Debug("Validating View row data " + $"Id={expectedPlayer.Id}, Identity={expectedPlayer.Identity}, Name={expectedPlayer.Name} => " + $"Id={viewIterRows.First().Id}, Identity={viewIterRows.First().Identity}, Name={viewIterRows.First().Name}"); - Debug.Assert(viewIterRows.First().Id == expectedPlayer.Id && - viewIterRows.First().Identity == expectedPlayer.Identity && - viewIterRows.First().Name == expectedPlayer.Name); + Debug.Assert(viewIterRows.First().Equals(expectedPlayer)); Log.Debug("Calling RemoteQuery on View"); var viewRemoteQueryRows = context.Db.MyPlayer.RemoteQuery("WHERE Id > 0"); Debug.Assert(viewRemoteQueryRows != null && viewRemoteQueryRows.Result.Length > 0); + Debug.Assert(viewRemoteQueryRows.Result.First().Equals(expectedPlayer)); Log.Debug("Calling Iter on Anonymous View"); var anonViewIterRows = context.Db.PlayersForLevel.Iter(); + var expectedPlayerAndLevel = new PlayerAndLevel + { + Id = 1, + Identity = context.Identity!.Value, + Name = "NewPlayer", + Level = 1 + }; Log.Debug("PlayersForLevel Iter count: " + (anonViewIterRows != null ? anonViewIterRows.Count().ToString() : "null")); Debug.Assert(anonViewIterRows != null && anonViewIterRows.Any()); + Log.Debug("Validating Anonymous View row data " + + $"Id={expectedPlayerAndLevel.Id}, Identity={expectedPlayerAndLevel.Identity}, Name={expectedPlayerAndLevel.Name}, Level={expectedPlayerAndLevel.Level} => " + + $"Id={anonViewIterRows.First().Id}, Identity={anonViewIterRows.First().Identity}, Name={anonViewIterRows.First().Name}, Level={anonViewIterRows.First().Level}"); + Debug.Assert(anonViewIterRows.First().Equals(expectedPlayerAndLevel)); Log.Debug("Calling RemoteQuery on Anonymous View"); var anonViewRemoteQueryRows = context.Db.PlayersForLevel.RemoteQuery("WHERE Level = 1"); Log.Debug("PlayersForLevel RemoteQuery count: " + (anonViewRemoteQueryRows != null ? anonViewRemoteQueryRows.Result.Length.ToString() : "null")); Debug.Assert(anonViewRemoteQueryRows != null && anonViewRemoteQueryRows.Result.Length > 0); + Debug.Assert(anonViewRemoteQueryRows.Result.First().Equals(expectedPlayerAndLevel)); } System.AppDomain.CurrentDomain.UnhandledException += (sender, args) => From 6fc761a45aa35e6b21b94a90ea3186ac635c0e7e Mon Sep 17 00:00:00 2001 From: Julien Lavocat Date: Thu, 20 Nov 2025 16:36:44 +0100 Subject: [PATCH 22/38] Add extra claims to v1/identity/websocket-token --- crates/auth/src/identity.rs | 9 +++++++++ crates/client-api/src/auth.rs | 8 ++++++++ crates/core/src/auth/token_validation.rs | 4 ++++ crates/core/src/client/client_connection.rs | 1 + 4 files changed, 22 insertions(+) diff --git a/crates/auth/src/identity.rs b/crates/auth/src/identity.rs index 59e16a61d3d..5298a8f8954 100644 --- a/crates/auth/src/identity.rs +++ b/crates/auth/src/identity.rs @@ -4,6 +4,7 @@ pub use jsonwebtoken::{DecodingKey, EncodingKey}; use serde::Deserializer; use serde::{Deserialize, Serialize}; use spacetimedb_lib::Identity; +use std::collections::HashMap; use std::time::SystemTime; #[derive(Debug, Clone)] @@ -41,6 +42,9 @@ pub struct SpacetimeIdentityClaims { pub iat: SystemTime, #[serde_as(as = "Option")] pub exp: Option, + + #[serde(flatten)] + pub extra: Option>, } fn deserialize_audience<'de, D>(deserializer: D) -> Result, D::Error> @@ -84,6 +88,10 @@ pub struct IncomingClaims { pub iat: SystemTime, #[serde_as(as = "Option")] pub exp: Option, + + /// All remaining claims from the JWT payload + #[serde(flatten)] + pub extra: Option>, } impl TryInto for IncomingClaims { @@ -122,6 +130,7 @@ impl TryInto for IncomingClaims { audience: self.audience, iat: self.iat, exp: self.exp, + extra: self.extra, }) } } diff --git a/crates/client-api/src/auth.rs b/crates/client-api/src/auth.rs index b9f091e6f08..76e4e37d74d 100644 --- a/crates/client-api/src/auth.rs +++ b/crates/client-api/src/auth.rs @@ -14,6 +14,7 @@ use spacetimedb::auth::token_validation::{ use spacetimedb::auth::JwtKeys; use spacetimedb::energy::EnergyQuanta; use spacetimedb::identity::Identity; +use std::collections::HashMap; use std::time::{Duration, SystemTime}; use uuid::Uuid; @@ -117,6 +118,7 @@ pub struct TokenClaims { pub issuer: String, pub subject: String, pub audience: Vec, + pub extra: Option>, } impl From for TokenClaims { @@ -126,6 +128,7 @@ impl From for TokenClaims { subject: auth.claims.subject, // This will need to be changed when we care about audiencies. audience: Vec::new(), + extra: auth.claims.extra, } } } @@ -136,6 +139,7 @@ impl TokenClaims { issuer, subject, audience: Vec::new(), + extra: None, } } @@ -159,6 +163,7 @@ impl TokenClaims { subject: self.subject.clone(), issuer: self.issuer.clone(), audience: self.audience.clone(), + extra: self.extra.clone(), iat, exp, }; @@ -184,6 +189,7 @@ impl SpacetimeAuth { subject: subject.clone(), // Placeholder audience. audience: vec!["spacetimedb".to_string()], + extra: None, }; let (claims, token) = claims.encode_and_sign(ctx.jwt_auth_provider()).map_err(log_and_500)?; @@ -291,6 +297,7 @@ mod tests { issuer: "localhost".to_string(), subject: "test-subject".to_string(), audience: vec!["spacetimedb".to_string()], + extra: None, }; let id = claims.id(); let (_, token) = claims.encode_and_sign(&kp.private)?; @@ -310,6 +317,7 @@ mod tests { issuer: "localhost".to_string(), subject: "test-subject".to_string(), audience: vec![dummy_audience.clone()], + extra: None, }; let (_, token) = claims.encode_and_sign(&kp.private)?; let st_creds = SpacetimeCreds::from_signed_token(token); diff --git a/crates/core/src/auth/token_validation.rs b/crates/core/src/auth/token_validation.rs index ba017dd9e23..2e50cb317e2 100644 --- a/crates/core/src/auth/token_validation.rs +++ b/crates/core/src/auth/token_validation.rs @@ -349,6 +349,7 @@ mod tests { audience: vec![], iat: std::time::SystemTime::now(), exp: None, + extra: None, }; let token = kp.private.sign(&orig_claims)?; @@ -391,6 +392,7 @@ mod tests { audience: vec![], iat: std::time::SystemTime::now(), exp: None, + extra: None, }; let token = kp.private.sign(&orig_claims)?; @@ -444,6 +446,7 @@ mod tests { audience: vec![], iat: std::time::SystemTime::now(), exp: None, + extra: None, }; let token = kp.private.sign(&orig_claims)?; @@ -609,6 +612,7 @@ mod tests { audience: vec![], iat: std::time::SystemTime::now(), exp: None, + extra: None, }; for kp in [kp1, kp2] { log::debug!("Testing with key {:?}", kp.kid); diff --git a/crates/core/src/client/client_connection.rs b/crates/core/src/client/client_connection.rs index d52bc88f7c7..985532dc7d6 100644 --- a/crates/core/src/client/client_connection.rs +++ b/crates/core/src/client/client_connection.rs @@ -335,6 +335,7 @@ impl ClientConnectionSender { audience: vec![], iat: SystemTime::now(), exp: None, + extra: None, }; let sender = Self { id, From f3b628251be7d73fe250ce412c108c0edfbff07e Mon Sep 17 00:00:00 2001 From: Jeffrey Dallatezza Date: Thu, 20 Nov 2025 09:27:29 -0800 Subject: [PATCH 23/38] Add a small test, and dont throw away the audience --- crates/client-api/src/auth.rs | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/crates/client-api/src/auth.rs b/crates/client-api/src/auth.rs index 76e4e37d74d..0ba2d554d5a 100644 --- a/crates/client-api/src/auth.rs +++ b/crates/client-api/src/auth.rs @@ -126,8 +126,7 @@ impl From for TokenClaims { Self { issuer: auth.claims.issuer, subject: auth.claims.subject, - // This will need to be changed when we care about audiencies. - audience: Vec::new(), + audience: auth.claims.audience, extra: auth.claims.extra, } } @@ -286,7 +285,7 @@ mod tests { use anyhow::Ok; use spacetimedb::auth::{token_validation::TokenValidator, JwtKeys}; - use std::collections::HashSet; + use std::collections::{HashMap, HashSet}; // Make sure that when we encode TokenClaims, we can decode to get the expected identity. #[tokio::test] @@ -307,6 +306,35 @@ mod tests { Ok(()) } + fn to_hashmap(value: serde_json::Value) -> HashMap { + let mut map = HashMap::new(); + value.as_object().unwrap().iter().for_each(|(k, v)| { + map.insert(k.clone(), v.clone()); + }); + map + } + + // Make sure that when we encode TokenClaims, we can decode the extra claims. + #[tokio::test] + async fn decode_encoded_token_with_extra_claims() -> Result<(), anyhow::Error> { + let kp = JwtKeys::generate()?; + + let claims = TokenClaims { + issuer: "localhost".to_string(), + subject: "test-subject".to_string(), + audience: vec!["spacetimedb".to_string()], + extra: Some(to_hashmap(serde_json::json!({"custom_claim": "value"}))), + }; + let id = claims.id(); + let (_, token) = claims.encode_and_sign(&kp.private)?; + let decoded = kp.public.validate_token(&token).await?; + + assert_eq!(decoded.identity, id); + let custom_claim_value = decoded.extra.as_ref().unwrap().get("custom_claim").unwrap(); + assert_eq!(custom_claim_value.as_str().unwrap(), "value"); + Ok(()) + } + // Test that extracting a JWT payload from a valid token gets the json representation. #[tokio::test] async fn extract_payload() -> Result<(), anyhow::Error> { From 2417e05ccad32aed3d7c94821e0522e00c24d1ef Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:51:58 -0600 Subject: [PATCH 24/38] Upgrade to version 1.9.0 --- Cargo.lock | 232 +++++++++--------- Cargo.toml | 66 ++--- LICENSE.txt | 4 +- .../BSATN.Codegen/BSATN.Codegen.csproj | 2 +- .../BSATN.Runtime/BSATN.Runtime.csproj | 2 +- crates/bindings-csharp/Codegen/Codegen.csproj | 2 +- crates/bindings-csharp/Runtime/Runtime.csproj | 2 +- crates/bindings-typescript/package.json | 2 +- .../project/typescript/package._json | 2 +- .../basic-c-sharp/server/StdbModule.csproj | 2 +- .../templates/basic-rust/client/Cargo.toml | 2 +- .../templates/basic-rust/server/Cargo.toml | 2 +- .../server-csharp/StdbModule.csproj | 2 +- licenses/BSL.txt | 4 +- sdks/csharp/SpacetimeDB.ClientSDK.csproj | 6 +- .../quickstart-chat/server/StdbModule.csproj | 2 +- .../regression-tests/server/StdbModule.csproj | 2 +- sdks/csharp/package.json | 2 +- 18 files changed, 169 insertions(+), 169 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2e8e99950b..f29624df92f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -443,7 +443,7 @@ name = "benchmarks-module" version = "0.1.0" dependencies = [ "anyhow", - "spacetimedb 1.8.0", + "spacetimedb 1.9.0", ] [[package]] @@ -1082,7 +1082,7 @@ dependencies = [ [[package]] name = "connect_disconnect_client" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "spacetimedb-sdk", @@ -3488,7 +3488,7 @@ name = "keynote-benchmarks" version = "0.1.0" dependencies = [ "log", - "spacetimedb 1.8.0", + "spacetimedb 1.9.0", ] [[package]] @@ -3868,7 +3868,7 @@ version = "0.0.0" dependencies = [ "anyhow", "log", - "spacetimedb 1.8.0", + "spacetimedb 1.9.0", ] [[package]] @@ -4961,7 +4961,7 @@ name = "perf-test-module" version = "0.1.0" dependencies = [ "log", - "spacetimedb 1.8.0", + "spacetimedb 1.9.0", ] [[package]] @@ -5330,7 +5330,7 @@ dependencies = [ [[package]] name = "procedure-client" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "env_logger 0.10.2", @@ -5523,7 +5523,7 @@ name = "quickstart-chat-module" version = "0.1.0" dependencies = [ "log", - "spacetimedb 1.8.0", + "spacetimedb 1.9.0", ] [[package]] @@ -6623,7 +6623,7 @@ dependencies = [ "anyhow", "log", "paste", - "spacetimedb 1.8.0", + "spacetimedb 1.9.0", ] [[package]] @@ -6633,12 +6633,12 @@ dependencies = [ "anyhow", "log", "paste", - "spacetimedb 1.8.0", + "spacetimedb 1.9.0", ] [[package]] name = "sdk-unreal-test-harness" -version = "1.8.0" +version = "1.9.0" dependencies = [ "serial_test", "spacetimedb-testing", @@ -7072,7 +7072,7 @@ name = "spacetime-module" version = "0.1.0" dependencies = [ "log", - "spacetimedb 1.8.0", + "spacetimedb 1.9.0", ] [[package]] @@ -7095,7 +7095,7 @@ dependencies = [ [[package]] name = "spacetimedb" -version = "1.8.0" +version = "1.9.0" dependencies = [ "bytemuck", "derive_more 0.99.20", @@ -7105,28 +7105,28 @@ dependencies = [ "rand 0.8.5", "scoped-tls", "serde_json", - "spacetimedb-bindings-macro 1.8.0", - "spacetimedb-bindings-sys 1.8.0", - "spacetimedb-lib 1.8.0", - "spacetimedb-primitives 1.8.0", + "spacetimedb-bindings-macro 1.9.0", + "spacetimedb-bindings-sys 1.9.0", + "spacetimedb-lib 1.9.0", + "spacetimedb-primitives 1.9.0", "trybuild", ] [[package]] name = "spacetimedb-auth" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "serde", "serde_json", "serde_with", "spacetimedb-jsonwebtoken", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", ] [[package]] name = "spacetimedb-bench" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "anymap", @@ -7154,11 +7154,11 @@ dependencies = [ "spacetimedb-data-structures", "spacetimedb-datastore", "spacetimedb-execution", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-paths", - "spacetimedb-primitives 1.8.0", + "spacetimedb-primitives 1.9.0", "spacetimedb-query", - "spacetimedb-sats 1.8.0", + "spacetimedb-sats 1.9.0", "spacetimedb-schema", "spacetimedb-standalone", "spacetimedb-table", @@ -7187,13 +7187,13 @@ dependencies = [ [[package]] name = "spacetimedb-bindings-macro" -version = "1.8.0" +version = "1.9.0" dependencies = [ "heck 0.4.1", "humantime", "proc-macro2", "quote", - "spacetimedb-primitives 1.8.0", + "spacetimedb-primitives 1.9.0", "syn 2.0.107", ] @@ -7208,14 +7208,14 @@ dependencies = [ [[package]] name = "spacetimedb-bindings-sys" -version = "1.8.0" +version = "1.9.0" dependencies = [ - "spacetimedb-primitives 1.8.0", + "spacetimedb-primitives 1.9.0", ] [[package]] name = "spacetimedb-cli" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "base64 0.21.7", @@ -7259,9 +7259,9 @@ dependencies = [ "spacetimedb-data-structures", "spacetimedb-fs-utils", "spacetimedb-jsonwebtoken", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-paths", - "spacetimedb-primitives 1.8.0", + "spacetimedb-primitives 1.9.0", "spacetimedb-schema", "syntect", "tabled", @@ -7286,7 +7286,7 @@ dependencies = [ [[package]] name = "spacetimedb-client-api" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "async-stream", @@ -7323,7 +7323,7 @@ dependencies = [ "spacetimedb-data-structures", "spacetimedb-datastore", "spacetimedb-jsonwebtoken", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-paths", "spacetimedb-schema", "tempfile", @@ -7341,7 +7341,7 @@ dependencies = [ [[package]] name = "spacetimedb-client-api-messages" -version = "1.8.0" +version = "1.9.0" dependencies = [ "bytes", "bytestring", @@ -7355,16 +7355,16 @@ dependencies = [ "serde_json", "serde_with", "smallvec", - "spacetimedb-lib 1.8.0", - "spacetimedb-primitives 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb-lib 1.9.0", + "spacetimedb-primitives 1.9.0", + "spacetimedb-sats 1.9.0", "strum", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-codegen" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -7373,15 +7373,15 @@ dependencies = [ "itertools 0.12.1", "regex", "spacetimedb-data-structures", - "spacetimedb-lib 1.8.0", - "spacetimedb-primitives 1.8.0", + "spacetimedb-lib 1.9.0", + "spacetimedb-primitives 1.9.0", "spacetimedb-schema", "spacetimedb-testing", ] [[package]] name = "spacetimedb-commitlog" -version = "1.8.0" +version = "1.9.0" dependencies = [ "async-stream", "bitflags 2.10.0", @@ -7402,8 +7402,8 @@ dependencies = [ "spacetimedb-commitlog", "spacetimedb-fs-utils", "spacetimedb-paths", - "spacetimedb-primitives 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb-primitives 1.9.0", + "spacetimedb-sats 1.9.0", "tempfile", "thiserror 1.0.69", "tokio", @@ -7414,7 +7414,7 @@ dependencies = [ [[package]] name = "spacetimedb-core" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "arrayvec", @@ -7495,14 +7495,14 @@ dependencies = [ "spacetimedb-fs-utils", "spacetimedb-jsonwebtoken", "spacetimedb-jwks", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-memory-usage", "spacetimedb-metrics", "spacetimedb-paths", "spacetimedb-physical-plan", - "spacetimedb-primitives 1.8.0", + "spacetimedb-primitives 1.9.0", "spacetimedb-query", - "spacetimedb-sats 1.8.0", + "spacetimedb-sats 1.9.0", "spacetimedb-schema", "spacetimedb-snapshot", "spacetimedb-subscription", @@ -7537,7 +7537,7 @@ dependencies = [ [[package]] name = "spacetimedb-data-structures" -version = "1.8.0" +version = "1.9.0" dependencies = [ "ahash 0.8.12", "crossbeam-queue", @@ -7551,7 +7551,7 @@ dependencies = [ [[package]] name = "spacetimedb-datastore" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "bytes", @@ -7571,11 +7571,11 @@ dependencies = [ "spacetimedb-data-structures", "spacetimedb-durability", "spacetimedb-execution", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-metrics", "spacetimedb-paths", - "spacetimedb-primitives 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb-primitives 1.9.0", + "spacetimedb-sats 1.9.0", "spacetimedb-schema", "spacetimedb-snapshot", "spacetimedb-table", @@ -7586,7 +7586,7 @@ dependencies = [ [[package]] name = "spacetimedb-durability" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "itertools 0.12.1", @@ -7594,7 +7594,7 @@ dependencies = [ "scopeguard", "spacetimedb-commitlog", "spacetimedb-paths", - "spacetimedb-sats 1.8.0", + "spacetimedb-sats 1.9.0", "tempfile", "thiserror 1.0.69", "tokio", @@ -7603,22 +7603,22 @@ dependencies = [ [[package]] name = "spacetimedb-execution" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "itertools 0.12.1", "spacetimedb-expr", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-physical-plan", - "spacetimedb-primitives 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb-primitives 1.9.0", + "spacetimedb-sats 1.9.0", "spacetimedb-sql-parser", "spacetimedb-table", ] [[package]] name = "spacetimedb-expr" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "bigdecimal", @@ -7626,10 +7626,10 @@ dependencies = [ "derive_more 0.99.20", "ethnum", "pretty_assertions", - "spacetimedb 1.8.0", - "spacetimedb-lib 1.8.0", - "spacetimedb-primitives 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb 1.9.0", + "spacetimedb-lib 1.9.0", + "spacetimedb-primitives 1.9.0", + "spacetimedb-sats 1.9.0", "spacetimedb-schema", "spacetimedb-sql-parser", "thiserror 1.0.69", @@ -7637,7 +7637,7 @@ dependencies = [ [[package]] name = "spacetimedb-fs-utils" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "hex", @@ -7699,7 +7699,7 @@ dependencies = [ [[package]] name = "spacetimedb-lib" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "bitflags 2.10.0", @@ -7717,17 +7717,17 @@ dependencies = [ "ron", "serde", "serde_json", - "spacetimedb-bindings-macro 1.8.0", + "spacetimedb-bindings-macro 1.9.0", "spacetimedb-memory-usage", "spacetimedb-metrics", - "spacetimedb-primitives 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb-primitives 1.9.0", + "spacetimedb-sats 1.9.0", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-memory-usage" -version = "1.8.0" +version = "1.9.0" dependencies = [ "decorum", "ethnum", @@ -7737,7 +7737,7 @@ dependencies = [ [[package]] name = "spacetimedb-metrics" -version = "1.8.0" +version = "1.9.0" dependencies = [ "arrayvec", "itertools 0.12.1", @@ -7747,7 +7747,7 @@ dependencies = [ [[package]] name = "spacetimedb-paths" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "chrono", @@ -7763,7 +7763,7 @@ dependencies = [ [[package]] name = "spacetimedb-pg" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "async-trait", @@ -7774,22 +7774,22 @@ dependencies = [ "pgwire", "spacetimedb-client-api", "spacetimedb-client-api-messages", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "thiserror 1.0.69", "tokio", ] [[package]] name = "spacetimedb-physical-plan" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "derive_more 0.99.20", "either", "pretty_assertions", "spacetimedb-expr", - "spacetimedb-lib 1.8.0", - "spacetimedb-primitives 1.8.0", + "spacetimedb-lib 1.9.0", + "spacetimedb-primitives 1.9.0", "spacetimedb-schema", "spacetimedb-sql-parser", "spacetimedb-table", @@ -7809,7 +7809,7 @@ dependencies = [ [[package]] name = "spacetimedb-primitives" -version = "1.8.0" +version = "1.9.0" dependencies = [ "bitflags 2.10.0", "either", @@ -7822,7 +7822,7 @@ dependencies = [ [[package]] name = "spacetimedb-query" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "itertools 0.12.1", @@ -7830,9 +7830,9 @@ dependencies = [ "spacetimedb-client-api-messages", "spacetimedb-execution", "spacetimedb-expr", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-physical-plan", - "spacetimedb-primitives 1.8.0", + "spacetimedb-primitives 1.9.0", "spacetimedb-sql-parser", "spacetimedb-table", ] @@ -7865,7 +7865,7 @@ dependencies = [ [[package]] name = "spacetimedb-sats" -version = "1.8.0" +version = "1.9.0" dependencies = [ "ahash 0.8.12", "anyhow", @@ -7890,16 +7890,16 @@ dependencies = [ "serde_json", "sha3", "smallvec", - "spacetimedb-bindings-macro 1.8.0", + "spacetimedb-bindings-macro 1.9.0", "spacetimedb-memory-usage", "spacetimedb-metrics", - "spacetimedb-primitives 1.8.0", + "spacetimedb-primitives 1.9.0", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-schema" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "derive_more 0.99.20", @@ -7916,9 +7916,9 @@ dependencies = [ "serial_test", "smallvec", "spacetimedb-data-structures", - "spacetimedb-lib 1.8.0", - "spacetimedb-primitives 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb-lib 1.9.0", + "spacetimedb-primitives 1.9.0", + "spacetimedb-sats 1.9.0", "spacetimedb-sql-parser", "spacetimedb-testing", "termcolor", @@ -7929,7 +7929,7 @@ dependencies = [ [[package]] name = "spacetimedb-sdk" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anymap", "base64 0.21.7", @@ -7948,9 +7948,9 @@ dependencies = [ "rand 0.9.2", "spacetimedb-client-api-messages", "spacetimedb-data-structures", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-metrics", - "spacetimedb-sats 1.8.0", + "spacetimedb-sats 1.9.0", "spacetimedb-testing", "thiserror 1.0.69", "tokio", @@ -7959,7 +7959,7 @@ dependencies = [ [[package]] name = "spacetimedb-snapshot" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "blake3", @@ -7976,10 +7976,10 @@ dependencies = [ "spacetimedb-datastore", "spacetimedb-durability", "spacetimedb-fs-utils", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-paths", - "spacetimedb-primitives 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb-primitives 1.9.0", + "spacetimedb-sats 1.9.0", "spacetimedb-schema", "spacetimedb-table", "tempfile", @@ -7992,17 +7992,17 @@ dependencies = [ [[package]] name = "spacetimedb-sql-parser" -version = "1.8.0" +version = "1.9.0" dependencies = [ "derive_more 0.99.20", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "sqlparser", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-standalone" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "async-trait", @@ -8026,7 +8026,7 @@ dependencies = [ "spacetimedb-client-api-messages", "spacetimedb-core", "spacetimedb-datastore", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-paths", "spacetimedb-pg", "spacetimedb-schema", @@ -8043,20 +8043,20 @@ dependencies = [ [[package]] name = "spacetimedb-subscription" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "spacetimedb-execution", "spacetimedb-expr", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-physical-plan", - "spacetimedb-primitives 1.8.0", + "spacetimedb-primitives 1.9.0", "spacetimedb-query", ] [[package]] name = "spacetimedb-table" -version = "1.8.0" +version = "1.9.0" dependencies = [ "ahash 0.8.12", "blake3", @@ -8072,17 +8072,17 @@ dependencies = [ "rand 0.9.2", "smallvec", "spacetimedb-data-structures", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-memory-usage", - "spacetimedb-primitives 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb-primitives 1.9.0", + "spacetimedb-sats 1.9.0", "spacetimedb-schema", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-testing" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "bytes", @@ -8099,7 +8099,7 @@ dependencies = [ "spacetimedb-client-api", "spacetimedb-core", "spacetimedb-data-structures", - "spacetimedb-lib 1.8.0", + "spacetimedb-lib 1.9.0", "spacetimedb-paths", "spacetimedb-schema", "spacetimedb-standalone", @@ -8110,7 +8110,7 @@ dependencies = [ [[package]] name = "spacetimedb-update" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "bytes", @@ -8135,7 +8135,7 @@ dependencies = [ [[package]] name = "spacetimedb-vm" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "arrayvec", @@ -8145,9 +8145,9 @@ dependencies = [ "smallvec", "spacetimedb-data-structures", "spacetimedb-execution", - "spacetimedb-lib 1.8.0", - "spacetimedb-primitives 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb-lib 1.9.0", + "spacetimedb-primitives 1.9.0", + "spacetimedb-sats 1.9.0", "spacetimedb-schema", "spacetimedb-table", "tempfile", @@ -8229,7 +8229,7 @@ dependencies = [ [[package]] name = "sqltest" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "async-trait", @@ -8248,8 +8248,8 @@ dependencies = [ "rust_decimal", "spacetimedb-core", "spacetimedb-datastore", - "spacetimedb-lib 1.8.0", - "spacetimedb-sats 1.8.0", + "spacetimedb-lib 1.9.0", + "spacetimedb-sats 1.9.0", "spacetimedb-vm", "sqllogictest", "sqllogictest-engines", @@ -8629,7 +8629,7 @@ checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "test-client" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "env_logger 0.10.2", @@ -8641,7 +8641,7 @@ dependencies = [ [[package]] name = "test-counter" -version = "1.8.0" +version = "1.9.0" dependencies = [ "anyhow", "spacetimedb-data-structures", @@ -9369,7 +9369,7 @@ version = "0.1.0" dependencies = [ "anyhow", "log", - "spacetimedb 1.8.0", + "spacetimedb 1.9.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ee371ec2be0..78efac14a01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,44 +94,44 @@ inherits = "release" debug = true [workspace.package] -version = "1.8.0" +version = "1.9.0" edition = "2021" # update rust-toolchain.toml too! rust-version = "1.90.0" [workspace.dependencies] -spacetimedb = { path = "crates/bindings", version = "=1.8.0" } -spacetimedb-auth = { path = "crates/auth", version = "=1.8.0" } -spacetimedb-bindings-macro = { path = "crates/bindings-macro", version = "=1.8.0" } -spacetimedb-bindings-sys = { path = "crates/bindings-sys", version = "=1.8.0" } -spacetimedb-cli = { path = "crates/cli", version = "=1.8.0" } -spacetimedb-client-api = { path = "crates/client-api", version = "=1.8.0" } -spacetimedb-client-api-messages = { path = "crates/client-api-messages", version = "=1.8.0" } -spacetimedb-codegen = { path = "crates/codegen", version = "=1.8.0" } -spacetimedb-commitlog = { path = "crates/commitlog", version = "=1.8.0" } -spacetimedb-core = { path = "crates/core", version = "=1.8.0" } -spacetimedb-data-structures = { path = "crates/data-structures", version = "=1.8.0" } -spacetimedb-datastore = { path = "crates/datastore", version = "=1.8.0" } -spacetimedb-durability = { path = "crates/durability", version = "=1.8.0" } -spacetimedb-execution = { path = "crates/execution", version = "=1.8.0" } -spacetimedb-expr = { path = "crates/expr", version = "=1.8.0" } -spacetimedb-lib = { path = "crates/lib", default-features = false, version = "=1.8.0" } -spacetimedb-memory-usage = { path = "crates/memory-usage", version = "=1.8.0", default-features = false } -spacetimedb-metrics = { path = "crates/metrics", version = "=1.8.0" } -spacetimedb-paths = { path = "crates/paths", version = "=1.8.0" } -spacetimedb-pg = { path = "crates/pg", version = "=1.8.0" } -spacetimedb-physical-plan = { path = "crates/physical-plan", version = "=1.8.0" } -spacetimedb-primitives = { path = "crates/primitives", version = "=1.8.0" } -spacetimedb-query = { path = "crates/query", version = "=1.8.0" } -spacetimedb-sats = { path = "crates/sats", version = "=1.8.0" } -spacetimedb-schema = { path = "crates/schema", version = "=1.8.0" } -spacetimedb-standalone = { path = "crates/standalone", version = "=1.8.0" } -spacetimedb-sql-parser = { path = "crates/sql-parser", version = "=1.8.0" } -spacetimedb-table = { path = "crates/table", version = "=1.8.0" } -spacetimedb-vm = { path = "crates/vm", version = "=1.8.0" } -spacetimedb-fs-utils = { path = "crates/fs-utils", version = "=1.8.0" } -spacetimedb-snapshot = { path = "crates/snapshot", version = "=1.8.0" } -spacetimedb-subscription = { path = "crates/subscription", version = "=1.8.0" } +spacetimedb = { path = "crates/bindings", version = "=1.9.0" } +spacetimedb-auth = { path = "crates/auth", version = "=1.9.0" } +spacetimedb-bindings-macro = { path = "crates/bindings-macro", version = "=1.9.0" } +spacetimedb-bindings-sys = { path = "crates/bindings-sys", version = "=1.9.0" } +spacetimedb-cli = { path = "crates/cli", version = "=1.9.0" } +spacetimedb-client-api = { path = "crates/client-api", version = "=1.9.0" } +spacetimedb-client-api-messages = { path = "crates/client-api-messages", version = "=1.9.0" } +spacetimedb-codegen = { path = "crates/codegen", version = "=1.9.0" } +spacetimedb-commitlog = { path = "crates/commitlog", version = "=1.9.0" } +spacetimedb-core = { path = "crates/core", version = "=1.9.0" } +spacetimedb-data-structures = { path = "crates/data-structures", version = "=1.9.0" } +spacetimedb-datastore = { path = "crates/datastore", version = "=1.9.0" } +spacetimedb-durability = { path = "crates/durability", version = "=1.9.0" } +spacetimedb-execution = { path = "crates/execution", version = "=1.9.0" } +spacetimedb-expr = { path = "crates/expr", version = "=1.9.0" } +spacetimedb-lib = { path = "crates/lib", default-features = false, version = "=1.9.0" } +spacetimedb-memory-usage = { path = "crates/memory-usage", version = "=1.9.0", default-features = false } +spacetimedb-metrics = { path = "crates/metrics", version = "=1.9.0" } +spacetimedb-paths = { path = "crates/paths", version = "=1.9.0" } +spacetimedb-pg = { path = "crates/pg", version = "=1.9.0" } +spacetimedb-physical-plan = { path = "crates/physical-plan", version = "=1.9.0" } +spacetimedb-primitives = { path = "crates/primitives", version = "=1.9.0" } +spacetimedb-query = { path = "crates/query", version = "=1.9.0" } +spacetimedb-sats = { path = "crates/sats", version = "=1.9.0" } +spacetimedb-schema = { path = "crates/schema", version = "=1.9.0" } +spacetimedb-standalone = { path = "crates/standalone", version = "=1.9.0" } +spacetimedb-sql-parser = { path = "crates/sql-parser", version = "=1.9.0" } +spacetimedb-table = { path = "crates/table", version = "=1.9.0" } +spacetimedb-vm = { path = "crates/vm", version = "=1.9.0" } +spacetimedb-fs-utils = { path = "crates/fs-utils", version = "=1.9.0" } +spacetimedb-snapshot = { path = "crates/snapshot", version = "=1.9.0" } +spacetimedb-subscription = { path = "crates/subscription", version = "=1.9.0" } # Prevent `ahash` from pulling in `getrandom` by disabling default features. # Modules use `getrandom02` and we need to prevent an incompatible version diff --git a/LICENSE.txt b/LICENSE.txt index e87554ac2f1..0c2732e24a3 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -5,7 +5,7 @@ Business Source License 1.1 Parameters Licensor: Clockwork Laboratories, Inc. -Licensed Work: SpacetimeDB 1.8.0 +Licensed Work: SpacetimeDB 1.9.0 The Licensed Work is (c) 2023 Clockwork Laboratories, Inc. @@ -21,7 +21,7 @@ Additional Use Grant: You may make use of the Licensed Work provided your Licensed Work by creating tables whose schemas are controlled by such third parties. -Change Date: 2030-11-11 +Change Date: 2030-11-20 Change License: GNU Affero General Public License v3.0 with a linking exception diff --git a/crates/bindings-csharp/BSATN.Codegen/BSATN.Codegen.csproj b/crates/bindings-csharp/BSATN.Codegen/BSATN.Codegen.csproj index 0b9d4d5d988..2157c723aef 100644 --- a/crates/bindings-csharp/BSATN.Codegen/BSATN.Codegen.csproj +++ b/crates/bindings-csharp/BSATN.Codegen/BSATN.Codegen.csproj @@ -2,7 +2,7 @@ SpacetimeDB.BSATN.Codegen - 1.8.0 + 1.9.0 SpacetimeDB BSATN Codegen The SpacetimeDB BSATN Codegen implements the Roslyn incremental generators for BSATN serialization/deserialization in C#. diff --git a/crates/bindings-csharp/BSATN.Runtime/BSATN.Runtime.csproj b/crates/bindings-csharp/BSATN.Runtime/BSATN.Runtime.csproj index cabd8839c73..ae7680b99bd 100644 --- a/crates/bindings-csharp/BSATN.Runtime/BSATN.Runtime.csproj +++ b/crates/bindings-csharp/BSATN.Runtime/BSATN.Runtime.csproj @@ -2,7 +2,7 @@ SpacetimeDB.BSATN.Runtime - 1.8.0 + 1.9.0 SpacetimeDB BSATN Runtime The SpacetimeDB BSATN Runtime implements APIs for BSATN serialization/deserialization in C#. true diff --git a/crates/bindings-csharp/Codegen/Codegen.csproj b/crates/bindings-csharp/Codegen/Codegen.csproj index d75f321631f..30e15115f23 100644 --- a/crates/bindings-csharp/Codegen/Codegen.csproj +++ b/crates/bindings-csharp/Codegen/Codegen.csproj @@ -2,7 +2,7 @@ SpacetimeDB.Codegen - 1.8.0 + 1.9.0 SpacetimeDB Module Codegen The SpacetimeDB Codegen implements the Roslyn incremental generators for writing SpacetimeDB modules in C#. diff --git a/crates/bindings-csharp/Runtime/Runtime.csproj b/crates/bindings-csharp/Runtime/Runtime.csproj index 1263f264005..9ee3b3e542e 100644 --- a/crates/bindings-csharp/Runtime/Runtime.csproj +++ b/crates/bindings-csharp/Runtime/Runtime.csproj @@ -2,7 +2,7 @@ SpacetimeDB.Runtime - 1.8.0 + 1.9.0 SpacetimeDB Module Runtime The SpacetimeDB Runtime implements the database runtime bindings for writing SpacetimeDB modules in C#. diff --git a/crates/bindings-typescript/package.json b/crates/bindings-typescript/package.json index 59c3cd41a13..a65f6719cee 100644 --- a/crates/bindings-typescript/package.json +++ b/crates/bindings-typescript/package.json @@ -1,6 +1,6 @@ { "name": "spacetimedb", - "version": "1.8.0", + "version": "1.9.0", "description": "API and ABI bindings for the SpacetimeDB TypeScript module library", "homepage": "https://github.com/clockworklabs/SpacetimeDB#readme", "bugs": { diff --git a/crates/cli/src/subcommands/project/typescript/package._json b/crates/cli/src/subcommands/project/typescript/package._json index 916b89a7729..2ab52d6a89b 100644 --- a/crates/cli/src/subcommands/project/typescript/package._json +++ b/crates/cli/src/subcommands/project/typescript/package._json @@ -10,6 +10,6 @@ "author": "", "license": "ISC", "dependencies": { - "spacetimedb": "1.8.*" + "spacetimedb": "1.9.*" } } \ No newline at end of file diff --git a/crates/cli/templates/basic-c-sharp/server/StdbModule.csproj b/crates/cli/templates/basic-c-sharp/server/StdbModule.csproj index 75dd64cb9f7..e36d30360ec 100644 --- a/crates/cli/templates/basic-c-sharp/server/StdbModule.csproj +++ b/crates/cli/templates/basic-c-sharp/server/StdbModule.csproj @@ -8,7 +8,7 @@ - + diff --git a/crates/cli/templates/basic-rust/client/Cargo.toml b/crates/cli/templates/basic-rust/client/Cargo.toml index 4426f687482..71d43c3c131 100644 --- a/crates/cli/templates/basic-rust/client/Cargo.toml +++ b/crates/cli/templates/basic-rust/client/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -spacetimedb-sdk = "1.8.*" +spacetimedb-sdk = "1.9.*" diff --git a/crates/cli/templates/basic-rust/server/Cargo.toml b/crates/cli/templates/basic-rust/server/Cargo.toml index 5cab11f9281..d90955c6d37 100644 --- a/crates/cli/templates/basic-rust/server/Cargo.toml +++ b/crates/cli/templates/basic-rust/server/Cargo.toml @@ -9,5 +9,5 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -spacetimedb = "1.8.*" +spacetimedb = "1.9.*" log = "0.4" diff --git a/demo/Blackholio/server-csharp/StdbModule.csproj b/demo/Blackholio/server-csharp/StdbModule.csproj index ed84bc2a56b..9308e3399f6 100644 --- a/demo/Blackholio/server-csharp/StdbModule.csproj +++ b/demo/Blackholio/server-csharp/StdbModule.csproj @@ -13,7 +13,7 @@ - + diff --git a/licenses/BSL.txt b/licenses/BSL.txt index d8b600245f0..7d842785e2a 100644 --- a/licenses/BSL.txt +++ b/licenses/BSL.txt @@ -5,7 +5,7 @@ Business Source License 1.1 Parameters Licensor: Clockwork Laboratories, Inc. -Licensed Work: SpacetimeDB 1.8.0 +Licensed Work: SpacetimeDB 1.9.0 The Licensed Work is (c) 2023 Clockwork Laboratories, Inc. @@ -21,7 +21,7 @@ Additional Use Grant: You may make use of the Licensed Work provided your Licensed Work by creating tables whose schemas are controlled by such third parties. -Change Date: 2030-11-11 +Change Date: 2030-11-20 Change License: GNU Affero General Public License v3.0 with a linking exception diff --git a/sdks/csharp/SpacetimeDB.ClientSDK.csproj b/sdks/csharp/SpacetimeDB.ClientSDK.csproj index 490bd10a305..69f36aa3cdc 100644 --- a/sdks/csharp/SpacetimeDB.ClientSDK.csproj +++ b/sdks/csharp/SpacetimeDB.ClientSDK.csproj @@ -16,8 +16,8 @@ logo.png README.md https://github.com/clockworklabs/com.clockworklabs.spacetimedbsdk - 1.8.0 - 1.8.0 + 1.9.0 + 1.9.0 $(DefaultItemExcludes);*~/** packages @@ -25,7 +25,7 @@ - + diff --git a/sdks/csharp/examples~/quickstart-chat/server/StdbModule.csproj b/sdks/csharp/examples~/quickstart-chat/server/StdbModule.csproj index 528f485954f..8602a71980a 100644 --- a/sdks/csharp/examples~/quickstart-chat/server/StdbModule.csproj +++ b/sdks/csharp/examples~/quickstart-chat/server/StdbModule.csproj @@ -14,7 +14,7 @@ - + diff --git a/sdks/csharp/examples~/regression-tests/server/StdbModule.csproj b/sdks/csharp/examples~/regression-tests/server/StdbModule.csproj index 75dd64cb9f7..e36d30360ec 100644 --- a/sdks/csharp/examples~/regression-tests/server/StdbModule.csproj +++ b/sdks/csharp/examples~/regression-tests/server/StdbModule.csproj @@ -8,7 +8,7 @@ - + diff --git a/sdks/csharp/package.json b/sdks/csharp/package.json index 8ff54b8afe6..fbfcf52a6a6 100644 --- a/sdks/csharp/package.json +++ b/sdks/csharp/package.json @@ -1,7 +1,7 @@ { "name": "com.clockworklabs.spacetimedbsdk", "displayName": "SpacetimeDB SDK", - "version": "1.8.0", + "version": "1.9.0", "description": "The SpacetimeDB Client SDK is a software development kit (SDK) designed to interact with and manipulate SpacetimeDB modules..", "keywords": [], "author": { From a537155affc3553a53a8c45022c54d957670e707 Mon Sep 17 00:00:00 2001 From: Tyler Cloutier Date: Thu, 20 Nov 2025 14:28:02 -0500 Subject: [PATCH 25/38] Added staging to allowable issuers --- crates/client-api/src/routes/database.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/client-api/src/routes/database.rs b/crates/client-api/src/routes/database.rs index 8d86709e68e..0a0590caab7 100644 --- a/crates/client-api/src/routes/database.rs +++ b/crates/client-api/src/routes/database.rs @@ -53,7 +53,8 @@ fn allow_creation(auth: &SpacetimeAuth) -> Result<(), ErrorResponse> { if !require_spacetime_auth_for_creation() { return Ok(()); } - if auth.claims.issuer.trim_end_matches('/') == "https://auth.spacetimedb.com" { + let issuer = auth.claims.issuer.trim_end_matches('/'); + if issuer == "https://auth.spacetimedb.com" || issuer == "https://auth.staging.spacetimedb.com" { Ok(()) } else { log::trace!( From db4368cc2efc1fc6e08701f01c95b54084d36a97 Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:37:50 -0600 Subject: [PATCH 26/38] Ran bindgen --- .../examples/quickstart-chat/src/module_bindings/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts b/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts index 54c2f50c27a..1f2a8a8b9c5 100644 --- a/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts +++ b/crates/bindings-typescript/examples/quickstart-chat/src/module_bindings/index.ts @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.8.0 (commit 3f1ec77822a11345de517e72dbcefe06cc9277d4). +// This was generated using spacetimedb cli version 1.9.0 (commit 2417e05ccad32aed3d7c94821e0522e00c24d1ef). /* eslint-disable */ /* tslint:disable */ @@ -83,7 +83,7 @@ const reducersSchema = __reducers( const REMOTE_MODULE = { versionInfo: { - cliVersion: '1.8.0' as const, + cliVersion: '1.9.0' as const, }, tables: tablesSchema.schemaType.tables, reducers: reducersSchema.reducersType.reducers, From dec22935510ebb6981b3e275920c4d51c202b997 Mon Sep 17 00:00:00 2001 From: Tyler Cloutier Date: Thu, 20 Nov 2025 14:47:49 -0500 Subject: [PATCH 27/38] Sets the required issuer via an ENV variable --- crates/client-api/src/routes/database.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/crates/client-api/src/routes/database.rs b/crates/client-api/src/routes/database.rs index 0a0590caab7..8363c916d00 100644 --- a/crates/client-api/src/routes/database.rs +++ b/crates/client-api/src/routes/database.rs @@ -44,22 +44,31 @@ use spacetimedb_schema::auto_migrate::{ use super::subscribe::{handle_websocket, HasWebSocketOptions}; -fn require_spacetime_auth_for_creation() -> bool { - env::var("TEMP_REQUIRE_SPACETIME_AUTH").is_ok_and(|v| !v.is_empty()) +fn require_spacetime_auth_for_creation() -> Option { + // If the string is a non-empty value, require SpacetimeAuth for database creation + // and return the value for logging purposes. + // TODO(cloutiertyler): This env var replaces TEMP_REQUIRE_SPACETIME_AUTH, + // we should remove that one in the future. We may eventually remove + // the below restriction entirely as well in Maincloud. + match env::var("TEMP_SPACETIMEAUTH_ISSUER_REQUIRED_TO_PUBLISH") { + Ok(v) if !v.is_empty() => Some(v), + _ => None, + } } // A hacky function to let us restrict database creation on maincloud. fn allow_creation(auth: &SpacetimeAuth) -> Result<(), ErrorResponse> { - if !require_spacetime_auth_for_creation() { + let Some(required_issuer) = require_spacetime_auth_for_creation() else { return Ok(()); - } + }; let issuer = auth.claims.issuer.trim_end_matches('/'); - if issuer == "https://auth.spacetimedb.com" || issuer == "https://auth.staging.spacetimedb.com" { + if issuer == required_issuer { Ok(()) } else { log::trace!( - "Rejecting creation request because auth issuer is {}", - auth.claims.issuer + "Rejecting creation request because auth issuer is {} and required issuer is {}", + auth.claims.issuer, + required_issuer ); Err(( StatusCode::UNAUTHORIZED, From 61dc60218ec5ab5e0c2d7c574924e3875093e499 Mon Sep 17 00:00:00 2001 From: Tyler Cloutier Date: Thu, 20 Nov 2025 14:56:37 -0500 Subject: [PATCH 28/38] Apply suggestion from @cloutiertyler Signed-off-by: Tyler Cloutier --- crates/client-api/src/routes/database.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/client-api/src/routes/database.rs b/crates/client-api/src/routes/database.rs index 8363c916d00..dce92cb4d57 100644 --- a/crates/client-api/src/routes/database.rs +++ b/crates/client-api/src/routes/database.rs @@ -45,8 +45,7 @@ use spacetimedb_schema::auto_migrate::{ use super::subscribe::{handle_websocket, HasWebSocketOptions}; fn require_spacetime_auth_for_creation() -> Option { - // If the string is a non-empty value, require SpacetimeAuth for database creation - // and return the value for logging purposes. + // If the string is a non-empty value, return the string to be used as the required issuer // TODO(cloutiertyler): This env var replaces TEMP_REQUIRE_SPACETIME_AUTH, // we should remove that one in the future. We may eventually remove // the below restriction entirely as well in Maincloud. From 868b2a6920e51a29532f00ba45407a9bf7281529 Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:24:31 -0600 Subject: [PATCH 29/38] Use custom runner for building CLI --- .github/workflows/package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 202a67547d6..fe45fa68999 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: include: - - { name: x86_64 Linux, target: x86_64-unknown-linux-gnu, runner: ubuntu-latest } + - { name: x86_64 Linux, target: x86_64-unknown-linux-gnu, runner: spacetimedb-new-runner } - { name: aarch64 Linux, target: aarch64-unknown-linux-gnu, runner: arm-runner } # Disabled because musl builds weren't working and we didn't want to investigate. See https://github.com/clockworklabs/SpacetimeDB/pull/2964. # - { name: x86_64 Linux musl, target: x86_64-unknown-linux-musl, runner: bare-metal, container: alpine } From 5a821f2219e986787188784e8b4818fa1e82f9f5 Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:35:36 -0600 Subject: [PATCH 30/38] Make sure to use container as well --- .github/workflows/package.yml | 45 ++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index fe45fa68999..bdd4236a228 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -14,15 +14,42 @@ jobs: fail-fast: false matrix: include: - - { name: x86_64 Linux, target: x86_64-unknown-linux-gnu, runner: spacetimedb-new-runner } - - { name: aarch64 Linux, target: aarch64-unknown-linux-gnu, runner: arm-runner } - # Disabled because musl builds weren't working and we didn't want to investigate. See https://github.com/clockworklabs/SpacetimeDB/pull/2964. - # - { name: x86_64 Linux musl, target: x86_64-unknown-linux-musl, runner: bare-metal, container: alpine } - # FIXME: arm musl build. "JavaScript Actions in Alpine containers are only supported on x64 Linux runners" - # - { name: aarch64 Linux musl, target: aarch64-unknown-linux-musl, runner: arm-runner } - - { name: aarch64 macOS, target: aarch64-apple-darwin, runner: macos-latest } - - { name: x86_64 macOS, target: x86_64-apple-darwin, runner: macos-latest } - - { name: x86_64 Windows, target: x86_64-pc-windows-msvc, runner: windows-latest } + - name: x86_64 Linux + target: x86_64-unknown-linux-gnu + runner: spacetimedb-new-runner + container: + image: localhost:5000/spacetimedb-ci:latest + options: > + --privileged + + - name: aarch64 Linux + target: aarch64-unknown-linux-gnu + runner: arm-runner + + # Disabled because musl builds weren't working and we didn't want to investigate. + # See https://github.com/clockworklabs/SpacetimeDB/pull/2964. + # - name: x86_64 Linux musl + # target: x86_64-unknown-linux-musl + # runner: bare-metal + # container: alpine + + # FIXME: arm musl build. + # "JavaScript Actions in Alpine containers are only supported on x64 Linux runners" + # - name: aarch64 Linux musl + # target: aarch64-unknown-linux-musl + # runner: arm-runner + + - name: aarch64 macOS + target: aarch64-apple-darwin + runner: macos-latest + + - name: x86_64 macOS + target: x86_64-apple-darwin + runner: macos-latest + + - name: x86_64 Windows + target: x86_64-pc-windows-msvc + runner: windows-latest name: Build CLI for ${{ matrix.name }} runs-on: ${{ matrix.runner }} From d16c20eee685e362dd4b0eb6a6a5a2d5c09c1363 Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:38:25 -0600 Subject: [PATCH 31/38] Possible fix --- .github/workflows/package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index bdd4236a228..2db8a522e82 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -19,7 +19,7 @@ jobs: runner: spacetimedb-new-runner container: image: localhost:5000/spacetimedb-ci:latest - options: > + options: >- --privileged - name: aarch64 Linux From e26e7fee502eb84c9d677def7052508c6d219cd6 Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:50:25 -0600 Subject: [PATCH 32/38] Revert other variants --- .github/workflows/package.yml | 37 ++++++++--------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 2db8a522e82..e9ccf1d8bec 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -21,35 +21,14 @@ jobs: image: localhost:5000/spacetimedb-ci:latest options: >- --privileged - - - name: aarch64 Linux - target: aarch64-unknown-linux-gnu - runner: arm-runner - - # Disabled because musl builds weren't working and we didn't want to investigate. - # See https://github.com/clockworklabs/SpacetimeDB/pull/2964. - # - name: x86_64 Linux musl - # target: x86_64-unknown-linux-musl - # runner: bare-metal - # container: alpine - - # FIXME: arm musl build. - # "JavaScript Actions in Alpine containers are only supported on x64 Linux runners" - # - name: aarch64 Linux musl - # target: aarch64-unknown-linux-musl - # runner: arm-runner - - - name: aarch64 macOS - target: aarch64-apple-darwin - runner: macos-latest - - - name: x86_64 macOS - target: x86_64-apple-darwin - runner: macos-latest - - - name: x86_64 Windows - target: x86_64-pc-windows-msvc - runner: windows-latest + - { name: aarch64 Linux, target: aarch64-unknown-linux-gnu, runner: arm-runner } + # Disabled because musl builds weren't working and we didn't want to investigate. See https://github.com/clockworklabs/SpacetimeDB/pull/2964. + # - { name: x86_64 Linux musl, target: x86_64-unknown-linux-musl, runner: bare-metal, container: alpine } + # FIXME: arm musl build. "JavaScript Actions in Alpine containers are only supported on x64 Linux runners" + # - { name: aarch64 Linux musl, target: aarch64-unknown-linux-musl, runner: arm-runner } + - { name: aarch64 macOS, target: aarch64-apple-darwin, runner: macos-latest } + - { name: x86_64 macOS, target: x86_64-apple-darwin, runner: macos-latest } + - { name: x86_64 Windows, target: x86_64-pc-windows-msvc, runner: windows-latest } name: Build CLI for ${{ matrix.name }} runs-on: ${{ matrix.runner }} From 2c386907f1f30b8367032e2a2d705b9cae0de1e8 Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Wed, 12 Nov 2025 08:19:42 -0600 Subject: [PATCH 33/38] Add Unity SDK release template directory (release~) --- sdks/csharp/release~/.gitignore | 11 +++ .../release~/spacetimedb.bsatn.runtime.meta | 8 +++ .../unversioned.meta | 8 +++ .../unversioned/analyzers.meta | 8 +++ .../unversioned/analyzers/dotnet.meta | 8 +++ .../unversioned/analyzers/dotnet/cs.meta | 8 +++ .../cs/SpacetimeDB.BSATN.Codegen.dll.meta | 71 +++++++++++++++++++ .../unversioned/lib.meta | 8 +++ .../unversioned/lib/netstandard2.1.meta | 8 +++ .../SpacetimeDB.BSATN.Runtime.dll.meta | 33 +++++++++ 10 files changed, 171 insertions(+) create mode 100644 sdks/csharp/release~/.gitignore create mode 100644 sdks/csharp/release~/spacetimedb.bsatn.runtime.meta create mode 100644 sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned.meta create mode 100644 sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers.meta create mode 100644 sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet.meta create mode 100644 sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs.meta create mode 100644 sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta create mode 100644 sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib.meta create mode 100644 sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1.meta create mode 100644 sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta diff --git a/sdks/csharp/release~/.gitignore b/sdks/csharp/release~/.gitignore new file mode 100644 index 00000000000..7d9dea737b9 --- /dev/null +++ b/sdks/csharp/release~/.gitignore @@ -0,0 +1,11 @@ +# Ignore most of NuGet package structure, except DLLs which are required by Unity. +/*/*/* + +!/*/*/analyzers +!/*/*/analyzers.meta + +!/*/*/lib +!/*/*/lib.meta + +# Ignore XML documentation metadata from packages too. +*.xml diff --git a/sdks/csharp/release~/spacetimedb.bsatn.runtime.meta b/sdks/csharp/release~/spacetimedb.bsatn.runtime.meta new file mode 100644 index 00000000000..6dc8b62571c --- /dev/null +++ b/sdks/csharp/release~/spacetimedb.bsatn.runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8865417631feca343997ae22b3320e75 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned.meta b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned.meta new file mode 100644 index 00000000000..a0714721e1a --- /dev/null +++ b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ae5666f23a6d73c43b030a1b9ba5916b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers.meta b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers.meta new file mode 100644 index 00000000000..aff342ea1f5 --- /dev/null +++ b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f112b25d371a72548b6b4959e104c4c3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet.meta b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet.meta new file mode 100644 index 00000000000..f2031f11a56 --- /dev/null +++ b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8554a9e409be36a42bdb316f4677fabf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs.meta b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs.meta new file mode 100644 index 00000000000..a387186fa76 --- /dev/null +++ b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a24e03405bfe85e4996b2524189ac24a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta new file mode 100644 index 00000000000..fcd3478e3fb --- /dev/null +++ b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta @@ -0,0 +1,71 @@ +fileFormatVersion: 2 +guid: 31ff25d2b5723480e873b2b8f46bba86 +labels: +- RoslynAnalyzer +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib.meta b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib.meta new file mode 100644 index 00000000000..3050ce8ab83 --- /dev/null +++ b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dc6d364d8264f4d43847a2f962630f96 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1.meta b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1.meta new file mode 100644 index 00000000000..56e591b6926 --- /dev/null +++ b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b964bd4b6540b3c4e90d73d46afd8fc2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta new file mode 100644 index 00000000000..de2769892f6 --- /dev/null +++ b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8ab8f6f35b91340e6b1269b6448e3bf0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: From 4e510d2c0895be522e6df399e67be52bb1576f15 Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:50:07 -0600 Subject: [PATCH 34/38] Fix release~ .gitignore to allow .meta files at all directory levels --- sdks/csharp/release~/.gitignore | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/sdks/csharp/release~/.gitignore b/sdks/csharp/release~/.gitignore index 7d9dea737b9..39026c1f32d 100644 --- a/sdks/csharp/release~/.gitignore +++ b/sdks/csharp/release~/.gitignore @@ -1,11 +1,5 @@ -# Ignore most of NuGet package structure, except DLLs which are required by Unity. -/*/*/* +# Ignore DLLs - these will be populated by dotnet restore +/*/*/*/*/*.dll -!/*/*/analyzers -!/*/*/analyzers.meta - -!/*/*/lib -!/*/*/lib.meta - -# Ignore XML documentation metadata from packages too. +# Ignore XML documentation *.xml From 9f670503f842b157fb760878631a3c6bd8431358 Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Wed, 12 Nov 2025 10:19:17 -0600 Subject: [PATCH 35/38] Remove restrictive patterns from packages/.gitignore to track all .meta files --- sdks/csharp/packages/.gitignore | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/sdks/csharp/packages/.gitignore b/sdks/csharp/packages/.gitignore index 7d9dea737b9..e69de29bb2d 100644 --- a/sdks/csharp/packages/.gitignore +++ b/sdks/csharp/packages/.gitignore @@ -1,11 +0,0 @@ -# Ignore most of NuGet package structure, except DLLs which are required by Unity. -/*/*/* - -!/*/*/analyzers -!/*/*/analyzers.meta - -!/*/*/lib -!/*/*/lib.meta - -# Ignore XML documentation metadata from packages too. -*.xml From 1d5ee85ab8a6c3e2e09e652b64df6fa348b5907d Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Wed, 12 Nov 2025 10:21:37 -0600 Subject: [PATCH 36/38] Remove empty packages/.gitignore file --- sdks/csharp/packages/.gitignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 sdks/csharp/packages/.gitignore diff --git a/sdks/csharp/packages/.gitignore b/sdks/csharp/packages/.gitignore deleted file mode 100644 index e69de29bb2d..00000000000 From 909a8fca177aed0088349baf0d2782914033e9d3 Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:04:28 -0600 Subject: [PATCH 37/38] Just gitignore things we don't have meta files for --- .../spacetimedb.bsatn.runtime/unversioned/.gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/.gitignore diff --git a/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/.gitignore b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/.gitignore new file mode 100644 index 00000000000..fa4f54404d1 --- /dev/null +++ b/sdks/csharp/release~/spacetimedb.bsatn.runtime/unversioned/.gitignore @@ -0,0 +1,8 @@ +.nupkg.metadata +.signature.p7s +LICENSE +README.md +logo.png +spacetimedb.bsatn.runtime.*.nupkg* +spacetimedb.bsatn.runtime.nuspec +**/*.xml From 3a6689f1d2dec01cd4dee5c45727bc1432181957 Mon Sep 17 00:00:00 2001 From: John Detter <4099508+jdetter@users.noreply.github.com> Date: Wed, 26 Nov 2025 20:09:14 -0600 Subject: [PATCH 38/38] Update typescript package size limits --- crates/bindings-typescript/package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bindings-typescript/package.json b/crates/bindings-typescript/package.json index 88886aa933f..248ab396608 100644 --- a/crates/bindings-typescript/package.json +++ b/crates/bindings-typescript/package.json @@ -82,7 +82,7 @@ "name": "cjs (brotli)", "path": "dist/index.cjs", "brotli": true, - "limit": "26 kB" + "limit": "27 kB" }, { "name": "esm (brotli)", @@ -94,31 +94,31 @@ "name": "esm (gzip)", "path": "dist/index.mjs", "gzip": true, - "limit": "30 kB" + "limit": "31 kB" }, { "name": "esm (uncompressed)", "path": "dist/index.mjs", "brotli": false, - "limit": "160 kB" + "limit": "164 kB" }, { "name": "esm min (brotli)", "path": "dist/min/index.browser.mjs", "brotli": true, - "limit": "14 kB" + "limit": "15 kB" }, { "name": "esm min (gzip)", "path": "dist/min/index.browser.mjs", "gzip": true, - "limit": "16 kB" + "limit": "17 kB" }, { "name": "esm min (uncompressed)", "path": "dist/min/index.browser.mjs", "brotli": false, - "limit": "65 kB" + "limit": "67 kB" }, { "name": "react esm min (brotli)",