Files
mdg-filius/packages/server/src/simChannel.ts
T
julian.kiedaischandClaude Opus 5.5 4b33e36580 feat: F5.5 large binary files as blobs on server F
From 16 KB a picture's or binary file's entry names its bytes by their
SHA-256 instead of holding them; the bytes are on F, content-addressed
(GET/POST /blobs with the session's grant, from the editor's origin, at
most 16 MiB). A document's blob side says where they are: the editor's
fetches and uploads, the room thread's asks F's main thread, and a start
waits for the ones asked for; without a side everything stays in the
document. Text stays Y.Text. A blob whose bytes are not there is never
written back empty. A blob F has not got yet is asked for again a few
times, and a running room reads late bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 16:46:50 +00:00

41 lines
2.6 KiB
TypeScript

// The messages between server F's main thread and a thread that holds
// simulation rooms (plan §8.2: rooms in worker_threads, so that a large
// network does not hold up the shared documents). A thread holds several
// rooms, each with a replica of its project's document: the document's
// updates go both ways, a person's messages go in, the room's come out,
// each for one person (a room tells its people apart; the main thread
// numbers them). A room's large binary files (blobs) are F's: the thread
// asks for the ones it has not got, and gives F the ones the simulation
// makes before the document's change that names them.
import type { HostMessage } from '@mdg-filius/protocol';
/** How often, and how long apart, a thread asks again for a blob F has not got (yet). */
export const BLOB_ASKS = 5;
export const BLOB_ASK_AGAIN_MILLISECONDS = 1_000;
export type ToRoomThread =
/** A room for a project, its document as it is now. */
| { readonly type: 'open'; readonly room: string; readonly state: Uint8Array }
| { readonly type: 'doc'; readonly room: string; readonly update: Uint8Array }
/** A person in the room, by name. */
| { readonly type: 'connect'; readonly room: string; readonly client: number; readonly name: string }
| { readonly type: 'client'; readonly room: string; readonly client: number; readonly message: unknown }
| { readonly type: 'disconnect'; readonly room: string; readonly client: number }
/** The simulation stopped (what it left written back first); answered by `stopped`. */
| { readonly type: 'stop'; readonly room: string }
/** The room ended — stopped first with `writeBack`, else as it is (its project is gone); answered by `closed`. */
| { readonly type: 'close'; readonly room: string; readonly writeBack: boolean }
/** The blobs asked for: those F has, and the names of those it has not. */
| { readonly type: 'blobs'; readonly room: string; readonly blobs: readonly { readonly sha: string; readonly bytes: Uint8Array }[]; readonly missing: readonly string[] };
export type FromRoomThread =
| { readonly type: 'doc'; readonly room: string; readonly update: Uint8Array }
| { readonly type: 'host'; readonly room: string; readonly client: number; readonly message: HostMessage }
| { readonly type: 'stopped'; readonly room: string }
/** Blobs the document names that the thread has not got. */
| { readonly type: 'needBlobs'; readonly room: string; readonly shas: readonly string[] }
/** A blob the simulation made (before the change that names it). */
| { readonly type: 'blob'; readonly room: string; readonly bytes: Uint8Array }
| { readonly type: 'closed'; readonly room: string };