@gramforge/render
v0.1.0
Published
Pure renderers over the `@gramforge/core` RichText AST — the layer just above `core`. No I/O, no HTTP, no transport dependency; every function is a deterministic projection of a validated message and JSR-publishable.
Downloads
91
Readme
@gramforge/render
Pure renderers over the @gramforge/core RichText AST — the layer just above
core. No I/O, no HTTP, no transport dependency; every function is a
deterministic projection of a validated message and JSR-publishable.
Install
pnpm add @gramforge/render @gramforge/coreRenderers
| Function | Output | Notes |
| --- | --- | --- |
| toEntities(message) | { text, entities } | The default. offset/length in UTF-16 code units (Bot API semantics). |
| toHtml(message) | string | Telegram's supported HTML subset (<b> <i> <u> <s> <code> <pre> <blockquote> <a> <tg-spoiler>). |
| toMarkdownV2(message) | string | Render fallback only — see below. |
| toRichMessage(nodes, capability) | RenderRichMessageResult DU | Capability-flagged Bot API 10.1 block subset. |
Usage
import { bold, createMessageText, text } from "@gramforge/core";
import { toEntities } from "@gramforge/render";
const message = createMessageText([text("hello "), bold(text("world"))]);
if (message.kind === "ok") {
const payload = toEntities(message.value);
// payload.text === "hello world"
// payload.entities === [{ type: "bold", offset: 6, length: 5 }]
}toEntities is the recommended renderer: it never needs escaping and offsets are
UTF-16 code units (JS string.length), matching the unit Telegram's Bot API
uses for MessageEntity. Astral-plane characters (most emoji, CJK) count as two
code units, exactly as the Bot API expects.
toMarkdownV2 is a render fallback only
toMarkdownV2 exists purely as an interoperability escape hatch for
callers/transports that must speak MarkdownV2 wire text. It is not an
authoring surface: @gramforge/core never accepts a raw MarkdownV2 string as
input. You author against the typed RichText AST, where escaping is impossible by
construction; MarkdownV2 (with its fragile reserved-character escaping) is only
ever produced, never consumed. Prefer toEntities or toHtml.
toRichMessage is capability-flagged and partial
Bot API 10.1's structured blocks (tables, headings, collapsible sections, math)
landed with unverified client support, so toRichMessage requires an explicit
capability token and only lowers that narrow RichBlockNode subset:
import { heading, math } from "@gramforge/core";
import { toRichMessage } from "@gramforge/render";
const result = toRichMessage([heading(1, [/* ... */]), math("a^2+b^2")], {
kind: "rich-message-10-1"
});
// result.kind === "ok" | "unsupported-node"Any node kind outside the supported subset is reported as
{ kind: "unsupported-node"; nodeKind } — never silently dropped.
