Rebasely Beta: data may be purged

examples/live-notes Public

TypeScript · 41 lines · 1.7 KiB · at 359d966

// The messages editors and a note's room exchange over the WebSocket, as JSON text frames.// Every version is the room's: the number of ops it has applied to the note.import type { Op } from "./ops.ts";// An edit, written against the note as it was at version `base`.export type ClientMessage = { type: "op"; base: number; op: Op };export type ServerMessage =  // The whole note: on connect, and whenever an editor's view can't be repaired.  | { type: "snapshot"; text: string; version: number }  // Your op was applied (or turned out to change nothing) and the note is now at `version`.  | { type: "ack"; version: number }  // Someone else's op, already transformed, which took the note to `version`.  | { type: "op"; version: number; op: Op }  | { type: "error"; message: string };// Parses and checks a frame from an editor. Returns null for anything malformed; the room// never trusts the shape of what arrives on a socket.export function parseClientMessage(raw: string): ClientMessage | null {  let m: unknown;  try {    m = JSON.parse(raw);  } catch {    return null;  }  if (!isObject(m) || m.type !== "op" || !isCount(m.base) || !isObject(m.op)) return null;  const op = m.op;  if (!isCount(op.pos)) return null;  if (op.type === "insert" && typeof op.text === "string") return { type: "op", base: m.base, op: { type: "insert", pos: op.pos, text: op.text } };  if (op.type === "delete" && isCount(op.count)) return { type: "op", base: m.base, op: { type: "delete", pos: op.pos, count: op.count } };  return null;}function isObject(v: unknown): v is Record<string, unknown> {  return typeof v === "object" && v !== null && !Array.isArray(v);}function isCount(v: unknown): v is number {  return Number.isSafeInteger(v) && (v as number) >= 0;}