Skip to content

Value-Type Design

An Stdb.* value type wraps an Effect schema rather than being a plain schema. That design is deliberate: SpaceTimeDB declaration sites must reject raw Effect schemas at compile time, while application code still needs ordinary schemas for decode, encode, HTTP bodies, and domain logic.

A value type must:

  1. Reuse an Effect schema for TypeScript values and JSON codecs.
  2. Carry SpaceTimeDB SATS information so tables, params, returns, and errors lower to native types.

Effect annotations preserve runtime metadata, but TypeScript cannot see whether a schema has a particular annotation. Phantom type markers on schemas are also fragile because Effect transforms rebuild schema types and strip the marker.

effect-spacetimedb uses an opaque wrapper. The wrapper is not itself a schema; unwrap with .schema when ordinary Effect schema interop is required.

This keeps one important guarantee: raw schemas are rejected in SATS positions at compile time. It also avoids “tag must be last” bugs where a refinement or brand accidentally strips a marker.

For branded domain values, keep the raw Effect schema as the source of truth and wrap it inline at SpaceTimeDB boundaries:

const UserId = Schema.String.pipe(Schema.brand("UserId"))
Stdb.string(UserId) // table fields, params, returns, error fields

For generic SATS values reused across many fields, keep a value-type alias:

const ShortString = Stdb.string(Schema.String.pipe(Schema.minLength(1)))