Packages

Elixir client for SpacetimeDB — BSATN binary protocol, WebSocket subscriptions, reducer calls, live ETS table mirrors

Current section

Files

Jump to
spacetimedb_ex lib spacetimedb types.ex
Raw

lib/spacetimedb/types.ex

defmodule SpacetimeDB.Types do
@moduledoc "Structs representing SpacetimeDB WebSocket protocol types."
# ---------------------------------------------------------------------------
# Identifiers
# ---------------------------------------------------------------------------
defmodule Identity do
@moduledoc "A 256-bit public key identifying a SpacetimeDB user or module."
@enforce_keys [:hex]
defstruct [:hex]
@type t :: %__MODULE__{hex: String.t()}
end
defmodule ConnectionId do
@moduledoc "An opaque identifier for a single client WebSocket connection."
@enforce_keys [:hex]
defstruct [:hex]
@type t :: %__MODULE__{hex: String.t()}
end
defmodule Timestamp do
@moduledoc "A point in time, in microseconds since the Unix epoch."
@enforce_keys [:microseconds_since_epoch]
defstruct [:microseconds_since_epoch]
@type t :: %__MODULE__{microseconds_since_epoch: integer()}
@doc "Convert to an Elixir `DateTime`."
@spec to_datetime(t()) :: DateTime.t()
def to_datetime(%__MODULE__{microseconds_since_epoch: us}),
do: DateTime.from_unix!(us, :microsecond)
end
# ---------------------------------------------------------------------------
# Table / row data
# ---------------------------------------------------------------------------
defmodule TableUpdate do
@moduledoc """
Incremental row changes for a single table, as reported in a transaction or
initial-subscription response.
In the JSON protocol rows are plain JSON values (maps, lists, etc.) decoded
directly from the server's response.
"""
defstruct table_id: nil, table_name: nil, inserts: [], deletes: []
@type t :: %__MODULE__{
table_id: non_neg_integer() | nil,
table_name: String.t() | nil,
inserts: [term()],
deletes: [term()]
}
end
# ---------------------------------------------------------------------------
# Server → Client messages
# ---------------------------------------------------------------------------
defmodule IdentityToken do
@moduledoc "First message after connecting — contains the client's identity and session token."
@enforce_keys [:identity, :token, :connection_id]
defstruct [:identity, :token, :connection_id]
@type t :: %__MODULE__{
identity: String.t(),
token: String.t(),
connection_id: String.t()
}
end
defmodule InitialSubscription do
@moduledoc "Initial rows for a legacy multi-query `Subscribe` request."
defstruct request_id: nil, tables: [], execution_time_micros: 0
@type t :: %__MODULE__{
request_id: non_neg_integer() | nil,
tables: [TableUpdate.t()],
execution_time_micros: non_neg_integer()
}
end
defmodule SubscribeApplied do
@moduledoc "Confirmation + initial rows for a `SubscribeSingle` or `SubscribeMulti` request."
defstruct request_id: nil, query_id: nil, tables: []
@type t :: %__MODULE__{
request_id: non_neg_integer() | nil,
query_id: non_neg_integer() | nil,
tables: [TableUpdate.t()]
}
end
defmodule UnsubscribeApplied do
@moduledoc "Confirmation that a `Unsubscribe` was processed."
defstruct request_id: nil, query_id: nil, tables: []
@type t :: %__MODULE__{
request_id: non_neg_integer() | nil,
query_id: non_neg_integer() | nil,
tables: [TableUpdate.t()]
}
end
defmodule SubscriptionError do
@moduledoc "The server rejected or invalidated a subscription."
defstruct request_id: nil, query_id: nil, error: nil
@type t :: %__MODULE__{
request_id: non_neg_integer() | nil,
query_id: non_neg_integer() | nil,
error: String.t()
}
end
defmodule ReducerCallInfo do
@moduledoc "Metadata about the reducer that produced a `TransactionUpdate`."
defstruct reducer_name: nil, request_id: nil, status: nil
@type t :: %__MODULE__{
reducer_name: String.t() | nil,
request_id: non_neg_integer() | nil,
status: :committed | {:failed, String.t()} | :out_of_energy | nil
}
end
defmodule TransactionUpdate do
@moduledoc """
Pushed to all subscribers whose queries match rows touched by a committed
reducer call. Also sent back to the caller of the reducer (even without a
matching subscription).
"""
defstruct [
:caller_identity,
:caller_connection_id,
:reducer_call,
:timestamp,
tables: [],
status: :committed,
energy_consumed: 0
]
@type status :: :committed | {:failed, String.t()} | :out_of_energy
@type t :: %__MODULE__{
caller_identity: String.t() | nil,
caller_connection_id: String.t() | nil,
reducer_call: ReducerCallInfo.t() | nil,
timestamp: Timestamp.t() | nil,
tables: [TableUpdate.t()],
status: status(),
energy_consumed: non_neg_integer()
}
end
defmodule OneOffQueryResponse do
@moduledoc "Response to a `OneOffQuery` request."
defstruct message_id: nil, error: nil, tables: [], execution_time_micros: 0
@type t :: %__MODULE__{
message_id: binary(),
error: String.t() | nil,
tables: [TableUpdate.t()],
execution_time_micros: non_neg_integer()
}
end
end