ChannelTransport
The channel contract: what an integration that gives channels declares, as the harness and sandbox contracts declare theirs. A channel transport receives the events an external system sends, each placing a message in an external conversation, such as a Slack thread or an email thread, and posts messages as replies in such a conversation, saying what the system returned for them. It can edit or delete a posted message when the system can, and it knows its own identity there, so that what it posted is not received as new.
The transport identifies conversations, messages, and senders by the system’s own ids and keeps
what else it needs of a conversation in its details; amends keeps those as they are and gives
them back when it posts there.
Since v0.1.0
Errors
Section titled “Errors”RefusedError (class)
Section titled “RefusedError (class)”The system refused what it was asked for good, as for a conversation that is gone.
Signature
declare class RefusedErrorSince v0.1.0
RejectedError (class)
Section titled “RejectedError (class)”The transport rejected an inbound request that is not the system’s, or whose signature does not hold, or that it cannot read; the system should be answered so.
Signature
declare class RejectedErrorSince v0.1.0
TransportError (class)
Section titled “TransportError (class)”The error a transport fails with: why, and what the system said as its cause. Only an
unavailable system is worth asking again.
Signature
declare class TransportErrorSince v0.1.0
TransportErrorReason
Section titled “TransportErrorReason”Why the transport could not do what was asked: one of the errors above, which
TransportError.reason holds.
Signature
declare const TransportErrorReason: Schema.toTaggedUnion< "_tag", readonly [typeof RejectedError, typeof RefusedError, typeof UnavailableError]>Since v0.1.0
TransportErrorReason (type alias)
Section titled “TransportErrorReason (type alias)”Signature
type TransportErrorReason = typeof TransportErrorReason.TypeSince v0.1.0
UnavailableError (class)
Section titled “UnavailableError (class)”The system could not be reached now, which may pass.
Signature
declare class UnavailableErrorSince v0.1.0
Models
Section titled “Models”ChannelTransport (interface)
Section titled “ChannelTransport (interface)”A channel’s transport: how a connection to the system receives events and posts there.
Example (A transport for a chat system that sends each message to a webhook)
import { ChannelTransport } from "@amends-dev/channel"import { DateTime, Effect, Schema } from "effect"
// What the system's webhook sends: one message in a room, as JSON.const Webhook = Schema.fromJsonString( Schema.Struct({ room: Schema.String, id: Schema.String, from: Schema.String, text: Schema.String }))
// The system's API, which answers a post with the new message's id.declare const sendToRoom: (room: string, text: string) => Effect.Effect<string, Error>
const failWith = (reason: ChannelTransport.TransportErrorReason) => (cause: unknown) => new ChannelTransport.TransportError({ reason, cause })
export const transport: ChannelTransport.ChannelTransport = { self: undefined, // A real transport first checks the signature the system puts in `inbound.headers`. receive: (inbound) => Effect.gen(function* () { const message = yield* Schema.decodeUnknownEffect(Webhook)(inbound.body).pipe( Effect.mapError(failWith(new ChannelTransport.RejectedError({}))) ) const event = ChannelTransport.Event.cases.MessageReceived.make({ conversation: { id: message.room, details: {} }, messageId: message.id, sender: { id: message.from, name: message.from }, text: message.text, receivedAt: yield* DateTime.now }) return { events: [event] } }), post: (conversation, outgoing) => sendToRoom(conversation.id, `${outgoing.author}: ${outgoing.text}`).pipe( Effect.map((messageId) => ({ messageId })), Effect.mapError(failWith(new ChannelTransport.UnavailableError({}))) )}Signature
export interface ChannelTransport { /** * The transport's own identity in the system, such as a bot user's id, when it has one. What * this identity sends is the transport's own post, received back, not a message to take in. */ readonly self: ExternalId | undefined
/** The events in a request the system sent, after checking that it is the system's. */ readonly receive: (inbound: Inbound) => Effect.Effect<Received, TransportError>
/** Post `outgoing` as a reply in `conversation`. */ readonly post: (conversation: Conversation, outgoing: Outgoing) => Effect.Effect<Posted, TransportError>
/** Replace the text of the message `messageId` it posted in `conversation`, when the system can. */ readonly edit?: ( conversation: Conversation, messageId: ExternalId, outgoing: Outgoing ) => Effect.Effect<void, TransportError>
/** Delete the message `messageId` it posted in `conversation`, when the system can. */ readonly delete?: (conversation: Conversation, messageId: ExternalId) => Effect.Effect<void, TransportError>}Since v0.1.0
Schemas
Section titled “Schemas”Conversation
Section titled “Conversation”An external conversation: its id, and what the transport needs to post there besides it, such as the Slack channel a thread is in, which amends keeps and gives back as it is.
Signature
declare const Conversation: Schema.Struct<{ readonly id: Schema.NonEmptyString readonly details: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>}>Since v0.1.0
Conversation (interface)
Section titled “Conversation (interface)”Signature
export interface Conversation extends Schema.Schema.Type<typeof Conversation> {}Since v0.1.0
One event the external system sent: a message received in a conversation, with the title the system gives the conversation when it has one, such as an email’s subject; a message edited; or a message deleted.
Signature
declare const Event: Schema.TaggedUnion<{ readonly MessageReceived: Schema.TaggedStruct< "MessageReceived", { readonly conversation: Schema.Struct<{ readonly id: Schema.NonEmptyString readonly details: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>> }> readonly messageId: Schema.NonEmptyString readonly sender: Schema.Struct<{ readonly id: Schema.NonEmptyString; readonly name: Schema.NonEmptyString }> readonly text: Schema.String readonly title: Schema.optionalKey<Schema.String> readonly receivedAt: Schema.DateTimeUtcFromString } > readonly MessageEdited: Schema.TaggedStruct< "MessageEdited", { readonly conversation: Schema.Struct<{ readonly id: Schema.NonEmptyString readonly details: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>> }> readonly messageId: Schema.NonEmptyString readonly sender: Schema.Struct<{ readonly id: Schema.NonEmptyString; readonly name: Schema.NonEmptyString }> readonly text: Schema.String readonly editedAt: Schema.DateTimeUtcFromString } > readonly MessageDeleted: Schema.TaggedStruct< "MessageDeleted", { readonly conversation: Schema.Struct<{ readonly id: Schema.NonEmptyString readonly details: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>> }> readonly messageId: Schema.NonEmptyString readonly sender: Schema.Struct<{ readonly id: Schema.NonEmptyString; readonly name: Schema.NonEmptyString }> readonly deletedAt: Schema.DateTimeUtcFromString } >}>Since v0.1.0
Event (type alias)
Section titled “Event (type alias)”Signature
type Event = typeof Event.TypeSince v0.1.0
ExternalId
Section titled “ExternalId”An identifier of the external system’s own: of a conversation, a message, or a user there.
Signature
declare const ExternalId: Schema.NonEmptyStringSince v0.1.0
ExternalId (type alias)
Section titled “ExternalId (type alias)”Signature
type ExternalId = typeof ExternalId.TypeSince v0.1.0
Inbound
Section titled “Inbound”What the external system sent a host, as the host received it: the request’s headers and body.
Signature
declare const Inbound: Schema.Struct<{ readonly headers: Schema.$Record<Schema.String, Schema.String> readonly body: Schema.String}>Since v0.1.0
Inbound (interface)
Section titled “Inbound (interface)”Signature
export interface Inbound extends Schema.Schema.Type<typeof Inbound> {}Since v0.1.0
Outgoing
Section titled “Outgoing”A message to post in an external conversation: its text, and what its author is called.
Signature
declare const Outgoing: Schema.Struct<{ readonly text: Schema.String; readonly author: Schema.NonEmptyString }>Since v0.1.0
Outgoing (interface)
Section titled “Outgoing (interface)”Signature
export interface Outgoing extends Schema.Schema.Type<typeof Outgoing> {}Since v0.1.0
Posted
Section titled “Posted”What the external system returned for a posted message: the id it gave it.
Signature
declare const Posted: Schema.Struct<{ readonly messageId: Schema.NonEmptyString }>Since v0.1.0
Posted (interface)
Section titled “Posted (interface)”Signature
export interface Posted extends Schema.Schema.Type<typeof Posted> {}Since v0.1.0
Received
Section titled “Received”What the transport made of an inbound request: the events in it, none for a request that carries no message, and what to answer the system with when it expects an answer, as Slack’s URL verification does.
Signature
declare const Received: Schema.Struct<{ readonly events: Schema.$Array< Schema.TaggedUnion<{ readonly MessageReceived: Schema.TaggedStruct< "MessageReceived", { readonly conversation: Schema.Struct<{ readonly id: Schema.NonEmptyString readonly details: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>> }> readonly messageId: Schema.NonEmptyString readonly sender: Schema.Struct<{ readonly id: Schema.NonEmptyString; readonly name: Schema.NonEmptyString }> readonly text: Schema.String readonly title: Schema.optionalKey<Schema.String> readonly receivedAt: Schema.DateTimeUtcFromString } > readonly MessageEdited: Schema.TaggedStruct< "MessageEdited", { readonly conversation: Schema.Struct<{ readonly id: Schema.NonEmptyString readonly details: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>> }> readonly messageId: Schema.NonEmptyString readonly sender: Schema.Struct<{ readonly id: Schema.NonEmptyString; readonly name: Schema.NonEmptyString }> readonly text: Schema.String readonly editedAt: Schema.DateTimeUtcFromString } > readonly MessageDeleted: Schema.TaggedStruct< "MessageDeleted", { readonly conversation: Schema.Struct<{ readonly id: Schema.NonEmptyString readonly details: Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>> }> readonly messageId: Schema.NonEmptyString readonly sender: Schema.Struct<{ readonly id: Schema.NonEmptyString; readonly name: Schema.NonEmptyString }> readonly deletedAt: Schema.DateTimeUtcFromString } > }> > readonly answer: Schema.optionalKey<Schema.String>}>Since v0.1.0
Received (interface)
Section titled “Received (interface)”Signature
export interface Received extends Schema.Schema.Type<typeof Received> {}Since v0.1.0
Sender
Section titled “Sender”Who sent a message in the external system: their id there, and what they are called.
Signature
declare const Sender: Schema.Struct<{ readonly id: Schema.NonEmptyString; readonly name: Schema.NonEmptyString }>Since v0.1.0
Sender (interface)
Section titled “Sender (interface)”Signature
export interface Sender extends Schema.Schema.Type<typeof Sender> {}Since v0.1.0