Zum Inhalt springen

Dateien, Presence und Broadcast

const file = await db.$files.upload(blob, { path: "avatars/me.png" });
const bytes = await db.$files.download("avatars/me.png");
const all = await db.$files.list({ prefix: "avatars/" });
await db.$files.delete("avatars/me.png");

Dateibytes liegen im FILES-R2-Bucket, mit 25-MB-Limit und Lesezugriff für Members, Schreibzugriff für Editors. Der Index wird über das Transaktionslog geschrieben, sodass eine Wiederherstellung ihn behält.

Presence ist ephemer und fährt auf dem Workspace-Socket mit:

const room = db.$presence("board:" + boardId);
room.set({ name, color });
room.cursor({ x, y });
const stop = room.subscribe((peers) => render(peers));
// Beim Verlassen des Raums:
room.leave();

Ein Presence-Room bietet außerdem drag() und clearDrag() für Drag-State.

Broadcast ist eine Fire-and-forget-Nachricht zwischen den Clients eines Workspace, auf demselben Socket:

const board = db.$broadcast<{ kind: "ping"; at: number }>("board:" + boardId);
const stop = board.subscribe((message, from) => flash(from.userId, message));
board.send({ kind: "ping", at: Date.now() }); // false (verworfen), während du offline bist

Ein abgelehntes Topic-Abonnement meldet sich über den optionalen onError-Callback von subscribe. Eine Nachricht erreicht jeden anderen Client, der auf dem Topic lauscht, mit Client-ID und User-ID des Senders; deine eigenen Nachrichten werden nicht zurückgespiegelt. Nichts wird gespeichert oder erneut abgespielt, und ein Send bei getrennter Verbindung wird verworfen statt später zugestellt. Lauschende Clients treten ihren Topics nach einem Reconnect wieder bei.

Standardmäßig darf jedes Mitglied des Workspace lauschen und senden. Entscheide pro Topic im Worker:

import { defineTapeDB, WORKSPACE_ROLE_LEVELS } from "tapedb/worker";
import schema from "./tapedb.schema";
defineTapeDB({
schema,
topics: {
authorize: ({ auth, topic, action }) =>
!topic.startsWith("admin:") || auth.role >= WORKSPACE_ROLE_LEVELS.ADMIN,
},
});

React-Bindings: usePresence und useBroadcast in React.