Tactical Media Broadcast Hub — initial release
Self-hosted multi-platform broadcast newsroom for activist collectives: publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style column wall with hostile-content-safe rendering, live control room with Auto Director, bridged chat governance with polls and quorum, distributed sandboxed transcode workers, encrypted content-addressed archive, monthly analytics. MIT licensed. See README.md and docs/.
This commit is contained in:
commit
aa5e262f3a
880 changed files with 111042 additions and 0 deletions
59
packages/safe-content/src/ast.ts
Normal file
59
packages/safe-content/src/ast.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* Closed rich-text AST for provider content (proposal §4.5 "rich content
|
||||
* contract"). Nothing outside these node types can be rendered: there is no
|
||||
* node that carries raw HTML, attributes, styles or arbitrary URLs.
|
||||
*
|
||||
* URL-bearing fields are only ever produced by `parseSafeHttpUrl` /
|
||||
* `toProxiedMediaUrl`, and the renderer re-validates them, so an AST that was
|
||||
* round-tripped through storage or the network cannot smuggle a bad scheme.
|
||||
*/
|
||||
|
||||
export const RICH_TEXT_VERSION = 1 as const;
|
||||
|
||||
export type TextNode = { readonly type: "text"; readonly text: string };
|
||||
export type LineBreakNode = { readonly type: "linebreak" };
|
||||
/** Custom emoji / emote. `src` is always a media-proxy URL, never the origin URL. */
|
||||
export type EmojiNode = { readonly type: "emoji"; readonly shortcode: string; readonly src: string };
|
||||
export type LinkNode = {
|
||||
readonly type: "link";
|
||||
/** Absolute http(s) URL, credential-free. */
|
||||
readonly href: string;
|
||||
/** ASCII (punycode) host of `href`, shown to the operator next to the label. */
|
||||
readonly host: string;
|
||||
readonly children: readonly (TextNode | EmojiNode)[];
|
||||
};
|
||||
export type MentionNode = {
|
||||
readonly type: "mention";
|
||||
/** Handle without the leading "@", e.g. `alice` or `alice@example.social`. */
|
||||
readonly handle: string;
|
||||
readonly href?: string;
|
||||
};
|
||||
export type HashtagNode = {
|
||||
readonly type: "hashtag";
|
||||
/** Tag without the leading "#". */
|
||||
readonly tag: string;
|
||||
readonly href?: string;
|
||||
};
|
||||
|
||||
export type InlineNode = TextNode | LineBreakNode | EmojiNode | LinkNode | MentionNode | HashtagNode;
|
||||
|
||||
export type ParagraphNode = { readonly type: "paragraph"; readonly children: readonly InlineNode[] };
|
||||
|
||||
export type RichTextDocument = {
|
||||
readonly type: "doc";
|
||||
readonly version: typeof RICH_TEXT_VERSION;
|
||||
readonly children: readonly ParagraphNode[];
|
||||
/** True when input exceeded a length/node/depth limit and was cut. */
|
||||
readonly truncated: boolean;
|
||||
};
|
||||
|
||||
export type LinkPreview = {
|
||||
readonly type: "linkPreview";
|
||||
readonly href: string;
|
||||
readonly host: string;
|
||||
readonly title?: string;
|
||||
readonly description?: string;
|
||||
readonly siteName?: string;
|
||||
/** Media-proxy URL for the preview image. */
|
||||
readonly image?: string;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue