Files
bare-operating-system/packages/bare-os-protocol/lib/chat-messages.js
T
2026-04-21 20:05:57 -04:00

199 lines
5.3 KiB
JavaScript

import c from 'compact-encoding'
/** Wire schema for payload records inside message `event` (uint8 in frame). */
export const BARE_OS_CHAT_WIRE_SCHEMA_VERSION = 1
/** Event kinds for `evtKind`. */
export const BARE_OS_CHAT_EVT_TEXT = 1
export const BARE_OS_CHAT_EVT_PRESENCE = 2
/**
* @param {number} len
*/
function fixedBuffer(len) {
return {
preencode(state, m) {
void m
state.end += len
},
encode(state, m) {
const b =
m instanceof Uint8Array ? m : /** @type {Uint8Array} */ (new Uint8Array(m))
if (b.byteLength !== len) {
throw new Error(`fixed buffer: expected ${len} bytes`)
}
state.buffer.set(b, state.start)
state.start += len
},
decode(state) {
const out = state.buffer.subarray(state.start, state.start + len)
state.start += len
return out
}
}
}
const buf16 = fixedBuffer(16)
const buf32 = fixedBuffer(32)
/** Message 0: hello — version + chat capability flag. */
export const msgChatHelloEncoding = {
preencode(state, m) {
c.uint16.preencode(state, m.chatSchemaVersion)
c.bool.preencode(state, m.swarmChatCapability)
},
encode(state, m) {
c.uint16.encode(state, m.chatSchemaVersion)
c.bool.encode(state, !!m.swarmChatCapability)
},
decode(state) {
return {
chatSchemaVersion: c.uint16.decode(state),
swarmChatCapability: c.bool.decode(state)
}
}
}
/** Message 1: helloAck — negotiated limits. */
export const msgChatHelloAckEncoding = {
preencode(state, m) {
c.uint16.preencode(state, m.chatSchemaVersion)
c.uint32.preencode(state, m.maxPayloadBytes)
},
encode(state, m) {
c.uint16.encode(state, m.chatSchemaVersion)
c.uint32.encode(state, m.maxPayloadBytes >>> 0)
},
decode(state) {
return {
chatSchemaVersion: c.uint16.decode(state),
maxPayloadBytes: c.uint32.decode(state)
}
}
}
/** Message 2: join room. */
export const msgChatJoinEncoding = {
preencode(state, m) {
c.string.preencode(state, m.roomId)
c.string.preencode(state, m.displayName)
buf32.preencode(state, m.senderPk)
buf16.preencode(state, m.clientInstanceId)
},
encode(state, m) {
c.string.encode(state, m.roomId)
c.string.encode(state, m.displayName)
buf32.encode(state, m.senderPk)
buf16.encode(state, m.clientInstanceId)
},
decode(state) {
return {
roomId: c.string.decode(state),
displayName: c.string.decode(state),
senderPk: buf32.decode(state),
clientInstanceId: buf16.decode(state)
}
}
}
/** Message 3: leave room. */
export const msgChatLeaveEncoding = {
preencode(state, m) {
c.string.preencode(state, m.roomId)
},
encode(state, m) {
c.string.encode(state, m.roomId)
},
decode(state) {
return { roomId: c.string.decode(state) }
}
}
/** Optional detached signature (Ed25519, length-prefixed buffer). */
const optionalSigEncoding = {
preencode(state, m) {
const has = !!(m.sigDetached && m.sigDetached.byteLength > 0)
c.bool.preencode(state, has)
if (has) c.buffer.preencode(state, m.sigDetached)
},
encode(state, m) {
const has = !!(m.sigDetached && m.sigDetached.byteLength > 0)
c.bool.encode(state, has)
if (has) c.buffer.encode(state, m.sigDetached)
},
decode(state) {
const has = c.bool.decode(state)
return has ? c.buffer.decode(state) : null
}
}
/** Message 4: chat event (text, presence, gossip relay). */
export const msgChatEventEncoding = {
preencode(state, m) {
c.uint8.preencode(state, m.schemaVersion)
c.uint8.preencode(state, m.evtKind)
c.string.preencode(state, m.roomId)
buf16.preencode(state, m.evtId)
c.uint.preencode(state, m.tsMs)
buf32.preencode(state, m.senderPk)
c.string.preencode(state, m.displayName)
c.string.preencode(state, m.body)
c.uint8.preencode(state, m.ttl)
optionalSigEncoding.preencode(state, m)
},
encode(state, m) {
c.uint8.encode(state, m.schemaVersion)
c.uint8.encode(state, m.evtKind)
c.string.encode(state, m.roomId)
buf16.encode(state, m.evtId)
c.uint.encode(state, m.tsMs)
buf32.encode(state, m.senderPk)
c.string.encode(state, m.displayName)
c.string.encode(state, m.body)
c.uint8.encode(state, m.ttl)
optionalSigEncoding.encode(state, m)
},
decode(state) {
const schemaVersion = c.uint8.decode(state)
const evtKind = c.uint8.decode(state)
const roomId = c.string.decode(state)
const evtId = buf16.decode(state)
const tsMs = c.uint.decode(state)
const senderPk = buf32.decode(state)
const displayName = c.string.decode(state)
const body = c.string.decode(state)
const ttl = c.uint8.decode(state)
const sigDetached = optionalSigEncoding.decode(state)
return {
schemaVersion,
evtKind,
roomId,
evtId,
tsMs,
senderPk,
displayName,
body,
ttl,
sigDetached
}
}
}
/** Message 5: control stub (room list later). */
export const msgChatControlEncoding = {
preencode(state, m) {
c.string.preencode(state, m.op)
c.string.preencode(state, m.payloadJson)
},
encode(state, m) {
c.string.encode(state, m.op)
c.string.encode(state, m.payloadJson || '')
},
decode(state) {
return {
op: c.string.decode(state),
payloadJson: c.string.decode(state)
}
}
}