Packages
caffeine_lang
6.2.0
6.3.1
6.3.0
6.2.2
6.2.1
6.2.0
6.1.2
6.1.1
6.1.0
6.0.0
5.6.0
5.5.0
5.4.4
5.4.3
5.4.2
5.4.1
5.4.0
5.3.0
5.2.0
5.1.1
5.1.0
5.0.12
5.0.11
5.0.10
5.0.8
5.0.7
5.0.6
5.0.5
5.0.4
5.0.1
5.0.0
4.10.0
4.9.0
4.8.3
4.8.2
4.8.1
4.8.0
4.7.9
4.7.8
4.7.7
4.7.6
4.7.5
4.6.7
4.6.6
4.6.5
4.6.4
4.6.3
4.6.2
4.6.0
4.5.1
4.5.0
4.4.4
4.4.3
4.4.1
4.4.0
4.3.7
4.3.6
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.0.2
1.0.1
0.1.0
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.2
0.0.1
A compiler for generating reliability artifacts from service expectation definitions.
Current section
Files
Jump to
Current section
Files
src/caffeine_lang@codegen@relay.erl
-module(caffeine_lang@codegen@relay).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/codegen/relay.gleam").
-export([generate/1]).
-export_type([signal_entry/0, signal_kind/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type signal_entry() :: {signal_entry,
binary(),
signal_kind(),
binary(),
gleam@dict:dict(binary(), caffeine_lang@value:value()),
gleam@dict:dict(binary(), binary()),
gleam@option:option(binary())}.
-type signal_kind() :: count | distribution.
-file("src/caffeine_lang/codegen/relay.gleam", 119).
?DOC(
" Serialize the `tags` dict — already `Dict(String, String)` because every\n"
" codegen-emitted tag is a string. Sorted by key for deterministic output.\n"
).
-spec tags_dict_json(gleam@dict:dict(binary(), binary())) -> gleam@json:json().
tags_dict_json(Tags) ->
_pipe = Tags,
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun(A, B) ->
gleam@string:compare(erlang:element(1, A), erlang:element(1, B))
end
),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Pair) ->
{erlang:element(1, Pair),
gleam@json:string(erlang:element(2, Pair))}
end
),
gleam@json:object(_pipe@3).
-file("src/caffeine_lang/codegen/relay.gleam", 148).
-spec match_value_json(caffeine_lang@value:value()) -> gleam@json:json().
match_value_json(V) ->
case V of
{string_value, S} ->
gleam@json:string(S);
{int_value, I} ->
gleam@json:int(I);
{float_value, F} ->
gleam@json:float(F);
{bool_value, B} ->
gleam@json:bool(B);
_ ->
gleam@json:null()
end.
-file("src/caffeine_lang/codegen/relay.gleam", 140).
?DOC(
" Serialize a `match` dict. Match values are runtime-untyped at the relay,\n"
" so we emit each as its nearest JSON shape. Strings stay strings; numeric\n"
" and boolean values keep their JSON-native form. Other shapes fall back\n"
" to `null` (shouldn't appear post-validate). Entries are sorted by key for\n"
" deterministic output across compile targets — `dict.to_list` doesn't\n"
" guarantee insertion order on JavaScript.\n"
).
-spec match_dict_json(gleam@dict:dict(binary(), caffeine_lang@value:value())) -> gleam@json:json().
match_dict_json(Match) ->
_pipe = Match,
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun(A, B) ->
gleam@string:compare(erlang:element(1, A), erlang:element(1, B))
end
),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Pair) ->
{erlang:element(1, Pair), match_value_json(erlang:element(2, Pair))}
end
),
gleam@json:object(_pipe@3).
-file("src/caffeine_lang/codegen/relay.gleam", 127).
-spec signal_kind_to_string(signal_kind()) -> binary().
signal_kind_to_string(Kind) ->
case Kind of
count ->
<<"count"/utf8>>;
distribution ->
<<"distribution"/utf8>>
end.
-file("src/caffeine_lang/codegen/relay.gleam", 103).
-spec signal_entry_json(signal_entry()) -> gleam@json:json().
signal_entry_json(Entry) ->
gleam@json:object(
[{<<"metric"/utf8>>, gleam@json:string(erlang:element(2, Entry))},
{<<"kind"/utf8>>,
gleam@json:string(
signal_kind_to_string(erlang:element(3, Entry))
)},
{<<"source"/utf8>>, gleam@json:string(erlang:element(4, Entry))},
{<<"match"/utf8>>, match_dict_json(erlang:element(5, Entry))},
{<<"tags"/utf8>>, tags_dict_json(erlang:element(6, Entry))},
{<<"value_path"/utf8>>, case erlang:element(7, Entry) of
none ->
gleam@json:null();
{some, P} ->
gleam@json:string(P)
end}]
).
-file("src/caffeine_lang/codegen/relay.gleam", 96).
?DOC(
" Wrap a list of entries in the top-level `signals.json` object: a version\n"
" tag plus the routing array. Schema version 2 adds the per-entry `tags`\n"
" field; relay builds before 2026-05-23 read v1 and don't know about tags.\n"
).
-spec signals_json(list(signal_entry())) -> gleam@json:json().
signals_json(Entries) ->
gleam@json:object(
[{<<"version"/utf8>>, gleam@json:int(2)},
{<<"signals"/utf8>>,
gleam@json:array(Entries, fun signal_entry_json/1)}]
).
-file("src/caffeine_lang/codegen/relay.gleam", 65).
?DOC(
" Build the list of signal entries from a single resolved IR by walking\n"
" `slo.indicators` for `ExternalSignal` variants. Literal-query indicators\n"
" are skipped — they don't need relay routing. Entries are sorted by\n"
" indicator name for deterministic output across compile targets.\n"
).
-spec ir_to_signal_entries(
caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:resolved())
) -> list(signal_entry()).
ir_to_signal_entries(Ir) ->
_pipe = erlang:element(3, erlang:element(5, Ir)),
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun(A, B) ->
gleam@string:compare(erlang:element(1, A), erlang:element(1, B))
end
),
gleam@list:filter_map(
_pipe@2,
fun(Pair) ->
{Name, Src} = Pair,
case Src of
{external_signal, Source, Match, Value_extraction} ->
{ok,
{signal_entry,
<<"caffeine."/utf8, (erlang:element(3, Ir))/binary>>,
case Value_extraction of
none ->
count;
{some, _} ->
distribution
end,
Source,
Match,
maps:from_list([{<<"indicator"/utf8>>, Name}]),
gleam@option:map(
Value_extraction,
fun(Ve) -> erlang:element(2, Ve) end
)}};
_ ->
{error, nil}
end
end
).
-file("src/caffeine_lang/codegen/relay.gleam", 51).
?DOC(false).
-spec generate(
list(caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:resolved()))
) -> gleam@option:option(binary()).
generate(Irs) ->
Entries = gleam@list:flat_map(Irs, fun ir_to_signal_entries/1),
case Entries of
[] ->
none;
_ ->
{some, gleam@json:to_string(signals_json(Entries))}
end.