-module(rocksky).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/rocksky.gleam").
-export([new/0, with_base_url/2, with_bearer_token/2, without_token/1, with_user_agent/2, with_header/3, with_send/2, base_url/1, 'query'/2, procedure/2, body/2, param/3, int_param/3, bool_param/3, repeated_param/3, header/3, limit/2, offset/2, cursor/2, start_date/2, end_date/2, genre/2, year/2, size/2, send/2]).
-export_type([client/0, method/0, request/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Rocksky — a Gleam SDK for the Rocksky XRPC API.\n"
"\n"
" The API is pipe-friendly: build a `Request(a)` with the relevant\n"
" endpoint constructor, chain param functions on it, then hand it to\n"
" `send` together with a `Client`.\n"
"\n"
" ```gleam\n"
" import rocksky\n"
" import rocksky/actor\n"
"\n"
" pub fn main() {\n"
" let client =\n"
" rocksky.new()\n"
" |> rocksky.with_bearer_token(\"xxx\")\n"
"\n"
" let assert Ok(profile) =\n"
" actor.get_profile(did: \"alice.bsky.social\")\n"
" |> rocksky.send(client)\n"
"\n"
" let assert Ok(scrobbles) =\n"
" actor.get_actor_scrobbles(did: \"alice.bsky.social\")\n"
" |> rocksky.limit(50)\n"
" |> rocksky.offset(0)\n"
" |> rocksky.send(client)\n"
" }\n"
" ```\n"
).
-opaque client() :: {client,
binary(),
gleam@option:option(binary()),
binary(),
list({binary(), binary()}),
fun((gleam@http@request:request(binary())) -> {ok,
gleam@http@response:response(binary())} |
{error, binary()})}.
-type method() :: query_method | procedure_method.
-opaque request(FGM) :: {request,
method(),
binary(),
list({binary(), binary()}),
list({binary(), binary()}),
gleam@option:option(gleam@json:json()),
gleam@dynamic@decode:decoder(FGM)}.
-file("src/rocksky.gleam", 374).
-spec default_send(gleam@http@request:request(binary())) -> {ok,
gleam@http@response:response(binary())} |
{error, binary()}.
default_send(Req) ->
_pipe = gleam@httpc:send(Req),
gleam@result:map_error(_pipe, fun gleam@string:inspect/1).
-file("src/rocksky.gleam", 71).
?DOC(" Build a fresh client targeting `default_base_url` with no authentication.\n").
-spec new() -> client().
new() ->
{client,
<<"https://api.rocksky.app"/utf8>>,
none,
<<"rocksky-gleam/0.1.0"/utf8>>,
[],
fun default_send/1}.
-file("src/rocksky.gleam", 367).
-spec trim_trailing_slash(binary()) -> binary().
trim_trailing_slash(Url) ->
case gleam_stdlib:string_ends_with(Url, <<"/"/utf8>>) of
true ->
gleam@string:drop_end(Url, 1);
false ->
Url
end.
-file("src/rocksky.gleam", 82).
?DOC(" Point the client at a different deployment. Trailing slashes are trimmed.\n").
-spec with_base_url(client(), binary()) -> client().
with_base_url(Client, Url) ->
{client,
trim_trailing_slash(Url),
erlang:element(3, Client),
erlang:element(4, Client),
erlang:element(5, Client),
erlang:element(6, Client)}.
-file("src/rocksky.gleam", 87).
?DOC(" Attach a Bluesky session token (sent as `Authorization: Bearer <token>`).\n").
-spec with_bearer_token(client(), binary()) -> client().
with_bearer_token(Client, Token) ->
{client,
erlang:element(2, Client),
{some, Token},
erlang:element(4, Client),
erlang:element(5, Client),
erlang:element(6, Client)}.
-file("src/rocksky.gleam", 92).
?DOC(" Remove any previously-set bearer token.\n").
-spec without_token(client()) -> client().
without_token(Client) ->
{client,
erlang:element(2, Client),
none,
erlang:element(4, Client),
erlang:element(5, Client),
erlang:element(6, Client)}.
-file("src/rocksky.gleam", 97).
?DOC(" Override the User-Agent header. Identify your app, please.\n").
-spec with_user_agent(client(), binary()) -> client().
with_user_agent(Client, User_agent) ->
{client,
erlang:element(2, Client),
erlang:element(3, Client),
User_agent,
erlang:element(5, Client),
erlang:element(6, Client)}.
-file("src/rocksky.gleam", 103).
?DOC(
" Add a header that ships with every request from this client.\n"
" To attach a header to a single request, use `header` on the request value.\n"
).
-spec with_header(client(), binary(), binary()) -> client().
with_header(Client, Name, Value) ->
{client,
erlang:element(2, Client),
erlang:element(3, Client),
erlang:element(4, Client),
[{Name, Value} | erlang:element(5, Client)],
erlang:element(6, Client)}.
-file("src/rocksky.gleam", 109).
?DOC(
" Replace the transport function. Mostly useful for tests and for JS\n"
" targets where you want to plug in a custom fetch.\n"
).
-spec with_send(
client(),
fun((gleam@http@request:request(binary())) -> {ok,
gleam@http@response:response(binary())} |
{error, binary()})
) -> client().
with_send(Client, Send) ->
{client,
erlang:element(2, Client),
erlang:element(3, Client),
erlang:element(4, Client),
erlang:element(5, Client),
Send}.
-file("src/rocksky.gleam", 114).
?DOC(" Expose the configured base URL (e.g. for logging or sharing config).\n").
-spec base_url(client()) -> binary().
base_url(Client) ->
erlang:element(2, Client).
-file("src/rocksky.gleam", 144).
?DOC(
" Start building a query (HTTP GET) against `/xrpc/{nsid}`. Endpoint modules\n"
" call this; you only need it directly to reach an XRPC method the SDK\n"
" hasn't surfaced yet.\n"
).
-spec 'query'(binary(), gleam@dynamic@decode:decoder(FGR)) -> request(FGR).
'query'(Nsid, Decoder) ->
{request, query_method, Nsid, [], [], none, Decoder}.
-file("src/rocksky.gleam", 156).
?DOC(" Start building a procedure (HTTP POST). See `query` for usage notes.\n").
-spec procedure(binary(), gleam@dynamic@decode:decoder(FGU)) -> request(FGU).
procedure(Nsid, Decoder) ->
{request, procedure_method, Nsid, [], [], none, Decoder}.
-file("src/rocksky.gleam", 168).
?DOC(" Attach a JSON body to a request. Procedures often need this.\n").
-spec body(request(FGX), gleam@json:json()) -> request(FGX).
body(Req, Body) ->
{request,
erlang:element(2, Req),
erlang:element(3, Req),
erlang:element(4, Req),
erlang:element(5, Req),
{some, Body},
erlang:element(7, Req)}.
-file("src/rocksky.gleam", 175).
?DOC(" Add a single string query parameter.\n").
-spec param(request(FHA), binary(), binary()) -> request(FHA).
param(Req, Name, Value) ->
{request,
erlang:element(2, Req),
erlang:element(3, Req),
[{Name, Value} | erlang:element(4, Req)],
erlang:element(5, Req),
erlang:element(6, Req),
erlang:element(7, Req)}.
-file("src/rocksky.gleam", 180).
?DOC(" Add a single integer query parameter (stringified at send time).\n").
-spec int_param(request(FHD), binary(), integer()) -> request(FHD).
int_param(Req, Name, Value) ->
param(Req, Name, erlang:integer_to_binary(Value)).
-file("src/rocksky.gleam", 185).
?DOC(" Add a single boolean query parameter (`true` / `false`).\n").
-spec bool_param(request(FHG), binary(), boolean()) -> request(FHG).
bool_param(Req, Name, Value) ->
S = case Value of
true ->
<<"true"/utf8>>;
false ->
<<"false"/utf8>>
end,
param(Req, Name, S).
-file("src/rocksky.gleam", 195).
?DOC(
" Add a query parameter repeated once per value (XRPC convention for\n"
" array-typed parameters, e.g. `getFollowers?dids=did:plc:a&dids=did:plc:b`).\n"
).
-spec repeated_param(request(FHJ), binary(), list(binary())) -> request(FHJ).
repeated_param(Req, Name, Values) ->
gleam@list:fold(Values, Req, fun(R, V) -> param(R, Name, V) end).
-file("src/rocksky.gleam", 205).
?DOC(
" Attach a header to just this request (in addition to any defaults the\n"
" client sets).\n"
).
-spec header(request(FHN), binary(), binary()) -> request(FHN).
header(Req, Name, Value) ->
{request,
erlang:element(2, Req),
erlang:element(3, Req),
erlang:element(4, Req),
[{Name, Value} | erlang:element(5, Req)],
erlang:element(6, Req),
erlang:element(7, Req)}.
-file("src/rocksky.gleam", 213).
-spec limit(request(FHQ), integer()) -> request(FHQ).
limit(Req, N) ->
int_param(Req, <<"limit"/utf8>>, N).
-file("src/rocksky.gleam", 217).
-spec offset(request(FHT), integer()) -> request(FHT).
offset(Req, N) ->
int_param(Req, <<"offset"/utf8>>, N).
-file("src/rocksky.gleam", 221).
-spec cursor(request(FHW), binary()) -> request(FHW).
cursor(Req, C) ->
param(Req, <<"cursor"/utf8>>, C).
-file("src/rocksky.gleam", 225).
-spec start_date(request(FHZ), binary()) -> request(FHZ).
start_date(Req, Date) ->
param(Req, <<"startDate"/utf8>>, Date).
-file("src/rocksky.gleam", 229).
-spec end_date(request(FIC), binary()) -> request(FIC).
end_date(Req, Date) ->
param(Req, <<"endDate"/utf8>>, Date).
-file("src/rocksky.gleam", 233).
-spec genre(request(FIF), binary()) -> request(FIF).
genre(Req, G) ->
param(Req, <<"genre"/utf8>>, G).
-file("src/rocksky.gleam", 237).
-spec year(request(FII), integer()) -> request(FII).
year(Req, Y) ->
int_param(Req, <<"year"/utf8>>, Y).
-file("src/rocksky.gleam", 241).
-spec size(request(FIL), integer()) -> request(FIL).
size(Req, N) ->
int_param(Req, <<"size"/utf8>>, N).
-file("src/rocksky.gleam", 350).
-spec parse_error_body(integer(), binary()) -> rocksky@error:rocksy_error().
parse_error_body(Status, Body) ->
Xrpc_decoder = begin
gleam@dynamic@decode:field(
<<"error"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Name) ->
gleam@dynamic@decode:optional_field(
<<"message"/utf8>>,
none,
gleam@dynamic@decode:map(
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Field@0) -> {some, Field@0} end
),
fun(Message) ->
gleam@dynamic@decode:success({Name, Message})
end
)
end
)
end,
case gleam@json:parse(Body, Xrpc_decoder) of
{ok, {Name@1, Message@1}} ->
{xrpc_error, Status, Name@1, Message@1};
{error, _} ->
{http_status_error, Status, Body}
end.
-file("src/rocksky.gleam", 343).
-spec from_json_error(gleam@json:decode_error()) -> rocksky@error:rocksy_error().
from_json_error(Err) ->
case Err of
{unable_to_decode, Errors} ->
{decode_error, Errors};
_ ->
{invalid_input, <<"Server returned invalid JSON"/utf8>>}
end.
-file("src/rocksky.gleam", 332).
-spec decode_body(binary(), gleam@dynamic@decode:decoder(FJF)) -> {ok, FJF} |
{error, rocksky@error:rocksy_error()}.
decode_body(Body, Decoder) ->
case Body of
<<""/utf8>> ->
_pipe = gleam@json:parse(<<"null"/utf8>>, Decoder),
gleam@result:map_error(_pipe, fun from_json_error/1);
_ ->
_pipe@1 = gleam@json:parse(Body, Decoder),
gleam@result:map_error(_pipe@1, fun from_json_error/1)
end.
-file("src/rocksky.gleam", 322).
-spec handle_response(
gleam@http@response:response(binary()),
gleam@dynamic@decode:decoder(FJB)
) -> {ok, FJB} | {error, rocksky@error:rocksy_error()}.
handle_response(Resp, Decoder) ->
case erlang:element(2, Resp) of
S when (S >= 200) andalso (S < 300) ->
decode_body(erlang:element(4, Resp), Decoder);
S@1 ->
{error, parse_error_body(S@1, erlang:element(4, Resp))}
end.
-file("src/rocksky.gleam", 312).
-spec maybe_set_content_type(
gleam@http@request:request(binary()),
gleam@option:option(gleam@json:json())
) -> gleam@http@request:request(binary()).
maybe_set_content_type(Req, Body) ->
case Body of
{some, _} ->
gleam@http@request:set_header(
Req,
<<"content-type"/utf8>>,
<<"application/json"/utf8>>
);
none ->
Req
end.
-file("src/rocksky.gleam", 302).
-spec apply_request_headers(
gleam@http@request:request(binary()),
list({binary(), binary()})
) -> gleam@http@request:request(binary()).
apply_request_headers(Req, Headers) ->
gleam@list:fold(
Headers,
Req,
fun(Acc, H) ->
{Name, Value} = H,
gleam@http@request:set_header(Acc, Name, Value)
end
).
-file("src/rocksky.gleam", 284).
-spec apply_default_headers(gleam@http@request:request(binary()), client()) -> gleam@http@request:request(binary()).
apply_default_headers(Req, Client) ->
Req@1 = begin
_pipe = Req,
_pipe@1 = gleam@http@request:set_header(
_pipe,
<<"accept"/utf8>>,
<<"application/json"/utf8>>
),
gleam@http@request:set_header(
_pipe@1,
<<"user-agent"/utf8>>,
erlang:element(4, Client)
)
end,
Req@2 = case erlang:element(3, Client) of
{some, T} ->
gleam@http@request:set_header(
Req@1,
<<"authorization"/utf8>>,
<<"Bearer "/utf8, T/binary>>
);
none ->
Req@1
end,
gleam@list:fold(
erlang:element(5, Client),
Req@2,
fun(Acc, H) ->
{Name, Value} = H,
gleam@http@request:set_header(Acc, Name, Value)
end
).
-file("src/rocksky.gleam", 250).
?DOC(" Send a request through the client and decode the response.\n").
-spec send(request(FIO), client()) -> {ok, FIO} |
{error, rocksky@error:rocksy_error()}.
send(Req, Client) ->
Url = <<<<<<(erlang:element(2, Client))/binary, "/xrpc/"/utf8>>/binary,
(erlang:element(3, Req))/binary>>/binary,
(rocksky@internal@query:encode(erlang:element(4, Req)))/binary>>,
case gleam@http@request:to(Url) of
{error, _} ->
{error,
{invalid_input,
<<"Invalid XRPC URL constructed: "/utf8, Url/binary>>}};
{ok, Http_req} ->
Body_str = case erlang:element(6, Req) of
{some, J} ->
gleam@json:to_string(J);
none ->
<<""/utf8>>
end,
Http_method = case erlang:element(2, Req) of
query_method ->
get;
procedure_method ->
post
end,
Http_req@1 = begin
_pipe = Http_req,
_pipe@1 = gleam@http@request:set_method(_pipe, Http_method),
_pipe@2 = apply_default_headers(_pipe@1, Client),
_pipe@3 = apply_request_headers(_pipe@2, erlang:element(5, Req)),
_pipe@4 = maybe_set_content_type(
_pipe@3,
erlang:element(6, Req)
),
gleam@http@request:set_body(_pipe@4, Body_str)
end,
_pipe@5 = Http_req@1,
_pipe@6 = (erlang:element(6, Client))(_pipe@5),
_pipe@7 = gleam@result:map_error(
_pipe@6,
fun(Field@0) -> {transport_error, Field@0} end
),
gleam@result:'try'(
_pipe@7,
fun(Resp) -> handle_response(Resp, erlang:element(7, Req)) end
)
end.