Jido.Signal (Jido Signal v2.0.0-rc.1)
View SourceDefines the core Signal structure in Jido, implementing the CloudEvents specification (v1.0.2) with Jido-specific extensions for agent-based systems.
Overview
Signals are the universal message format in Jido, serving as the nervous system of your agent-based application. Every event, command, and state change flows through the system as a Signal, providing:
- Standardized event structure (CloudEvents v1.0.2 compatible)
- Rich metadata and context tracking
- Flexible dispatch configuration
- Automatic serialization
CloudEvents Compliance
Each Signal implements the CloudEvents v1.0.2 specification with these required fields:
specversion: Always "1.0.2"id: Unique identifier (UUID v4)source: Origin of the event ("/service/component")type: Classification of the event ("domain.entity.action")
And optional fields:
subject: Specific subject of the eventtime: Timestamp in ISO 8601 formatdatacontenttype: Media type of the data (defaults to "application/json")dataschema: Schema defining the data structuredata: The actual event payload
Jido Extensions
Beyond the CloudEvents spec, Signals support a flexible extension system for
adding custom metadata and behavior. See Jido.Signal.Ext for details.
Creating Signals
Signals can be created in several ways (prefer the positional new/3):
alias Jido.Signal
# Preferred: positional constructor (type, data, attrs)
{:ok, signal} = Signal.new("metrics.collected", %{cpu: 80, memory: 70},
source: "/monitoring"
)
# Also available: map/keyword constructor (backwards compatible)
{:ok, signal} = Signal.new(%{
type: "user.created",
source: "/auth/registration",
data: %{user_id: "123", email: "user@example.com"}
})
# Data payloads follow CloudEvents rules:
# - When datacontenttype is JSON (or omitted in JSON format), `data` can be any JSON value
# (object/map, array, string, number, boolean, null)
# - For non-JSON payloads, encode according to datacontenttype; binary payloads use data_base64 during JSON serializationCustom Signal Types
You can define custom Signal types using the use Jido.Signal pattern:
defmodule MySignal do
use Jido.Signal,
type: "my.custom.signal",
default_source: "/my/service",
datacontenttype: "application/json",
schema: [
user_id: [type: :string, required: true],
message: [type: :string, required: true]
]
end
# Create instances
{:ok, signal} = MySignal.new(%{user_id: "123", message: "Hello"})
# Override runtime fields
{:ok, signal} = MySignal.new(
%{user_id: "123", message: "Hello"},
source: "/different/source",
subject: "user-notification"
)Signal Types
Signal types are strings, but typically use a hierarchical dot notation:
<domain>.<entity>.<action>[.<qualifier>]Examples:
user.profile.updatedorder.payment.processed.successsystem.metrics.collected
Guidelines for type naming:
- Use lowercase with dots
- Keep segments meaningful
- Order from general to specific
- Include qualifiers when needed
Data Content Types
The datacontenttype field indicates the format of the data field:
application/json(default) - JSON-structured datatext/plain- Unstructured textapplication/octet-stream- Binary data- Custom MIME types for specific formats
Dispatch Configuration
Signal dispatch is configured when subscribing to the Bus or when calling Dispatch directly:
# Configure dispatch when subscribing
Bus.subscribe(bus, "user.*", dispatch: {:pubsub, topic: "events"})
# Or dispatch directly with config
Dispatch.dispatch(signal, {:logger, level: :info})See Also
Jido.Signal.Router- Signal routingJido.Signal.Dispatch- Dispatch handling- CloudEvents spec: https://cloudevents.io/
Summary
Functions
Defines a new Signal module.
Removes extension data from a Signal.
Deserializes binary data back into a Signal struct or list of Signal structs.
Flattens extension data to CloudEvents-compliant top-level attributes.
Creates a new Signal struct from a map.
Retrieves extension data from a Signal.
Inflates top-level attributes back to extension data.
Lists all extensions currently present on a Signal.
Converts a struct or list of structs to Signal data format.
Creates a new Signal struct.
Creates a Signal with explicit type and payload, plus optional attrs.
Creates a new Signal struct, raising an error if invalid.
Creates a new Signal struct with explicit type and data, raising an error if invalid.
Adds extension data to a Signal.
Returns the Zoi schema for Signal
Serializes a Signal or a list of Signals using the specified or default serializer.
Legacy serialize function that returns binary directly (for backward compatibility).
Types
Functions
Defines a new Signal module.
This macro sets up the necessary structure and callbacks for a custom Signal, including configuration validation and default implementations.
Options
:type(String.t/0) - Required. The type of the Signal:default_source(String.t/0) - The default source of the Signal:datacontenttype(String.t/0) - The content type of the data field:dataschema(String.t/0) - Schema URI for the data field (optional):schema(keyword/0) - A NimbleOptions schema for validating the Signal's data parameters The default value is[].
Examples
defmodule MySignal do
use Jido.Signal,
type: "my.custom.signal",
default_source: "/my/service",
schema: [
user_id: [type: :string, required: true],
message: [type: :string, required: true]
]
end
Removes extension data from a Signal.
Removes the extension data stored under the given namespace and returns the updated Signal.
Parameters
signal- The Signal struct to remove fromnamespace- The extension namespace (string)
Returns
The updated Signal struct with the extension removed
Examples
# Remove authentication extension
updated_signal = Jido.Signal.delete_extension(signal, "auth")
# Extension data is no longer present
nil = Jido.Signal.get_extension(updated_signal, "auth")
Deserializes binary data back into a Signal struct or list of Signal structs.
Parameters
binary: The serialized binary data to deserializeopts: Optional configuration including::serializer- The serializer module to use (defaults to configured serializer):type- Specific type to deserialize to:type_provider- Custom type provider
Returns
{:ok, Signal.t() | list(Signal.t())} if successful, {:error, reason} otherwise
Examples
# JSON deserialization (default)
iex> json = ~s({"type":"example.event","source":"/example","id":"123"})
iex> {:ok, signal} = Jido.Signal.deserialize(json)
iex> signal.type
"example.event"
# Using a specific serializer
iex> {:ok, signal} = Jido.Signal.deserialize(binary, serializer: Jido.Signal.Serialization.ErlangTermSerializer)
iex> signal.type
"example.event"
# Deserializing multiple Signals
iex> json = ~s([{"type":"event1","source":"/ex1"},{"type":"event2","source":"/ex2"}])
iex> {:ok, signals} = Jido.Signal.deserialize(json)
iex> length(signals)
2
Flattens extension data to CloudEvents-compliant top-level attributes.
Takes a Signal struct and converts all extension data to top-level attributes
by calling each extension's to_attrs/1 function and merging the results
into the base signal map.
Parameters
signal- The Signal struct containing extensions to flatten
Returns
A map with extensions flattened to top-level CloudEvents attributes
Examples
signal = %Signal{type: "test", source: "/test", extensions: %{"auth" => %{user_id: "123"}}}
flattened = Jido.Signal.flatten_extensions(signal)
# => %{"type" => "test", "source" => "/test", "user_id" => "123", ...}
Creates a new Signal struct from a map.
Parameters
map: A map containing the Signal attributes.
Returns
{:ok, Signal.t()} if the map is valid, {:error, String.t()} otherwise.
Examples
iex> Jido.Signal.from_map(%{"type" => "example.event", "source" => "/example", "id" => "123"})
{:ok, %Jido.Signal{type: "example.event", source: "/example", id: "123", ...}}
Retrieves extension data from a Signal.
Returns the extension data stored under the given namespace, or nil if no data exists for that extension.
Parameters
signal- The Signal struct to retrieve fromnamespace- The extension namespace (string)
Returns
The extension data if present, nil otherwise
Examples
# Retrieve authentication data
auth_data = Jido.Signal.get_extension(signal, "auth")
# => %{user_id: "123", roles: ["user"]}
# Non-existent extension returns nil
missing = Jido.Signal.get_extension(signal, "nonexistent")
# => nil
Inflates top-level attributes back to extension data.
Takes a map of attributes (typically from deserialization) and extracts
extension data by calling each registered extension's from_attrs/1 function.
Returns both the extensions map and the remaining attributes with extension
data removed.
Parameters
attrs- Map of attributes to extract extensions from
Returns
A tuple {extensions_map, remaining_attrs} where:
extensions_mapcontains extension data keyed by namespaceremaining_attrshas extension-specific attributes removed
Examples
attrs = %{"type" => "test", "source" => "/test", "user_id" => "123", "roles" => ["admin"]}
{extensions, remaining} = Jido.Signal.inflate_extensions(attrs)
# => {%{"auth" => %{user_id: "123", roles: ["admin"]}}, %{"type" => "test", "source" => "/test"}}
Lists all extensions currently present on a Signal.
Returns a list of extension namespace strings for extensions that have data stored in the Signal.
Parameters
signal- The Signal struct to inspect
Returns
A list of extension namespace strings
Examples
# Signal with multiple extensions
extensions = Jido.Signal.list_extensions(signal)
# => ["auth", "tracking", "metadata"]
# Signal with no extensions
extensions = Jido.Signal.list_extensions(signal)
# => []
@spec map_to_signal_data([struct()], Keyword.t()) :: [t()]
@spec map_to_signal_data( struct(), Keyword.t() ) :: t()
Converts a struct or list of structs to Signal data format.
This function is useful for converting domain objects to Signal format while preserving their type information through the TypeProvider.
Parameters
signals: A struct or list of structs to convertfields: Additional fields to include (currently unused)
Returns
Signal struct or list of Signal structs with the original data as payload
Examples
# Converting a single struct
iex> user = %User{id: 1, name: "John"}
iex> signal = Jido.Signal.map_to_signal_data(user)
iex> signal.data
%User{id: 1, name: "John"}
# Converting multiple structs
iex> users = [%User{id: 1}, %User{id: 2}]
iex> signals = Jido.Signal.map_to_signal_data(users)
iex> length(signals)
2
Creates a new Signal struct.
Parameters
attrs: A map or keyword list containing the Signal attributes.
Returns
{:ok, Signal.t()} if the attributes are valid, {:error, String.t()} otherwise.
Examples
iex> Jido.Signal.new(%{type: "example.event", source: "/example", id: "123"})
{:ok, %Jido.Signal{type: "example.event", source: "/example", id: "123", ...}}
iex> Jido.Signal.new(type: "example.event", source: "/example")
{:ok, %Jido.Signal{type: "example.event", source: "/example", ...}}
Creates a Signal with explicit type and payload, plus optional attrs.
typemust be a stringdatacan be any term (map, string, etc.)attrsis a map or keyword list for other fields (e.g.,:source,:subject,:jido_dispatch)attrsmust NOT include:type/"type"or:data/"data"
Examples:
iex> {:ok, s} = Jido.Signal.new("user.created", %{user_id: "123"}, source: "/auth")
iex> s.type
"user.created"
iex> {:ok, s} = Jido.Signal.new("log.message", "Hello world")
iex> s.data
"Hello world"
Creates a new Signal struct, raising an error if invalid.
Parameters
attrs: A map or keyword list containing the Signal attributes.
Returns
Signal.t() if the attributes are valid.
Raises
RuntimeError if the attributes are invalid.
Examples
iex> Jido.Signal.new!(%{type: "example.event", source: "/example"})
%Jido.Signal{type: "example.event", source: "/example", ...}
iex> Jido.Signal.new!(type: "example.event", source: "/example")
%Jido.Signal{type: "example.event", source: "/example", ...}
Creates a new Signal struct with explicit type and data, raising an error if invalid.
Parameters
type: A string representing the event type (e.g.,"user.created").data: The payload (any term; see CloudEvents rules below).attrs: (Optional) A map or keyword list of additional Signal attributes (e.g.,:source,:subject).
Returns
Signal.t() if the attributes are valid.
Raises
ArgumentError if the attributes are invalid.
Examples
iex> Jido.Signal.new!("user.created", %{user_id: "123"}, source: "/auth")
%Jido.Signal{type: "user.created", source: "/auth", data: %{user_id: "123"}, ...}
iex> Jido.Signal.new!("user.created", %{user_id: "123"})
%Jido.Signal{type: "user.created", source: "...", data: %{user_id: "123"}, ...}
Adds extension data to a Signal.
Validates the extension data against the extension's schema and stores it in the Signal's extensions map under the extension's namespace.
Parameters
signal- The Signal struct to add the extension tonamespace- The extension namespace (string)data- The extension data to add
Returns
{:ok, Signal.t()} if successful, {:error, reason} if validation fails
Examples
# Add authentication extension data
{:ok, signal} = Jido.Signal.put_extension(signal, "auth", %{user_id: "123"})
# Validation will occur based on extension schema
{:ok, signal} = Jido.Signal.put_extension(signal, "tracking", %{session_id: "abc"})
Returns the Zoi schema for Signal
Serializes a Signal or a list of Signals using the specified or default serializer.
Parameters
signal_or_list: A Signal struct or list of Signal structsopts: Optional configuration including::serializer- The serializer module to use (defaults to configured serializer)
Returns
{:ok, binary} on success, {:error, reason} on failure
Examples
iex> signal = %Jido.Signal{type: "example.event", source: "/example"}
iex> {:ok, binary} = Jido.Signal.serialize(signal)
iex> is_binary(binary)
true
# Using a specific serializer
iex> {:ok, binary} = Jido.Signal.serialize(signal, serializer: Jido.Signal.Serialization.ErlangTermSerializer)
iex> is_binary(binary)
true
# Serializing multiple Signals
iex> signals = [
...> %Jido.Signal{type: "event1", source: "/ex1"},
...> %Jido.Signal{type: "event2", source: "/ex2"}
...> ]
iex> {:ok, binary} = Jido.Signal.serialize(signals)
iex> is_binary(binary)
true
Legacy serialize function that returns binary directly (for backward compatibility).