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
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 messagesm/ + DocumentId
for ephemeral messagesa/ + 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.
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
}
class NetworkSubsystem {
sendMessage(
peerId: PeerId, //
channelId: ChannelId,
message: Uint8Array,
broadcast: boolean
) {}
}
// events & payloads
interface PeerPayload {
peerId: PeerId
channelId: ChannelId
}
// in #sendSyncMessage
this.emit('message', {
targetId: peerId,
channelId, // here the channelId represents the documentId
message,
broadcast: false,
})
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
}
}
// not used anywhere
interface DocHandleMessagePayload {
destinationId: PeerId
channelId: ChannelId
data: Uint8Array
}
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,
})
}
}