63 lines · 2.4 KB
import * as decoding from "lib0/decoding";
import * as encoding from "lib0/encoding";
import { MESSAGE_AWARENESS } from "~/lib/editor-protocol";

/**
 * y-protocols awareness updates, framed without a server-side Awareness instance. The room only relays
 * and caches them, because Awareness keeps a timer running that would stop the Durable Object hibernating.
 */
export type AwarenessEntry = { clientId: number; clock: number; state: unknown };

export function decodeAwareness(update: Uint8Array): AwarenessEntry[] {
  const decoder = decoding.createDecoder(update);
  const count = decoding.readVarUint(decoder);
  const entries: AwarenessEntry[] = [];
  for (let index = 0; index < count; index++) {
    const clientId = decoding.readVarUint(decoder);
    const clock = decoding.readVarUint(decoder);
    entries.push({ clientId, clock, state: JSON.parse(decoding.readVarString(decoder)) });
  }
  return entries;
}

export function encodeAwareness(entries: AwarenessEntry[]): Uint8Array {
  const encoder = encoding.createEncoder();
  encoding.writeVarUint(encoder, entries.length);
  for (const entry of entries) {
    encoding.writeVarUint(encoder, entry.clientId);
    encoding.writeVarUint(encoder, entry.clock);
    encoding.writeVarString(encoder, JSON.stringify(entry.state));
  }
  return encoding.toUint8Array(encoder);
}

/** Wraps an awareness update in the room's binary frame. */
export function awarenessFrame(update: Uint8Array): Uint8Array {
  const encoder = encoding.createEncoder();
  encoding.writeVarUint(encoder, MESSAGE_AWARENESS);
  encoding.writeVarUint8Array(encoder, update);
  return encoding.toUint8Array(encoder);
}