89 Commits

Author SHA1 Message Date
Stable Genius
31b9af3da3
fix(csharp-codegen): escape C# reserved keywords in generated identifiers (#4535)
## Summary

This PR fixes an issue where C# reserved keywords (like `params`,
`class`, `event`, etc.) used as field or parameter names in SpacetimeDB
types would cause compilation errors in the generated code.

## Problem

When a user defines a table or reducer with a field named using a C#
reserved keyword:

```csharp
[SpacetimeDB.Table]
public partial struct MyTable
{
    public int @params;  // User escapes it in their code
    public string @class;
}
```

The codegen would generate invalid C# like:

```csharp
// Generated code (broken)
public int params;  // Error: keyword used as identifier
public void Read(BinaryReader reader) {
    params = ...;  // Error
}
```

## Solution

1. Added an `Identifier` property to `MemberDeclaration` in the codegen
that automatically detects C# reserved keywords using
`SyntaxFacts.GetKeywordKind()` and prefixes them with `@` when needed.

2. Updated all code generation sites to use `Identifier` instead of
`Name` when generating:
   - Field declarations
   - Property accessors
   - Constructor parameters
   - BSATN serialization code
   - Index accessors
   - Reducer/procedure parameters

3. Added a regression test that verifies tables, reducers, and
procedures with keyword field names compile successfully.

## Test Plan

- Added `CSharpKeywordIdentifiersAreEscapedInGeneratedCode` test that
creates a table with `@class` and `@params` fields, plus a reducer and
procedure with keyword parameters
- Existing tests continue to pass (verified locally with
`FormerlyForbiddenFieldNames` fixture which already tests edge cases
like `Read`, `Write`, `GetAlgebraicType`)

Fixes #4529

---------

Co-authored-by: Stable Genius <259448942+stablegenius49@users.noreply.github.com>
Co-authored-by: Ryan <r.ekhoff@clockworklabs.io>
2026-03-19 21:54:50 +00:00
joshua-spacetime
3fe50e72d0
[Rust] Extend view table accessors with count (#4638)
# Description of Changes

Adds `count` to rust's procedural view API.

This API already existed in C# and typescript. This patch just adds
tests for those module languages.

We were already tracking reads for `count`, and so all this patch does
is add the new rust module bindings.

# API and ABI breaking changes

None

# Expected complexity level and risk

1

# Testing

Smoketests for rust, C#, and typescript.
2026-03-16 13:03:50 +00:00
joshua-spacetime
a39b81b087
[C#] Primary keys for query builder views (#4626)
# Description of Changes

Same change set as
https://github.com/clockworklabs/SpacetimeDB/pull/4614, just targeting
master.

The return type of a query builder view is now a special SATS product
type `{ __query__: T }`. A view with this return type now has a primary
key if `T` has a primary key. This means that client codegen will
generate an `OnUpdate` callback for such views. It will also generate
query builder index bindings for the primary key column.

# API and ABI breaking changes

None. Old modules with query builder views still work, they just don't
have primary keys.

# Expected complexity level and risk

2

# Testing

Added equivalent tests to the ones that were added in #4573 and #4572.
2026-03-13 00:57:50 +00:00
joshua-spacetime
c5743cfc8d
Add implicit query builder conversions from bool to BoolExpr (#4547)
# Description of Changes

Adds implicit query builder conversions from `bool` to `BoolExpr` so
that you can write:
```rust
ctx.from.user().r#where(|u| u.online)
```

instead of
```rust
ctx.from.user().r#where(|u| u.online.eq(true))
```

Also removes `NullableCol` and `NullableIxCol` types from C# query
builder.

# API and ABI breaking changes

None

# Expected complexity level and risk

1

# Testing

Unit and smoketests
2026-03-05 07:14:08 +00:00
joshua-spacetime
89fba42165
Make accessor required for table-level index defs in C# (#4541)
# Description of Changes

Make `Accessor` a required argument for table-level index defs in C# to
align with rust and typescript. The same change was done for typescript
in https://github.com/clockworklabs/SpacetimeDB/pull/4525.

# API and ABI breaking changes

Technically breaks the module api, although I believe this is the
behavior that is outlined in the spec, and so the current behavior
should really be considered a bug.

# Expected complexity level and risk

1

# Testing

Added negative compile tests
2026-03-03 23:06:19 +00:00
Ryan
d87e49487b
[C#] Improve error messages for Views (#4435)
This is the implementation of a fix for #4425

# Description of Changes

* Clarified C# generator diagnostics for view return types:
1. Updated the comments around `IQuery<T>` handling to describe the
return value as `T?`, matching C# semantics.
2. Adjusted the validation comment to say views must return `List<T>` or
nullable `T` instead of “Vec/Option”.
* Synced the diagnostics fixture comments with the new terminology so
STDB0024 examples talk about `List<T>`/`T?`.
* Checked current documentation for anything C# related to “Vec/Option”
and confirmed everything now references `List<T>`/`T?`.
* Regenerated/verified tests and snapshots.

# API and ABI breaking changes

None

# Expected complexity level and risk

1 - Changes are documentation and diagnostic-comment only.

# Testing

- [X] CLI rebuilt, local `dotnet test` pass and error output tests
validated.

---------

Signed-off-by: Ryan <r.ekhoff@clockworklabs.io>
Co-authored-by: joshua-spacetime <josh@clockworklabs.io>
2026-02-27 01:46:39 +00:00
Ryan
85095cfa85
[C#] Removes Query<TRow> and .Build() in favor of IQuery<TRow> (#4393)
This fixes an issue with the C# implementation of Query Builder
requiring `Build()` to return a query, and the return type being
`Query<TRow>` rather than `IQuery<TRow>`.

# Description of Changes

1. Runtime/query-builder
* Removed the concrete `Query<TRow>` carrier type and every `.Build()`.
Query builder shapes now expose only `IQuery<TRow>` plus `ToSql()`.
* Ensured all builder entry points (tables, joins, filters) continue to
return `IQuery<TRow>`.
2. Source generator + bindings
* Updated `ViewDeclaration` analysis to treat any return type
implementing `SpacetimeDB.IQuery<TRow>` as a SQL view.
* Dispatcher generation now emits
`ViewResultHeader.RawSql(returnValue.ToSql())` . This eliminates a
`Query<TRow>` special-case.
3. Tests, fixtures, regression module
* Converted the C# query-builder unit tests, codegen fixtures, and
regression-test server views to call `ToSql()`/return `IQuery<TRow>`.
* Added coverage proving `RightSemiJoin` (and friends) still satisfy
`IQuery<TRow>`.
4. CLI templates & generated bindings
* Regenerated/edited C# template bindings so
`SubscriptionBuilder.AddQuery` accepts `Func<QueryBuilder,
IQuery<TRow>>` and captures SQL via `ToSql()`.

# API and ABI breaking changes

While technically API breaking, this actually brings the API closer to
the intended design.
* `Query<TRow>` has been removed from the public surface area; any
previous references (including `.Build()` and `.Sql`) must be replaced
with the builder instance itself plus `.ToSql()`.
* View methods must now return an `IQuery<TRow>` (or any custom type
implementing it) when producing SQL for the host.
* Generated C# client bindings now expect typed subscription callbacks
to produce `IQuery<TRow>`, aligning the client SDK with the new runtime
contract.

# Expected complexity level and risk

3 - Medium: Touches runtime, codegen, fixtures, and templates. Risk is
mitigated by parity with Rust semantics and comprehensive test updates,
but downstream modules must recompile to adopt the new interface.

# Testing

- [X] Built CLI and ran regression tests locally with removed `.Build()`
- [X] Ran `dotnet test .\sdks\csharp\tests~\tests.csproj -c Release`
with all tests passing
- [X] Ran `dotnet test
.\crates\bindings-csharp\Codegen.Tests\Codegen.Tests.csproj -c Release`
with all tests passing
2026-02-23 15:34:51 +00:00
clockwork-labs-bot
dd2c95f113
Move CaseConversionPolicy to public SpacetimeDB namespace (#4382)
Move `CaseConversionPolicy` from `SpacetimeDB.Internal` to the public
`SpacetimeDB` namespace so module authors can write:

```csharp
[SpacetimeDB.Settings]
public const CaseConversionPolicy CASE_CONVERSION_POLICY = CaseConversionPolicy.SnakeCase;
```

instead of the verbose:

```csharp
public const SpacetimeDB.Internal.CaseConversionPolicy CASE_CONVERSION_POLICY = SpacetimeDB.Internal.CaseConversionPolicy.SnakeCase;
```

### Changes
- Move enum definition from `SpacetimeDB.Internal` to `SpacetimeDB`
namespace in autogen
- Fully qualify all `Internal` references to
`SpacetimeDB.CaseConversionPolicy`
- Codegen source generator accepts both
`SpacetimeDB.CaseConversionPolicy` and
`SpacetimeDB.Internal.CaseConversionPolicy` for backward compatibility
- Updated test fixture and verified snapshots
- All 4 codegen tests pass

---------

Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
2026-02-20 22:21:46 +00:00
Shubham Mishra
e2f8a60759
Case conversion (#4263)
# Description of Changes

Update the Default casing policy to `snake_case` for `RawModuleDefV10`.

Messy PR contains changes at different places, so that CI can pass:

Here are the main changes as follows:
- `bindings-macro` & `bindings` crate: `name` macro in Indexes for
canonical name and supply it to `RawModuleDefV10` via `ExplicitNames`.
- `bindings-typescript`: 
- Changes has been reviewed through this PR -
https://github.com/clockworklabs/SpacetimeDB/pull/4308.
   
- `binding-csharp`: a single line change to pass `sourceName` of index
instead of null.
- `codegen`:
  
- Changes has been merged from branch -
https://github.com/clockworklabs/SpacetimeDB/pull/4337.
  
- Except a fix in rust codegen to use canonical name in Query buillder
instead of accessor.
  
- `lib/db/raw_def`: Extends `RawDefModuleV10` structure to support case
conversion.
  
- `schema` crate:
- `validate/v9` - Nothing itself should change or changes in v9
validation logic but the file contains a `CoreValidator` which is shared
with `validate/v10`. No test have t be updated to `validate/v9` which
ensures we aren't regressing it.
- `validate/v10`: This is the main meat, look at the new tests added in
bottom to understand what it does.
     
   -  Rest of the files are either test updates or module bindings. 
     
    ## Testing:
1. Extensive unit tests have been added to verify generated `ModuleDef`
is correct.
2. I have done some e2e testing to verify rust codegen with rust and
typescript modules.
3. I would have like to do more testing for other codegens , I am
continue doing .

I have removed `sql.py` smoketest, as that seems to be already migated
in new framework and was headache to update.

## Expected complexity level and risk
4, It could have side-effect which aren't easily visible.


 
 
 
 
 
 -  -  -

---------

Signed-off-by: Shubham Mishra <shivam828787@gmail.com>
Co-authored-by: rekhoff <r.ekhoff@clockworklabs.io>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: clockwork-labs-bot <bot@clockworklabs.com>
Co-authored-by: joshua-spacetime <josh@clockworklabs.io>
Co-authored-by: Noa <coolreader18@gmail.com>
Co-authored-by: = <cloutiertyler@gmail.com>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@clockworklabs.io>
Co-authored-by: Jason Larabie <jason@clockworklabs.io>
Co-authored-by: Phoebe Goldman <phoebe@clockworklabs.io>
2026-02-20 10:44:29 +00:00
Ryan
d965569397
[C#] Cononical Names and Casing Settings in ModuleDef (#4368)
This is the implementation of the "Nice to Have in 2.0" items from #4295

# Description of Changes
* Propagate the module’s case-conversion policy and each explicit
canonical name (tables, reducers, procedures, views, indexes) into
`RawModuleDefV10`, so runtime consumers receive the same metadata
emitted by the Rust toolchain.
* Implement support for `[SpacetimeDB.Settings]` + explicit `Name = ...`
attribute overrides in the C# module bindings generator.
* Adds an `explicitnames` fixture to the Codegen Test suite to cover the
generated registration calls, ensuring accessor vs. canonical names stay
in sync.
# API and ABI breaking changes
None. Generator + runtime internals only; no public surface changes.

# Expected complexity level and risk
2 – localized to the C# codegen/runtime plumbing, validated by focused
tests.

# Testing
- [X] Compiled CLI changes locally and ran C# regression tests.
- [X] Ran `dotnet test
crates/bindings-csharp/Codegen.Tests/Codegen.Tests.csproj -c Release` to
verify code tests pass
2026-02-20 06:31:07 +00:00
Ryan
e03b7d7516
Updated C# Name attribute to Accessor (#4306)
# Description of Changes

Implementation of #4295

Convert existing `Name` attribute to `Accessor` to support new Canonical
Case conversation of 2.0

# API and ABI breaking changes

Yes, in C# modules, we no longer use the attribute name `Name`, it
should now be `Accessor`

# Expected complexity level and risk

1

# Testing

- [X] Build and tested locally
- [X] Ran regression tests locally
2026-02-17 01:16:16 +00:00
Ryan
bba6f89e5f
[C#] Adding RawModuleDefV10 to C# Module Bindings without Event Tables (#4288)
# Description of Changes
This is the implementation of #4255
This is a rebuild of #4262 after a rebase throughly messed up that PR's
changelog, this time under `master` branch.
* Refactored `Module` so all V10 builder state and registration helpers
now live on `partial class RawModuleDefV10`. The static `Module` class
simply registers components against that builder and serializes via
`moduleDef.BuildModuleDefinition()`
* Removed the deprecated `__describe_module__` export. Only
`__describe_module_v10__` is emitted now across the runtime, native
shim, generated bindings, and snapshots
* Mirrored the Rust TODO that future V10 sections will cover Event
tables and Case-conversion policy so we don't lose track of those items

Event Tables will be added once they are finished being implemented.

# API and ABI breaking changes
Modules built with these bindings now expose only
`__describe_module_v10__`
The legacy `__describe_module__` symbol is gone from both managed and
native layers. Hosts expecting the old export must switch to the V10
entry point (which is already the canonical ABI for 2.0).

# Expected complexity level and risk
2 - Low. The refactor keeps the previous builder logic but relocates it,
and the export removal matches the already-supported V10 host path.

# Testing
- [X] Successfully built CLI and DLLs without errors.
- [X] Ran `dotnet build crates/bindings-csharp/Runtime/Runtime.csproj`
without errors.
- [X] Ran `dotnet test
crates/bindings-csharp/Codegen.Tests/Codegen.Tests.csproj` without
errors.
- [X] Ran `bash run-regression-tests.sh` without errors.

---------

Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
2026-02-13 07:49:44 +00:00
Tyler Cloutier
57b7ef5e79
Limit update() to only work on primary keys (#4279)
## Summary
- Restricts `update()` across all three server-side SDKs (Rust, C#,
TypeScript) to only work on primary key columns, not all unique columns
- Calling `update()` on a non-PK unique column is semantically a
delete+insert — clients won't see it as a row update unless the primary
key stays the same
- Fresh re-implementation of #1862, extended to cover C# and TypeScript
bindings

### Design principle

Primary key is a constraint on columns, not a property of indexes. An
index is unique or not unique — whether `update()` is available is
derived from the column's attributes at the point of use, not stored on
the index.

This differs from #1862 which introduced a `Uniqueness` enum (`No |
Unique | PrimaryKey`) on the index itself. Instead, we keep `is_unique:
bool` and check the column metadata where needed.

### Rust changes
- Add `PrimaryKey` marker trait and `where Col: PrimaryKey` bound on
`UniqueColumn::update()`
- `marker_type()` receives `primary_key_column` and checks the column
directly — no `is_pk` field on the index
- `sdk-test` unique tables use `update_non_pk_by` (delete+insert)
instead of `update_by`
- Benchmarks that used `update()` on `#[unique]` columns changed to
either `#[primary_key]` or delete+insert

### C# changes
- Only emit `Update()` on `UniqueIndex` when the column has `PrimaryKey`
attrs
- Update `unique_*` test reducers in `sdk-test-cs` to use
`Delete()`+`Insert()` instead

### TypeScript changes
- `UniqueIndex` now has only `find` + `delete` (no `update`)
- `AllColumnsPrimaryKey` type-level helper checks column metadata from
the `TableDef`
- `Index` routing conditionally intersects `{ update() }` when columns
are PK
- Runtime only attaches `update` method for PK unique indexes
- `sdk-test-ts` unique tables use delete+insert instead of update
- Type-level test in `schema.test-d.ts` verifies `id.update()` compiles
(PK) and `name2.update` errors (non-PK unique)

## Test plan
- [x] C# codegen tests pass (`dotnet test
crates/bindings-csharp/Codegen.Tests`)
- [x] Rust SDK test module compiles
- [x] Benchmarks compile
- [x] TypeScript type checks pass (`npx tsc --noEmit`)
- [ ] CI passes

Closes #1862
2026-02-13 00:56:54 +00:00
Ryan
759f5220b9
[C#] Module bindings for Typed Query Builder (#4159)
# Description of Changes
This is the implementation of the Typed Query Builder for C# modules
outlined in #3750.
This requires the changes from #4146 to be in place before it can
properly function.

This aligns the C# typed query builder SQL formatter with the Rust
implementation:
* Supports `And/Or` with same grouping styles as Rust.
* All comparison operators (`Eq/Neq/Lt/…`) wrap expressions in
parentheses so chained predicates produce byte-for-byte identical SQL.

# API and ABI breaking changes
Not breaking. Existing modules keep producing the same SQL semantics;
only redundant parentheses were added to match the Rust formatter.

# Expected complexity level and risk
2 — localized string-format updates plus parity harness tweaks. Low
functional risk; largest concern is ensuring every comparison path
gained parentheses.

# Testing
- [X] Tested against a local test harness validating output from these
changes match current Rust behavior.

---------

Signed-off-by: Ryan <r.ekhoff@clockworklabs.io>
Co-authored-by: joshua-spacetime <josh@clockworklabs.io>
2026-02-11 01:38:30 +00:00
Ryan
201970e9ca
[C#] Update module bindings to use new View ABI (#4146)
# Description of Changes

This change implements the module bindings changes to Views that where
updated with core in #3685 and the Rust module bindings implementation
in #3819.
Updates the C# module bindings to use new header-first view ABI
(`ViewResultHeader`) and updates the return codes for the
`__call_view__` and `__call_view_anon__` module exports. This is a
prerequsite for `Query` builder support being added to C# modules.

# API and ABI breaking changes

Not breaking. Existing modules will continue to use the old ABI. New
modules will use the new ABI. However previous host versions will not
support modules built using this version of the bindings.

# Expected complexity level and risk

2

# Testing

This is an internal refactor. All existing tests should continue to
pass. All existing tests should continue to pass. The only tests that
needed updating were the C# codegen snapshot tests (`Codegen.Tests`)
because the generated view dispatcher bodies changed (they now prefix
`ViewResultHeader.RowData` before the existing row payload).
2026-01-31 01:08:12 +00:00
Ryan
6f91cfd524
Enable RefOption returns from Views to support Views returning a single class (#3964)
# Description of Changes

This PR adds a regression test covering nullable reference-type view
returns in C# modules (e.g. Account? where Account is a class), as
reported in
[#3962](https://github.com/clockworklabs/SpacetimeDB/issues/3962).

```csharp
public static Account? MyAccount(ViewContext ctx)
```
* Updated the C# regression-test server module to include a
reference-type table and views that exercise the RefOption path:
* A new public reference-type table row:
```csharp
[SpacetimeDB.Table(Name = "account", Public = true)]
public partial class Account { ... }
```
* A public at-most-one view that returns a nullable reference type
(Account?) via Find(...):
```csharp
[SpacetimeDB.View(Name = "my_account", Public = true)]
public static Account? MyAccount(ViewContext ctx)
{
    return ctx.Db.account.Identity.Find(ctx.Sender) as Account;
}
```
* A second public view that returns null to ensure the “empty result”
case is exercised:
```csharp
[SpacetimeDB.View(Name = "my_account_missing", Public = true)]
public static Account? MyAccountMissing(ViewContext ctx) => null;
```
* Updated ClientConnected to ensure an Account row exists for the
connecting identity so the “one row” case is deterministic.
* Updated the C# regression-test client to:
  * Subscribe to the new views:
    * `SELECT * FROM my_account`
    * `SELECT * FROM my_account_missing`
  * Assert correct semantics for nullable reference-type view returns:
    * MyAccount.Count == 1
    * MyAccountMissing.Count == 0
* Updated the regression-test server project to use local C#
runtime/codegen project references so the regression module exercises
the in-repo generator/runtime behavior (instead of the published
SpacetimeDB.Runtime package).

# API and ABI breaking changes

None.
* No changes to public module schema/wire format semantics beyond adding
regression-test-only tables/views.
* No behavior changes outside the C# regression test module + harness.

# Expected complexity level and risk

2 - Low

* Changes are isolated to regression tests and project wiring.
* The scenario specifically guards the nullable reference-type
“Option-like view return” path against regressions.

# Testing

<!-- Describe any testing you've done, and any testing you'd like your
reviewers to do,
so that you're confident that all the changes work as expected! -->

- [X] Ran C# regression tests with no failures in new View tests

Signed-off-by: Ryan <r.ekhoff@clockworklabs.io>
2026-01-15 00:21:08 +00:00
Ryan
f9ccf4c1c4
Move Bound struct out of SpacetimeDB.Internal to SpacetimeDB and Local out of SpacetiemDB.Runtime (#3996)
# Description of Changes
This PR fixes a C# SDK regression where using `Bound` in index filters
could trigger an ambiguous reference compiler error for Local after
upgrading to `v1.11.2`, as reported in
[#3995](https://github.com/clockworklabs/SpacetimeDB/issues/3995).
It also fixes a related warning-spam regression (`CS0436`) where user
projects could see `Local` type conflicts between generated module code
and the `SpacetimeDB.Runtime` assembly.

* Introduced a public `SpacetimeDB.Bound` type so users no longer need
to import `SpacetimeDB.Internal` to use bounds in index filters.
* Kept `SpacetimeDB.Internal.Bound` for compatibility, but added
implicit conversions between `SpacetimeDB.Internal.Bound` and
`SpacetimeDB.Bound`.
* Updated the C# code generator to emit fully-qualified
`global::SpacetimeDB.Bound` in generated index filter signatures,
avoiding `SpacetimeDB.Internal` in public-facing APIs and preventing
name collisions (e.g., `Local`).
* Updated internal runtime bounds helpers (`BTreeIndexBounds<...>`) to
explicitly use `SpacetimeDB.Bound` when constructing range-scan
arguments.
* Updated Codegen snapshot fixtures to match the new generated output
(type name + formatting).
* Fixed codegen output for `ITableView` `static abstract` member
implementations to generate `public static` methods (required for the
generated code to compile).
It also fixes a related warning-spam regression (CS0436) where user
projects could see Local type conflicts between generated module code
and the SpacetimeDB.Runtime assembly.

Additional fix (related to the `Local` reports):
* Removed the runtime assembly’s ownership of `SpacetimeDB.Local`
(introduced more recently than the generated module `Local`) to prevent
`CS0436` duplicate-type warnings. Basically, the runtime’s concrete
`Local`/`ProcedureTxContext` helpers were renamed and made internal so
the code generator remains the sole owner of module-level
`SpacetimeDB.Local`.

Regression coverage:
* Added generator regression assertions to ensure generated code does
not reference `global::SpacetimeDB.Internal.Bound<...>` and does
reference `global::SpacetimeDB.Bound<...>`.
* Added a runtime API regression assertion that `SpacetimeDB.Bound`
exists and is public in the runtime reference.
* Added a regression assertion that `SpacetimeDB.Runtime` does not
define codegen-owned types (e.g. `SpacetimeDB.Local`,
`ProcedureContext`, etc.) to prevent future `CS0436` conflicts.
* Added a “simulated downstream user file” compile check ensuring no
`CS0436` diagnostics occur when user code references
`SpacetimeDB.Local`.
# API and ABI breaking changes
None.
* No schema or wire-format changes.
* The changes are limited to C# type exposure / naming and codegen
output.
* `SpacetimeDB.Internal.Bound` remains usable via implicit conversions
(backwards compatible for existing code).
# Expected complexity level and risk
2 - Low
* Changes are isolated to C# runtime type exposure, codegen type
references, and snapshot updates.
* No runtime behavior changes to index scan encoding/decoding; only
avoids requiring SpacetimeDB.Internal in user code.
# Testing
- [X] Ran:`dotnet test
crates/bindings-csharp/Codegen.Tests/Codegen.Tests.csproj`
- [X] Ran regression tests locally.
2026-01-14 22:42:30 +00:00
Mario Montoya
82d5a4f6c0
Implement SpacetimeType for Result<T, E> (#3790)
# Description of Changes

Closes #3673 

*NOTE*: C++ part will be in another PR

# Expected complexity level and risk

2

Adding a new type touch everywhere

# Testing

- [x] Adding smoke and unit test

---------

Signed-off-by: Ryan <r.ekhoff@clockworklabs.io>
Co-authored-by: rekhoff <r.ekhoff@clockworklabs.io>
Co-authored-by: Phoebe Goldman <phoebe@goldman-tribe.org>
Co-authored-by: Jason Larabie <jason@clockworklabs.io>
2026-01-08 15:50:18 +00:00
Mario Montoya
8fb0bcf922
Add UUID built-in convenience type to SpacetimeDB (#3538)
# Description of Changes

Closes
[#3290](https://github.com/clockworklabs/SpacetimeDB/issues/3290).

Adds a new "special" type to SATS, `UUID`, which is represented as the
product `{ __uuid__: u128 }`. Adds versions of this type to all of our
various languages' module bindings libraries and client SDKs, and
updates codegen to recognize it and output references to those named
library types. Adds methods for creating new UUIDs according to the V4
(all random) and V7 (timestamp, monotonic counter and random)
specifications.

# API and ABI breaking changes

We add a new type 

# Expected complexity level and risk

2

it impacts all over the code

# Testing

- [x] Extends the Rust and Unreal SDK tests, and the associated
`module-test` modules in Rust, C# and TypeScript, with uses of UUIDs.
- [x] Extends the C# SDK regression tests with uses of UUIDs.
- [x] Extends the TypeScript test suite with tests with uses of UUIDs.

---------

Signed-off-by: Mario Montoya <mamcx@elmalabarista.com>
Co-authored-by: Phoebe Goldman <phoebe@clockworklabs.io>
Co-authored-by: Jason Larabie <jason@clockworklabs.io>
Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
2026-01-02 17:17:24 +00:00
Ryan
8a0cd87c4f
Adds datastore_index_scan_point_bsatn to C# Runtime (#3909)
# Description of Changes

To resolve #3875, we added exact-match unique index point lookup support
to the C# bindings by introducing and using
`datastore_index_scan_point_bsatn`.

Previously, generated unique index `Find()` was (in at least one
codepath) implemented as:

* A range scan (`datastore_index_scan_range_bsatn`) over a BTree bound,
then
* `SingleOrDefault()` to collapse the results into a single row.
When the scan is empty, `SingleOrDefault()` returns `default(T)`. For
value-type rows this can manifest as a default-initialized row instead
of “missing”, which is what surfaced as “default-ish row” behavior in
views.

Using `datastore_index_scan_point_bsatn` makes the C# implementation
match Rust semantics more closely by performing an exact point lookup
and returning:

* `null` when no rows are found
* the row when exactly one row is found
* (defensively) an error if >1 row is returned (unique index invariant
violation)
Similarly, `datastore_delete_by_index_scan_point_bsatn` was added and
used so deletes-by-unique-key are also exact-match point operations
rather than range deletes.

Runtime updates were made to utilize point scan in `FindSingle(key)` and
in both mutable/read-only unique-index paths.

To keep this non-breaking for existing modules, codegen now detects
whether the table row is a struct or a class and chooses the appropriate
base type:
* Struct rows: `Find()` returns `Row?` (`Nullable<Row>`).
* Class rows: `Find()` returns `Row?` (nullable reference, `null` on
miss).

# API and ABI breaking changes

This change is non-breaking with respect to row type kinds, because
class/record table rows continue to work via
RefUniqueIndex/ReadOnlyRefUniqueIndex while struct rows use
UniqueIndex/ReadOnlyUniqueIndex.

API surface changes:
* Generated `Find()` return type is now nullable (`Row?`) to correctly
represent “missing”.

ABI/runtime:
* Requires the point-scan hostcall import
(`datastore_index_scan_point_bsatn`) to be available; the runtime uses
point-scan for unique lookup (and point delete for unique delete).

# Expected complexity level and risk

Low 2

# Testing

- [X] Local testing: repro module + client validate view and direct
Find() behavior

---------

Signed-off-by: rekhoff <r.ekhoff@clockworklabs.io>
2025-12-20 00:32:37 +00:00
rekhoff
39f01289e5
C# implementation of Transactions for Procedures (#3809)
# Description of Changes

Implements the C# equivalent of #3638

This implement uses inheritance, where abstract base classes (like
`ProcedureContextBase` in `ProcedureContext.cs`) store the core of the
implementation, and then generated wrappers (like `ProcedureContext` in
the generated FFI.cs file) inherit from them.

For error handling, we work like Rust's implementation of `Result<T,E>`
but we require `where E : Exception` because of how exceptions work in
C#. Transaction-level failures come back as a `TxOutcome` and user
errors should follow the `Result<T,E>` pattern. In this implementation,
we have `UnwrapOrThrow()` throws exceptions directly because of C#'s
error handling pattern.

Unlike the Rust implementation's direct `Result` propagation, we are
using an `AbortGuard` pattern (in `ProcedureContext.cs`) for exception
handling, which uses `IDisposable` for automatic cleanup.

Most changes should have fairly similar Rust-equivalents beyond that.
For module authors, the changes here allow for the transation logic to
work like:
```csharp
ctx.TryWithTx<ResultType, Exception>(tx => {
    // transaction logic
    return Result<ResultType, Exception>.Ok(result);
});
```
This change includes a number of tests added to the
`sdks/csharp/examples~/regression-tests/`'s `server` and `client` to
validate the behavior of the changes. `server` changes provide further
usage examples for module authors.

# API and ABI breaking changes

Should not be a breaking change

# Expected complexity level and risk

2

# Testing

- [x] Created Regression Tests that show transitions in procedures
working in various ways, all of which pass.
2025-12-18 18:41:47 +00:00
Cameron Hutchison
62e79fad95
C# Views - Use Name from ViewAttribute instead of Method Name (#3792)
C# Views - Use Name from ViewAttribute instead of Method Name

# Description of Changes

The [documentation for C#
views](https://spacetimedb.com/docs/modules/c-sharp#views) says that
"Views must be declared as Public, **with an explicit Name**, and
[...]". However, the `Name` provided to the `View` attribute is not
being used as the name of the view in the Module or the generated C#
client SDK code. The `ViewDeclaration` actually checks that the `View`
attribute's name is not null or empty, but then proceeds to do nothing
with it.

This PR updates the `ViewDeclaration` to use the `Name` property from
`ViewAttribute`.

For more info - see my bug report in Discord:
https://discord.com/channels/1037340874172014652/1443881580602069043

# API and ABI breaking changes

No breaking change to the API. Though, anyone who has a view name
declared that's different from their method name will have to deal with
that during migration of their modules.

# Expected complexity level and risk

1 - Trivial change

# Testing

I compiled the C# projects under `crates/bindings-csharp`, built the
NuGet packages, and tested them locally with a project using SpacetimeDB
1.10 (CLI and associated packages).

I confirmed that the generated classes now use the value from the `View`
attribute as the `RemoteTableName`. See attached image.

<img width="1429" height="372" alt="Screenshot 2025-11-28 at 3 04 48 PM"
src="https://github.com/user-attachments/assets/1db83c14-b0dc-4bcb-87ac-50e104f4d501"
/>

---------

Co-authored-by: rekhoff <r.ekhoff@clockworklabs.io>
Co-authored-by: joshua-spacetime <josh@clockworklabs.io>
2025-12-02 07:42:25 +00:00
rekhoff
60e4a640e8
C# module bindings for Procedures (#3732)
# Description of Changes

Implements the C# module bindings for Procedures (#3510)

# API and ABI breaking changes

None

# Expected complexity level and risk

2

# Testing

- [X] Locally tested against existing C# Procedures regression test
client (minus the Transaction and HTTP portions, as those are not in
this change).
2025-11-25 16:50:54 +00:00
rekhoff
cb35104c0f
Export __call_view__ in C# (#3691)
# Description of Changes

1. Updates the Replication Tests in
`sdks/csharp/examples~/regression-tests` to include better coverage of
Views
2. Added missing linkage for __call_view__ and __call_view_anon__
3. Updated *ViewDispatcher Invoke to transform BSATN.ValueOption<> into
BSATN.List<>
4. Fixed issues with the indexing of views to match correctly during
__call_view__ and __call_view_anon__

# API and ABI breaking changes

No

# Expected complexity level and risk

2

# Testing

- [x] Running `run-regression-tests.sh` passes.

---------

Signed-off-by: rekhoff <r.ekhoff@clockworklabs.io>
Signed-off-by: Jason Larabie <jason@clockworklabs.io>
Co-authored-by: Jason Larabie <jason@clockworklabs.io>
Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
Co-authored-by: joshua-spacetime <josh@clockworklabs.io>
2025-11-20 17:17:37 +00:00
Jason Larabie
87de672bb3
Fix C# module bindings codegen for namespaces with view dispatcher (#3668)
# Description of Changes

Closes: #3658 
With the work on Views we ran into trouble with the Return Type and
certain C# code layout where the types are defined inside the Module
class. This fixes the bug in our current approach but needs
consideration to possibly be changed entirely.

# API and ABI breaking changes

N/A

# Expected complexity level and risk

1 - Minor change

# Testing

- [x] Retested with the linked code with and without the namespace
- [x] Re-ran regression tests

---------

Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
Co-authored-by: joshua-spacetime <josh@clockworklabs.io>
Co-authored-by: rekhoff <r.ekhoff@clockworklabs.io>
2025-11-19 23:32:21 +00:00
rekhoff
4143c15b08
C# bindings for views (#3585)
# Description of Changes

Updates C# bindings to allow module authors to define `View`s and
`AnonymousView`s using the pattern:
```
[SpacetimeDB.View]
public static ExampleData? GetExampleDataById(ViewContext ctx, uint id)
{
    return ctx.Db.ExampleData.Id.Find(id);
}
	
[SpacetimeDB.View]
public static ExampleData? GetAnonymousExampleDataById(AnonymousViewContext ctx, uint id)
{
    return ctx.Db.ExampleData.Id.Find(id);
}
```
During publishing of a C# module, the views data will be converted into
`RawViewDefV9`

# API and ABI breaking changes

No known breaking changes

# Expected complexity level and risk

2

# Testing

- [X] Locally tested locally adding the above sample pattern to the
`sdks\csharp\examples~\regression-tests\server` test and performing a
`dotnet build` and a `spacetime publish test`, both of which succeeded.

---------

Signed-off-by: Jason Larabie <jason@clockworklabs.io>
Co-authored-by: Jason Larabie <jason@clockworklabs.io>
2025-11-11 12:01:44 -05:00
Jeffrey Dallatezza
9586136db7
Treat a missing aud claim as an empty list (#3542)
# Description of Changes

The `aud` claim is required in the OIDC spec, but the server currently
allows it to be missing, and the spacetime auth tokens we use for the
website don't have an audience.

Previously the module bindings would throw an error if there were no
`aud` claim in a jwt payload (if someone used the `audience` within a
reducer), but this change makes us treat a missing audience as an empty
list.

This also renames the `authCtx` fields to `senderAuth` in the typescript
and csharp module APIs, so they match rust.

# API and ABI breaking changes

This doesn't break any ABIs.

This changes the ReducerContext APIs in typescript and rust, but only by
renaming a field that hasn't been released yet.

# Expected complexity level and risk

1.

# Testing

I've tested accessing the `audience` within a reducer for a token
missing an `aud` claim in Typescript and Rust.
2025-10-31 00:48:40 +00:00
Jeffrey Dallatezza
bf37b2947b
Add bindings for csharp modules to use JWT claims (#3414)
# Description of Changes

This exposes JWT claims for csharp modules, similar to how they are
exposed to rust modules in
https://github.com/clockworklabs/SpacetimeDB/pull/3288.

This adds the new types `AuthCtx` and `JwtClaims`, and adds an `AuthCtx`
to the `ReducerContext`.

`AuthCtx` represents the credentials associated with the request, and
`JwtClaims` represents a jwt token.

One difference from the rust version is that I didn't create helpers to
build an `AuthCtx` from a jwt payload. The reason is that we would need
to be able to compute the identity from the payload claims, which
requires a blake3 hash implementation. The first two c# libraries I
found had issues at runtime
([Blake3](https://www.nuget.org/packages/Blake3) is wrapping a rust
implementation, and
[HashifyNet](https://github.com/Deskasoft/HashifyNET/tree/main/HashifyNet/Algorithms/Blake3)
seems to be broken by our trimming because it uses reflection heavily).
I can look into taking the implementation from `HashifyNet`, since it is
MIT licensed, but I don't think we need to block merging on that.

# API and ABI breaking changes

This adds the new types `AuthCtx` and `JwtClaims`, and adds an `AuthCtx`
to the `ReducerContext`.

This also adds a csharp wrapper for the get_jwt ABI function added in
https://github.com/clockworklabs/SpacetimeDB/pull/3288.

# Expected complexity level and risk

2.

# Testing

This has a very minimal unit test of JwtClaims.

I manually tested using this locally with the csharp quickstart, and I
was able to print jwt tokens inside the module.
2025-10-21 19:17:33 +00:00
rekhoff
a546bc8961
Rekhoff/csharp default field values (#3235)
# Description of Changes

This is the implementation of issue #3191. This adds a Default attribute
to C# module fields.

**Note**: In C#, attribute arguments must be compile-time constants,
which means you can't directly use non-constant expressions like new
expressions, method calls, or dynamic values in attribute constructors.
(Ref:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/attributes#2324-attribute-parameter-types)

For this reason, these default values are limited to primitive types,
enums, and strings.
This includes (shown as `C# Type` (`BSATN type`):

* `bool` (`Bool`)
* `sbyte` (`I8`)
* `byte` (`U8`)
* `short` (`I16`)
* `ushort` (`U16`)
* `int` (`I32`)
* `unit` (`U32`)
* `long` (`I64`)
* `ulong` (`U64`)
* `float` (`F32`)
* `double` (`F64`)
* `enum` (`Enum`)
* `string` (`String`)
* `null` (`RefOption`) <- Nullable type

Because of C# limitations, for nullable and complex data types, such as
a struct, can take use `[Default(null)]` to populate values with null
defaults. This allows things like structs to workaround the non-constant
expressions in attribute constructors limitation by allowing these
complex types to still be able to be added as new column tables.

The `int` type can also be in the form of Hex or Binary literals, such
as `[Default(0x2A)]` or `[Default(0b00101010)]`

Both Decimal (like `[Default(3.14m)]`) and Char (like `[Default('A')]`)
are unsupported types in BSATN and will still return `BSATN0001` errors.

# API and ABI breaking changes

Not API breaking.
This change only adds the `[Default(value)]` attribute logic.
Using the `[Default(value)]` attribute with older versions SpacetimeDB
C# modules will result in an error.

# Expected complexity level and risk

2

# Testing

Local testing of this requires use of CLI changes in #3278 

- [x] Regression test of functionality added.
2025-10-06 16:56:37 +00:00
james gilles
0d2a45b239
Fix fields named 'read' (#2525)
# Description of Changes

Fixes
https://github.com/orgs/clockworklabs/projects/22?pane=issue&itemId=102392974&issue=clockworklabs%7Ccom.clockworklabs.spacetimedbsdk%7C276

by renaming `internal` `static` serializer fields so that they do not
overlap with user-provided names.

Note, however, that some field names still will not work: `ReadFields`,
`WriteFields`, `Equals`, and `GetHashCode`.
This would require a separate fix since the error would happen in a
different place. In this case we would need to change the name of the
generated member to something like `ReadFields_` or `Equals_`, which I'm
not sure is a good idea.

# API and ABI breaking changes

N/A

# Expected complexity level and risk

0

# Testing

SDK tests and `dotnet-verify` tests are passing.

---------

Signed-off-by: rekhoff <r.ekhoff@clockworklabs.io>
Co-authored-by: rekhoff <r.ekhoff@clockworklabs.io>
2025-08-26 21:17:46 +00:00
james gilles
74ad4a14ce
Fix Equals and GetHashCode for types containing Lists and Arrays in C# (#2710) 2025-05-12 18:11:01 +00:00
Mazdak Farrokhzad
c39f7fa64c
Plain enums as index keys with specialized indices (#2506) 2025-04-09 18:52:15 +00:00
joshua-spacetime
aac7894aac
Feature gate [ClientVisibilityFilter] in C# (#2556) 2025-04-08 20:42:00 +00:00
james gilles
13dfb03135
Add [ClientVisibilityFilter] to csharp module library (#2481) 2025-03-28 19:03:13 +00:00
Mazdak Farrokhzad
668eef0d10
bindings-csharp: use the update ABI (#2269)
Co-authored-by: rekhoff <r.ekhoff@clockworklabs.io>
2025-02-22 17:45:28 +00:00
Phoebe Goldman
aedc601145
Rename Address to ConnectionId (#2220)
Signed-off-by: Phoebe Goldman <phoebe@goldman-tribe.org>
Co-authored-by: James Gilles <jameshgilles@gmail.com>
Co-authored-by: Tyler Cloutier <cloutiertyler@users.noreply.github.com>
2025-02-10 00:40:16 +00:00
Phoebe Goldman
91327d585e
Make Timestamp a special SATS type (#1836)
Signed-off-by: Phoebe Goldman <phoebe@goldman-tribe.org>
Co-authored-by: James Gilles <jameshgilles@gmail.com>
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
2025-02-07 18:17:53 +00:00
Ingvar Stepanyan
79a9c712f0
Implement new C# index syntax (#2078) 2025-01-07 20:58:17 +00:00
Ingvar Stepanyan
40faad6e2e
Implement ctx.Identity for C# (#2091) 2025-01-07 20:45:27 +00:00
Ingvar Stepanyan
c87add7349
C#: fix class names for unique column indices (#2088) 2025-01-06 18:58:07 +00:00
Ingvar Stepanyan
78b48c9a98
Reduce noisy C# compilation errors (#2067) 2024-12-30 23:48:50 +00:00
Ingvar Stepanyan
08a3f3210b
Migrate C# ModuleDef to V9 (#1670) 2024-12-16 21:49:42 +00:00
james gilles
c657b4f088
Rip useless names out of RawDef (#1918) 2024-11-20 21:02:00 +00:00
Ingvar Stepanyan
0935b7346b
Fix Find() with struct-based rows (#1917) 2024-10-29 21:33:59 +00:00
Jeremie Pelletier
02b70d5c94
c# client generate (#1707) 2024-10-04 04:12:15 +00:00
Ingvar Stepanyan
5ee077e6d9
[C#] [Reducers] Implement new context fields, enum-based kinds, validation (#1729) 2024-10-01 16:30:32 +00:00
Ingvar Stepanyan
8790abde78
[C#] Mark experimental API (#1769) 2024-09-30 21:42:08 +00:00
Ingvar Stepanyan
bcdea5702c
[C#] Remove .Query support (#1772) 2024-09-30 21:22:29 +00:00
Ingvar Stepanyan
37ac4a43ee
[C#] Fix table view names and duplicate table registration (#1757) 2024-09-30 16:17:12 +00:00
Ingvar Stepanyan
6884bf6f25
Fix C# visibility check (#1765) 2024-09-30 14:14:53 +00:00