Stream output with Flow

Consume rendered output incrementally instead of materializing the whole document as one String — useful for HTTP responses, large documents, or piping into channels.

Prerequisites: a writer module on the classpath (markup-asciidoc-writer, markup-markdown-writer, or markup-dot-writer). Each brings kotlinx-coroutines with it; no extra dependency is needed.

1. Get a Flow from the model

Every writer exposes a streaming form: asciidocFlow(), markdownFlow(), and dotFlow() each return a cold Flow<String>. Nothing is rendered until the flow is collected.

import org.markup.poet.dsl.document.article
import org.markup.poet.dsl.write.asciidoc.asciidocFlow

val doc = article("T") {
    section("A") {}
    section("B") {}
}

val flow = doc.asciidocFlow()   // cold; renders on collection

2. Rely on the contract, not the chunking

The contract, from the writer API documentation:

Chunks arrive in document order and concatenating all emissions yields exactly toAsciidoc(). Chunk boundaries are an implementation detail.

Currently the title line is one chunk and each top-level block is one chunk; standalone tables (and graphs in the DOT writer) stream per row. For the document above, collecting yields exactly three chunks:

val chunks = doc.asciidocFlow().toList()
// chunks[0] == "= T\n"
// chunks[1] == "\n== A\n"
// chunks[2] == "\n== B\n"
// chunks.joinToString("") == doc.toAsciidoc()

Do not depend on the number or size of chunks — only on their order and their concatenation. See Writer API contract.

3. Collect into a streaming consumer

A typical use is forwarding chunks to a streaming HTTP response or a channel as they arrive:

suspend fun respond(doc: Markup, out: Appendable) {
    doc.asciidocFlow().collect { chunk ->
        // e.g. inside Ktor's respondTextWriter { }, a
        // kotlinx.coroutines Channel, or any suspend-friendly sink
        out.append(chunk)
    }
}

Because the flow is cold, each collection re-renders from the model; collecting twice produces the same output twice.

When to stream

Use Flow when the consumer is asynchronous or benefits from backpressure. For synchronous file or network output, writeAsciidocTo(sink) is simpler — see Write to a kotlinx-io Sink.