figbus/elm-urbit-api/2.0.1

NOTE: the latest version is 4.0.1

Urbit

Data Types

type Session

A session represents an active connection to an urbit ship.

type alias Message =
{ lastEventId : Int
, data : MessageData
}

Represents an incoming message.

= Poke Int (Result String ())
| Subscribe Int (Result String ())
| Diff Int Value
| Quit Int

An incoming message may be one of the following:

  • Poke represents a response for a previous poke
  • Subscribe represents a response for a subscription requests
  • Diff represents a new incoming diff for a subscription
  • Quit means you have been kicked from a subscription and should likely attempt to resubscribe

In all cases the first Int value represents the Id of the message it is relevant to.

url : Session -> String

Get a Session's url.

ship : Session -> String

Get a Session's ship.

uid : Session -> String

Get a Session's uid.

Get a Session's lastEventId.

Connection Setup

These are required in order to maintain an active connection to an urbit ship and to perform poke/subscription requests.

{ ship : String
, url : String
, channelId : String
, code : String
}
-> (Result Error Session -> msg)
-> Cmd msg

Connect to an urbit ship and return a Session on success. Should be followed by setupEventSource (this must be done as a separate step due to the nature of Elm ports).

You can generate a unique channelId any way you wish, however the provided genChannelId function is recommended.

{ ship : String
, url : String
, channelId : String
}
-> (Result Error Session -> msg)
-> Cmd msg

Alternative to connect that does not require a code.

Note: Ensure the correct cookie is set through some other means beforehand or else this will not work. Hosting from within an urbit using the :file-server app with public set to false should work, for example.

setupEventSource : (String -> Cmd msg) -> Session -> Cmd msg

This will set up the realtime communication channel with urbit. Because Elm does not support the EventSource API required, this must be done using ports. Elm does not allow ports to be published as part of a package, but luckily the setup for this is fairly straightforward.

First, copy/paste the following into your js file and replace any references to app with the variable name of your running Elm app:

app.ports.setupUrbitEventSource.subscribe((url) => {
  const eventSource = new EventSource(url, { withCredentials: true });

  eventSource.onmessage = function (event) {
    app.ports.onUrbitMessage.send({ message: event });
  };

  eventSource.onerror = function (event) {
    app.ports.onUrbitMessage.send({ error: event });
  };
});

Next, declare a port module and the following ports to handle the initial setup and incoming messages:

port setupUrbitEventSource : String -> Cmd msg


port onUrbitMessage : (JE.Value -> msg) -> Sub msg

Now, the newly created setupUrbitEventSource can be passed as the first argument to our setupEventSource function and used to perform the necessary setup. Similarly, the onUrbitMessage function can be passed as the first argument to messages below to set up the subscription to incoming events.

setupCommand =
    Urbit.setupEventSource setupUrbitEventSource session


messagesSubscription =
    Urbit.messages onUrbitMessage GotUrbitMessage

See the Elm guide for further information regarding ports.

messages : ((Value -> msg) -> Sub msg) -> (Result Error Message -> msg) -> Sub msg

Subscribe to incoming messages from urbit. See setupEventSource above for instructions on how to use this. Every message received must be acknowledged by using the related ack function, otherwise urbit will stop accepting any further messages.

ack :
{ lastEventId : Int
, session : Session
}
-> (Result Error () -> msg)
-> ( Session, Cmd msg )

Acknowledge an incoming message. Must be performed for every incoming message received.

Stateless Requests

These do not require an active connection, however they do at least require you to be authenticated.

{ url : String
, code : String
}
-> (Result Error () -> msg)
-> Cmd msg

Login to an urbit without maintaining an active connection. This will allow for stateless scry and spider requests that require authentication but not others.

Note: This is not required if you have already used connect to authenticate.

{ url : String
, app : String
, path : String
, mark : String
, decoder : Decoder a
}
-> (Result Error a -> msg)
-> Cmd msg

Make a scry request.

{ url : String
, inputMark : String
, outputMark : String
, threadName : String
, body : Value
}
-> (Result Error Value -> msg)
-> Cmd msg

Start a thread and get the response.

Stateful Requests

{ ship : String
, app : String
, mark : String
, json : Value
, session : Session
}
-> (Result Error () -> msg)
-> ( Session, Cmd msg )

Poke an app on a ship.

{ ship : String
, app : String
, path : String
, session : Session
}
-> (Result Error () -> msg)
-> ( Session, Cmd msg )

Subscribe to ship events on some path.

unsubscribe : Int -> Session -> (Result Error () -> msg) -> ( Session, Cmd msg )

Unsubscribe from an existing subscription.

disconnect : Session -> (Result Error () -> msg) -> Cmd msg

Safely deletes a channel.

Helpers

genChannelId : Posix -> Generator String

Recommended way to create a unique id for a session's channel. Takes the current time and returns a random generator which can then be used to generate the full ID.