Gramforge

Gramforge API reference

Exports of @gramforge/core. Back to the module

Functions

blockquote

function
blockquote(...children: readonly Inline[]): BlockquoteNode

bold

function
bold(...children: readonly Inline[]): BoldNode

code

function
code(value: string): CodeNode

expandableBlockquote

function
expandableBlockquote(...children: readonly Inline[]): ExpandableBlockquoteNode

gram

function
gram(strings: TemplateStringsArray, ...values: readonly GramValue[]): RichTextNode[]

italic

function
italic(...children: readonly Inline[]): ItalicNode

mentionUser

function
mentionUser(userId: number, label: string): MentionNode

mentionUsername

function
mentionUsername(username: string): MentionNode

pre

function
pre(value: string, language?: PreLanguage): PreNode

spoiler

function
spoiler(...children: readonly Inline[]): SpoilerNode

strikethrough

function
strikethrough(...children: readonly Inline[]): StrikethroughNode

text

function
text(value: string): TextNode

underline

function
underline(...children: readonly Inline[]): UnderlineNode

defineCallbackAction

function

Validate `actionId` at call time and return an explicitly-typed def. An invalid `actionId` is a programmer bug (a hardcoded literal that violates the `/^[a-z0-9]{1,8}$/` budget), not an expected runtime path, so this throws a descriptive `Error` rather than returning a result DU.

<TPayload extends Record<string, unknown>>(input: { actionId: string; payloadSchema: z.ZodType<TPayload>; fields: readonly (keyof TPayload & string)[]; }): CallbackActionDef<TPayload>

decodeCallbackData

function

Decode a `callback_data` string against a namespace. Generic `TPayload` is the caller's asserted payload type; the actual shape is enforced by the matched action's zod schema, and the validated result is branded to `TPayload` only after a successful `safeParse`.

<TPayload, TActions extends Record<string, AnyCallbackActionDef> = Record<string, AnyCallbackActionDef>>(callbackData: string, namespace: CallbackNamespace<TActions>, deps: CallbackDecodeDeps): Promise<CallbackDecodeResult<TPayload>>

encodeCallbackAction

function

Encode via a namespace + action key. The key is narrowed to the namespace's declared action names (a bogus key is a compile-time error, not a runtime one); payload is a free `TPayload` here — `callbackButton` is the entry point that ties payload shape to the chosen action.

<TPayload extends Record<string, unknown>, TActions extends Record<string, AnyCallbackActionDef>>(namespace: CallbackNamespace<TActions>, actionKey: keyof TActions & string, payload: TPayload, deps: CallbackEncodeDeps): Promise<string>

callbackButton

function

Build one inline button for a bound action + typed payload. `payload` is statically tied to the action's declared payload shape, so a missing/extra field is a compile-time error.

<TPayload extends Record<string, unknown>>(text: string, action: BoundCallbackAction<TPayload>, payload: TPayload, deps: CallbackEncodeDeps): Promise<InlineKeyboardButton>

inlineKeyboard

function

Shape a list of rows into an `InlineKeyboardMarkup`.

inlineKeyboard(rows: readonly (readonly InlineKeyboardButton[])[]): InlineKeyboardMarkup

keyboardRow

function

Shape a list of buttons into a single keyboard row.

keyboardRow(buttons: readonly InlineKeyboardButton[]): readonly InlineKeyboardButton[]

defineCallbackNamespace

function

Validate `namespaceId` at call time (programmer bug on failure → throw) and bind every action to the namespace id.

<TActions extends Record<string, AnyCallbackActionDef>>(input: { namespaceId: string; actions: TActions; }): CallbackNamespace<TActions>

createInMemoryPointerStore

function

A non-durable, non-evicting `PointerStore` backed by a `Map`. TEST-ONLY: it holds envelopes in process memory with no persistence, no eviction, and no cross-process visibility. Do NOT use in production — decode must survive a process restart, which this store does not.

createInMemoryPointerStore(): PointerStore

toChatId

function
toChatId(value: number): IdResult<ChatId>

toMessageId

function
toMessageId(value: number): IdResult<MessageId>

nodeText

function

The rendered plain text of a single node (its leaves concatenated in order).

nodeText(node: RichTextNode): string

renderedLength

function

UTF-16 code-unit length of the rendered text (the unit Telegram counts).

renderedLength(nodes: readonly RichTextNode[]): number

renderPlainText

function

The rendered plain text of a node sequence.

renderPlainText(nodes: readonly RichTextNode[]): string

resetDiagnostics

function

Test-only: clear the dedup set between tests.

resetDiagnostics(): void

setDiagnosticHook

function
setDiagnosticHook(fn: ((e: DiagnosticEvent) => void) | null): void

warnDegraded

function
warnDegraded(context: string, detail: string): void

warnNotImplemented

function
warnNotImplemented(category: string, detail: string): void

warnUnexpected

function
warnUnexpected(context: string, detail: string, data?: Record<string, unknown>): void

createCaptionText

function
createCaptionText(nodes: readonly RichTextNode[]): CaptionTextResult

createMessageText

function
createMessageText(nodes: readonly RichTextNode[]): MessageTextResult

messageNodes

function

Read the validated nodes back out of a branded message/caption value.

messageNodes(message: MessageText | CaptionText): readonly RichTextNode[]

messageText

function

Read the rendered plain text back out of a branded message/caption value.

messageText(message: MessageText | CaptionText): string

collapsible

function
collapsible(summary: readonly RichTextNode[], children: readonly RichBlockNode[]): CollapsibleBlock

heading

function
heading(level: 1 | 2 | 3 | 4, children: readonly RichTextNode[]): HeadingBlock

math

function
math(latex: string): MathBlock

table

function
table(header: readonly (readonly RichTextNode[])[], rows: readonly (readonly (readonly RichTextNode[])[])[]): TableBlock

parseRichText

function
parseRichText(value: unknown): ParseRichTextResult

Types

GramValue

type

A value interpolable into a `gram` template.

type GramValue = RichTextNode | readonly RichTextNode[] | string

Inline

type

A child accepted by the builders: a node, or a bare string (wrapped as text).

type Inline = RichTextNode | string

Brand

type
type Brand = Brand<T, B>

CallbackActionDef

type

A declared callback action: id + payload schema + positional field order.

type CallbackActionDef = CallbackActionDef<TPayload>

CallbackDecodeDeps

type

Dependencies a decode call needs. `hmacSecrets` is a `[current, ...previous]` list verified in order so a caller can grace-period a secret rotation; passing just `[current]` is the simpler valid default.

type CallbackDecodeDeps = {
  readonly pointerStore: PointerStore;
  readonly hmacSecrets: readonly Uint8Array[];
  readonly now: () => number;
}

CallbackDecodeResult

type

The six mutually-exclusive outcomes of decoding a `callback_data` string.

type CallbackDecodeResult = CallbackDecodeResult<TPayload>

CallbackEncodeDeps

type

Dependencies an encode call needs: store + signing secret + clock + TTL.

type CallbackEncodeDeps = {
  readonly pointerStore: PointerStore;
  readonly hmacSecret: Uint8Array;
  readonly now: () => number;
  readonly ttlMs: number;
}

CallbackActionId

type

A short (1-8 char) action id, part of the compact wire format.

type CallbackActionId = string & z.BRAND<"CallbackActionId">

CallbackNamespaceId

type

A short (1-4 char) namespace id, part of the compact wire format.

type CallbackNamespaceId = string & z.BRAND<"CallbackNamespaceId">

CallbackToken

type

A fixed 22-char base64url (128-bit) pointer-store token.

type CallbackToken = string & z.BRAND<"CallbackToken">

InlineKeyboardButton

type

The minimal Bot API inline button shape `core` produces.

type InlineKeyboardButton = {
  readonly text: string;
  readonly callback_data: string;
}

InlineKeyboardMarkup

type

A grid of button rows, ready to drop into `reply_markup`.

type InlineKeyboardMarkup = {
  readonly inline_keyboard: readonly (readonly InlineKeyboardButton[])[];
}

BoundCallbackAction

type

An action bound to its namespace — self-contained for encoding.

type BoundCallbackAction = BoundCallbackAction<TPayload>

CallbackNamespace

type

A compile-time registry of bound actions keyed by declaration name.

type CallbackNamespace = CallbackNamespace<TActions>

PointerEnvelope

type

The signed-at-rest record a pointer token resolves to. `notAfter` is embedded here (not on the wire) and checked post-lookup so a still-resolvable token for a button that should be considered dead decodes as `expired`, not `ok`.

type PointerEnvelope = {
  readonly namespaceId: CallbackNamespaceId;
  readonly actionId: CallbackActionId;
  /** `JSON.stringify` of the zod-validated payload. */
  readonly payloadJson: string;
  /** Epoch ms; caller-chosen TTL horizon. */
  readonly notAfter: number;
}

PointerStore

type

Caller-supplied storage for spillover envelopes. `put` overwrite is a caller bug (tokens are 128-bit random, collision is cryptographically negligible), not something the codec guards against.

type PointerStore = {
  put(token: CallbackToken, envelope: PointerEnvelope): Promise<void>;
  get(token: CallbackToken): Promise<PointerEnvelope | undefined>;
}

ChatId

type

A Telegram chat identifier (may be negative for groups/channels).

type ChatId = Brand<number, "ChatId">

IdResult

type
type IdResult = IdResult<T>

MessageId

type

A Telegram message identifier within a chat.

type MessageId = Brand<number, "MessageId">

DiagnosticKind

type

Runtime diagnostics — the house alternative to deferred-work comments. Three functions: `warnNotImplemented`, `warnDegraded`, `warnUnexpected`. Each fires once per stable key (except `warnUnexpected`, which always fires), is greppable, and can be piped into a UI panel via {@link setDiagnosticHook}. Usage: warnDegraded("MyModule.fallback", "linear interp — cubic deferred, spec:120-140"); warnNotImplemented("MyModule.featureX", "spec §4 not yet lowered"); warnUnexpected("MyModule.parse", "expected number, got string", { value });

type DiagnosticKind = "not-implemented" | "degraded" | "unexpected"

CaptionTextResult

type
type CaptionTextResult = | { kind: "ok"; value: CaptionText }
  | { kind: "too-long"; limit: typeof CAPTION_TEXT_LIMIT; actual: number }

MessageTextResult

type
type MessageTextResult = | { kind: "ok"; value: MessageText }
  | { kind: "too-long"; limit: typeof MESSAGE_TEXT_LIMIT; actual: number }

RichBlockNode

type
type RichBlockNode = HeadingBlock | MathBlock | TableBlock | CollapsibleBlock

BlockquoteChild

type

Nodes permitted directly inside a blockquote (excludes both blockquote variants).

type BlockquoteChild = | TextNode
  | CodeNode
  | PreNode
  | BoldNode
  | ItalicNode
  | UnderlineNode
  | StrikethroughNode
  | SpoilerNode
  | LinkNode
  | MentionNode

MentionTarget

type

Mention target: by

type MentionTarget = | { readonly kind: "username"; readonly username: string }
  | { readonly kind: "user-id"; readonly userId: number; readonly label: string }

ParseRichTextResult

type
type ParseRichTextResult = | { kind: "ok"; value: RichTextNode[] }
  | { kind: "invalid"; message: string }

PreLanguage

type

Language tag for a `pre` block, modeled as a discriminated union (no `.optional`).

type PreLanguage = | { readonly kind: "known"; readonly value: string }
  | { readonly kind: "unknown" }

RichTextNode

type
type RichTextNode = | TextNode
  | CodeNode
  | PreNode
  | BoldNode
  | ItalicNode
  | UnderlineNode
  | StrikethroughNode
  | SpoilerNode
  | LinkNode
  | MentionNode
  | BlockquoteNode
  | ExpandableBlockquoteNode

Interfaces

DiagnosticEvent

interface
interface DiagnosticEvent

kind

property
kind: DiagnosticKind

category

property
category: string

detail

property
detail: string

stack

property
stack: string

data

property
data: Record<string, unknown>

timestamp

property
timestamp: number

CaptionText

interface

A RichText document proven to fit within the 1024-code-unit caption limit.

interface CaptionText

[CAPTION_TEXT_BRAND]

property
[CAPTION_TEXT_BRAND]: true

nodes

property
nodes: readonly RichTextNode[]

text

property
text: string

MessageText

interface

A RichText document proven to fit within the 4096-code-unit message limit.

interface MessageText

[MESSAGE_TEXT_BRAND]

property
[MESSAGE_TEXT_BRAND]: true

nodes

property
nodes: readonly RichTextNode[]

text

property
text: string

CollapsibleBlock

interface
interface CollapsibleBlock

kind

property
kind: "collapsible"

summary

property
summary: readonly RichTextNode[]

children

property
children: readonly RichBlockNode[]

HeadingBlock

interface
interface HeadingBlock

kind

property
kind: "heading"

level

property
level: 1 | 2 | 3 | 4

children

property
children: readonly RichTextNode[]

MathBlock

interface
interface MathBlock

kind

property
kind: "math"

latex

property
latex: string

TableBlock

interface
interface TableBlock

kind

property
kind: "table"

header

property
header: readonly (readonly RichTextNode[])[]

rows

property
rows: readonly (readonly (readonly RichTextNode[])[])[]

BlockquoteNode

interface
interface BlockquoteNode

kind

property
kind: "blockquote"

children

property
children: readonly BlockquoteChild[]

BoldNode

interface
interface BoldNode

kind

property
kind: "bold"

children

property
children: readonly RichTextNode[]

CodeNode

interface
interface CodeNode

kind

property
kind: "code"

value

property
value: string

ExpandableBlockquoteNode

interface
interface ExpandableBlockquoteNode

kind

property
kind: "expandable-blockquote"

children

property
children: readonly BlockquoteChild[]

ItalicNode

interface
interface ItalicNode

kind

property
kind: "italic"

children

property
children: readonly RichTextNode[]

LinkNode

interface
interface LinkNode

kind

property
kind: "link"

href

property
href: string

children

property
children: readonly RichTextNode[]

MentionNode

interface
interface MentionNode

kind

property
kind: "mention"

target

property
target: MentionTarget

PreNode

interface
interface PreNode

kind

property
kind: "pre"

language

property
language: PreLanguage

children

property
children: readonly TextNode[]

SpoilerNode

interface
interface SpoilerNode

kind

property
kind: "spoiler"

children

property
children: readonly RichTextNode[]

StrikethroughNode

interface
interface StrikethroughNode

kind

property
kind: "strikethrough"

children

property
children: readonly RichTextNode[]

TextNode

interface
interface TextNode

kind

property
kind: "text"

value

property
value: string

UnderlineNode

interface
interface UnderlineNode

kind

property
kind: "underline"

children

property
children: readonly RichTextNode[]

Constants

CALLBACK_DATA_MAX_BYTES

const

Hard Bot API ceiling on `callback_data`, in UTF-8 bytes (Bot API 10.1).

const CALLBACK_DATA_MAX_BYTES: 64

CallbackActionIdSchema

const
const CallbackActionIdSchema: z.ZodType<CallbackActionId, z.ZodTypeDef, string>

CallbackNamespaceIdSchema

const
const CallbackNamespaceIdSchema: z.ZodType<CallbackNamespaceId, z.ZodTypeDef, string>

CallbackTokenSchema

const
const CallbackTokenSchema: z.ZodType<CallbackToken, z.ZodTypeDef, string>

InlineKeyboardButtonSchema

const
const InlineKeyboardButtonSchema: z.ZodType<InlineKeyboardButton, z.ZodTypeDef, InlineKeyboardButton>

InlineKeyboardMarkupSchema

const
const InlineKeyboardMarkupSchema: z.ZodType<InlineKeyboardMarkup, z.ZodTypeDef, InlineKeyboardMarkup>

CAPTION_TEXT_LIMIT

const
const CAPTION_TEXT_LIMIT: 1024

MESSAGE_TEXT_LIMIT

const
const MESSAGE_TEXT_LIMIT: 4096

CollapsibleBlockSchema

const
const CollapsibleBlockSchema: z.ZodType<CollapsibleBlock, z.ZodTypeDef, CollapsibleBlock>

HeadingBlockSchema

const
const HeadingBlockSchema: z.ZodType<HeadingBlock, z.ZodTypeDef, HeadingBlock>

MathBlockSchema

const
const MathBlockSchema: z.ZodType<MathBlock, z.ZodTypeDef, MathBlock>

RichBlockNodeSchema

const
const RichBlockNodeSchema: z.ZodType<RichBlockNode, z.ZodTypeDef, RichBlockNode>

TableBlockSchema

const
const TableBlockSchema: z.ZodType<TableBlock, z.ZodTypeDef, TableBlock>

BlockquoteChildSchema

const

The blockquote child union: every node kind except the two blockquote variants.

const BlockquoteChildSchema: z.ZodType<BlockquoteChild, z.ZodTypeDef, BlockquoteChild>

BlockquoteNodeSchema

const
const BlockquoteNodeSchema: z.ZodType<BlockquoteNode, z.ZodTypeDef, BlockquoteNode>

BoldNodeSchema

const
const BoldNodeSchema: z.ZodType<BoldNode, z.ZodTypeDef, BoldNode>

CodeNodeSchema

const
const CodeNodeSchema: z.ZodType<CodeNode, z.ZodTypeDef, CodeNode>

ExpandableBlockquoteNodeSchema

const
const ExpandableBlockquoteNodeSchema: z.ZodType<ExpandableBlockquoteNode, z.ZodTypeDef, ExpandableBlockquoteNode>

ItalicNodeSchema

const
const ItalicNodeSchema: z.ZodType<ItalicNode, z.ZodTypeDef, ItalicNode>

LinkNodeSchema

const
const LinkNodeSchema: z.ZodType<LinkNode, z.ZodTypeDef, LinkNode>

MentionNodeSchema

const
const MentionNodeSchema: z.ZodType<MentionNode, z.ZodTypeDef, MentionNode>

PreNodeSchema

const
const PreNodeSchema: z.ZodType<PreNode, z.ZodTypeDef, PreNode>

RichTextNodeSchema

const
const RichTextNodeSchema: z.ZodType<RichTextNode, z.ZodTypeDef, RichTextNode>

RichTextSchema

const
const RichTextSchema: z.ZodType<RichTextNode[], z.ZodTypeDef, RichTextNode[]>

SpoilerNodeSchema

const
const SpoilerNodeSchema: z.ZodType<SpoilerNode, z.ZodTypeDef, SpoilerNode>

StrikethroughNodeSchema

const
const StrikethroughNodeSchema: z.ZodType<StrikethroughNode, z.ZodTypeDef, StrikethroughNode>

TextNodeSchema

const
const TextNodeSchema: z.ZodType<TextNode, z.ZodTypeDef, TextNode>

UnderlineNodeSchema

const
const UnderlineNodeSchema: z.ZodType<UnderlineNode, z.ZodTypeDef, UnderlineNode>