Skip to content

Instantly share code, notes, and snippets.

@HerbCaudill
Last active June 10, 2023 11:32
Show Gist options
  • Save HerbCaudill/8f6b36acf20ed0477b2dda2d86a6793c to your computer and use it in GitHub Desktop.
Save HerbCaudill/8f6b36acf20ed0477b2dda2d86a6793c to your computer and use it in GitHub Desktop.
automerge-repo message types

Proposed message types

type SyncMessage = {
  type: 'SYNC_MESSAGE'
  senderId: PeerId
  recipientId: PeerId
  documentId: DocumentId
  payload: Uint8Array // Automerge binary sync message
}

type EphemeralMessage = {
  type: 'EPHEMERAL_MESSAGE'
  senderId: PeerId
  documentId: DocumentId
  payload: unknown // up to the application
}

// eventually

type AuthMessage = {
  type: 'AUTH_MESSAGE'
  senderId: PeerId
  recipientId: PeerId
  shareId: ShareId
  payload: unknown // up to the application
}

type Message = SyncMessage | EphemeralMessage | AuthMessage

Existing message-related types

Messages might contain the following properties:

peerId might be the ID of the sender or the recipient

targetId ID of the recipient (or * for broadcast messages)

senderId ID of the sender

channelId can mean a few different things:

  • DocumentId for sync messages
  • m/ + DocumentId for ephemeral messages
  • a/ + TeamId for auth messages
  • constant SYNC_CHANNEL - I don't think this is used?

message is a byte array containing the automerge sync message

broadcast boolean

  • ephemeral messages are always broadcast
  • arrive messages in a BroadcastChannelNetworkAdapter are broadcast

Note: arrive, welcome, and join message types are specific to various network adapters and should be defined locally in those packages.

NetworkAdapter.ts

class NetworkAdapter {
  sendMessage(
    peerId: PeerId, //
    channelId: ChannelId,
    message: Uint8Array,
    broadcast: boolean
  ): void {}
}

// events & payloads

interface PeerCandidatePayload {
  peerId: PeerId
  channelId: ChannelId
}

interface MessagePayload {
  targetId: PeerId
  channelId: ChannelId
  message: Uint8Array
  broadcast: boolean
}

interface InboundMessagePayload extends MessagePayload {
  type?: string
  senderId: PeerId
}

NetworkSubsystem.ts

class NetworkSubsystem {
  sendMessage(
    peerId: PeerId, //
    channelId: ChannelId,
    message: Uint8Array,
    broadcast: boolean
  ) {}
}

// events & payloads

interface PeerPayload {
  peerId: PeerId
  channelId: ChannelId
}

DocSynchronizer.ts

// in #sendSyncMessage
this.emit('message', {
  targetId: peerId,
  channelId, // here the channelId represents the documentId
  message,
  broadcast: false,
})

EphemeralData.ts

class EphemeralData {
  broadcast(
    channelId: ChannelId, //
    message: unknown
  ) {
    this.emit('message', {
      targetId: '*' as PeerId, // TODO: we don't really need a targetId for broadcast
      channelId: ('m/' + channelId) as ChannelId,
      message: messageBytes,
      broadcast: true,
    })
  }

  receive(
    senderId: PeerId, //
    grossChannelId: ChannelId,
    message: Uint8Array
  ) {
    this.emit('data', {
      peerId: senderId,
      channelId,
      data,
    })
  }
}

// WHAT IN GOD'S NAME
export interface EphemeralDataPayload {
  channelId: ChannelId
  peerId: PeerId
  data: {
    peerId: PeerId
    channelId: ChannelId
    data: unknown
  }
}

DocHandle.ts

// not used anywhere
interface DocHandleMessagePayload {
  destinationId: PeerId
  channelId: ChannelId
  data: Uint8Array
}

automerge-repo-network-broadcastchannel

class BroadcastChannelNetworkAdapter {
  connect(peerId: PeerId) {
    // ...
    this.#broadcastChannel.postMessage({
      senderId: this.peerId,
      targetId: senderId,
      type: 'welcome',
    })

    // ...
    this.emit('message', {
      senderId,
      targetId,
      channelId,
      message: new Uint8Array(message),
      broadcast,
    })
  }

  sendMessage(peerId: PeerId, channelId: ChannelId, uint8message: Uint8Array, broadcast: boolean) {
    const message = uint8message.buffer.slice(
      uint8message.byteOffset,
      uint8message.byteOffset + uint8message.byteLength
    )
    this.#broadcastChannel.postMessage({
      senderId: this.peerId,
      targetId: peerId,
      type: 'message',
      channelId,
      message,
      broadcast,
    })
  }

  join(joinChannelId: ChannelId) {
    this.#broadcastChannel.postMessage({
      senderId: this.peerId,
      channelId: joinChannelId,
      type: 'arrive',
      broadcast: true,
    })
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment