Help us shape the future of Go! Take the 2026 Go Developer Survey

emaildoc

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2026 License: AGPL-3.0 Imports: 16 Imported by: 0

Documentation

Overview

Package emaildoc turns an editor document into email-ready HTML, with limits: a document in, HTML or an error out (ADR 0040).

A document is the JSON of @react-email/editor. The render is the editor's own export code (a generated bundle, `mise run build:emaildoc`) run in an embedded JavaScript runtime (goja): no Node, no sidecar, no cgo. A runtime takes seconds to start and tens of megabytes, so it starts on the first call, not at boot; calls queue for it. A call that runs past its time or memory limit is stopped and its runtime is discarded (a stopped runtime does not recover), and a fresh one starts on the next call. Inputs are capped (size, depth, node count) before they reach a runtime.

A document is also validated: structurally against the editor's schema and by rules of ours (heading levels, a button's attributes, a link's href, allowed attribute values). An invalid document is refused with a reason an agent can act on; nothing is repaired or dropped.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalid      = errors.New("the document is invalid")
	ErrTooLarge     = errors.New("the document is too large")
	ErrTooDeep      = errors.New("the document is nested too deeply")
	ErrTooManyNodes = errors.New("the document has too many nodes")
	ErrTimeout      = errors.New("the render took too long and was stopped")
	ErrMemoryLimit  = errors.New("the render used too much memory and was stopped")
)

Errors a call can end with, to be told apart with errors.Is. An invalid document is an *InvalidError (errors.Is(err, ErrInvalid)).

Functions

func EditorVersion

func EditorVersion() string

EditorVersion is the version of the editor whose export code the renderer runs. A stored document records it, so a later dependency update can tell which documents it has to re-render.

Types

type AgentAttr

type AgentAttr struct {
	Default json.RawMessage
	Type    string
	Allowed []string
	Rule    string
}

AgentAttr is one attribute of a node or mark as an agent is told it. The editor's schema holds only the default; the type, the allowed values and the rule are ours, written beside the checks in rules.go that enforce them (the same alignments, list types and URL schemes), so a document that follows what an agent is told is not refused.

type AgentDescription

type AgentDescription struct {
	EditorVersion string
	Nodes         map[string]AgentType
	Marks         map[string]AgentType
	Rules         []string
	Example       json.RawMessage
}

AgentDescription is the document format as an agent reads it: the structure the editor derives, the attribute rules, the rules across the document and a valid document to start from.

func AgentSchema

func AgentSchema() AgentDescription

AgentSchema describes the editor's document for an agent.

type AgentType

type AgentType struct {
	Content string
	Group   string
	Attrs   map[string]AgentAttr
}

AgentType is one node or mark type for an agent.

type InvalidError

type InvalidError struct {
	Reason string
}

InvalidError is a document that the editor cannot hold, with the reason in words an author can act on.

func (*InvalidError) Error

func (e *InvalidError) Error() string

func (*InvalidError) Is

func (e *InvalidError) Is(target error) bool

Is makes every *InvalidError match ErrInvalid.

type Limits

type Limits struct {
	// MaxBytes is the size of the document's JSON.
	MaxBytes int
	// MaxDepth is how deep nodes nest (the document is 1).
	MaxDepth int
	// MaxNodes is how many nodes the document holds in all, text nodes included.
	MaxNodes int
	// Timeout is how long one call may run once a runtime is ready.
	Timeout time.Duration
	// StartTimeout is how long a call waits for a runtime to start (the first call of a process,
	// or the first after one was discarded), on top of Timeout. A start takes seconds, many more on
	// a busy machine; a call that gives up leaves the start running for the next one.
	StartTimeout time.Duration
	// MemoryGrowth is how many bytes the process heap may grow during one call. The heap counter is
	// process-wide, so another goroutine's allocations count too; the call is stopped only when the
	// growth stays over the limit for a fifth of a second, which a passing burst does not, so the
	// limit is a guard, not an accounting.
	MemoryGrowth uint64
}

Limits cap a document before it reaches a runtime.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits are sized for an email: the prototype rendered 1,000 paragraphs in about 8 seconds and 1,000 nested levels in about 10, so a document stays well under both.

type Option

type Option func(*Renderer, *int)

Option configures New.

func WithRuntimes

func WithRuntimes(n int) Option

WithRuntimes sets how many runtimes the Renderer may run at once (default 1). Each costs about 16 MB and seconds to start, the first about 68 MB.

type Rendered

type Rendered struct {
	// HTML is the full email document, formatted.
	HTML string
	// Text is the plain-text part derived from it.
	Text string
}

Rendered is the editor's export of a document.

type Renderer

type Renderer struct {
	// contains filtered or unexported fields
}

Renderer renders and validates documents. It is safe for concurrent use. The zero value is not usable: build one with New.

func Default

func Default() *Renderer

Default is the process-wide Renderer with the default limits and one runtime.

func New

func New(opts ...Option) *Renderer

New returns a Renderer. It starts nothing: the first call does.

func (*Renderer) Render

func (r *Renderer) Render(ctx context.Context, doc []byte) (Rendered, error)

Render validates doc and exports it as an email: HTML and a plain-text part. It never repairs a document; an invalid one is refused with the reason.

func (*Renderer) Started

func (r *Renderer) Started() bool

Started reports whether a runtime has been started yet.

func (*Renderer) Validate

func (r *Renderer) Validate(ctx context.Context, doc []byte) error

Validate answers nil when doc is a document the editor can hold and Render can export, else an error that says why: an *InvalidError, or a limit error.

type Schema

type Schema struct {
	Nodes map[string]SchemaType `json:"nodes"`
	Marks map[string]SchemaType `json:"marks"`
}

Schema is what the editor's extensions define: every node and mark with its content expression and the attributes it accepts, with their defaults. ProseMirror keeps no attribute types or allowed values, so those live in this package's rules and in what an agent is told by hand.

func EditorSchema

func EditorSchema() Schema

EditorSchema returns the editor's schema listing.

func (Schema) Listing

func (s Schema) Listing() string

Listing is the schema as compact text for an agent: one line per node and mark.

type SchemaType

type SchemaType struct {
	// Content is the ProseMirror content expression ("paragraph block*"); empty for a leaf.
	Content string `json:"content,omitempty"`
	// Group is the group the node belongs to ("block", "inline"); empty for a mark.
	Group string `json:"group,omitempty"`
	// Attrs maps each accepted attribute to its default.
	Attrs map[string]any `json:"attrs"`
}

SchemaType is one node or mark.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL