[{"data":1,"prerenderedAt":1747},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fgetting-started\u002Fmodules-overview":314,"\u002Fdocs\u002Fgetting-started\u002Fmodules-overview-surround":1742},[4,38,86,149,176,219,257,273],{"title":5,"path":6,"stem":7,"children":8,"icon":37},"Getting Started","\u002Fdocs\u002Fgetting-started","1.docs\u002F1.getting-started\u002F1.index",[9,12,17,22,27,32],{"title":10,"path":6,"stem":7,"icon":11},"Getting started","i-lucide-flag",{"title":13,"path":14,"stem":15,"icon":16},"Modules overview","\u002Fdocs\u002Fgetting-started\u002Fmodules-overview","1.docs\u002F1.getting-started\u002F2.modules-overview","i-lucide-boxes",{"title":18,"path":19,"stem":20,"icon":21},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","1.docs\u002F1.getting-started\u002F3.installation","i-lucide-download",{"title":23,"path":24,"stem":25,"icon":26},"License configuration","\u002Fdocs\u002Fgetting-started\u002Flicense-configuration","1.docs\u002F1.getting-started\u002F4.license-configuration","i-lucide-key-round",{"title":28,"path":29,"stem":30,"icon":31},"Your first app","\u002Fdocs\u002Fgetting-started\u002Ffirst-app","1.docs\u002F1.getting-started\u002F5.first-app","i-lucide-square-play",{"title":33,"path":34,"stem":35,"icon":36},"Project setup","\u002Fdocs\u002Fgetting-started\u002Fproject-setup","1.docs\u002F1.getting-started\u002F6.project-setup","i-lucide-package",false,{"title":39,"path":40,"stem":41,"children":42,"icon":45},"Core Concepts","\u002Fdocs\u002Fconcepts","1.docs\u002F2.concepts\u002F1.index",[43,46,51,56,61,66,71,76,81],{"title":44,"path":40,"stem":41,"icon":45},"Core concepts","i-lucide-book-open",{"title":47,"path":48,"stem":49,"icon":50},"Architecture","\u002Fdocs\u002Fconcepts\u002Farchitecture","1.docs\u002F2.concepts\u002F2.architecture","i-lucide-layers",{"title":52,"path":53,"stem":54,"icon":55},"Exactly-once semantics","\u002Fdocs\u002Fconcepts\u002Fexactly-once","1.docs\u002F2.concepts\u002F3.exactly-once","i-lucide-shield-check",{"title":57,"path":58,"stem":59,"icon":60},"Lanes and parallelism","\u002Fdocs\u002Fconcepts\u002Flanes-and-parallelism","1.docs\u002F2.concepts\u002F4.lanes-and-parallelism","i-lucide-split",{"title":62,"path":63,"stem":64,"icon":65},"State and thread-safety","\u002Fdocs\u002Fconcepts\u002Fstate-and-thread-safety","1.docs\u002F2.concepts\u002F5.state-and-thread-safety","i-lucide-lock",{"title":67,"path":68,"stem":69,"icon":70},"Event time and watermarks","\u002Fdocs\u002Fconcepts\u002Fevent-time-and-watermarks","1.docs\u002F2.concepts\u002F6.event-time-and-watermarks","i-lucide-clock",{"title":72,"path":73,"stem":74,"icon":75},"The configuration model","\u002Fdocs\u002Fconcepts\u002Fconfiguration-model","1.docs\u002F2.concepts\u002F7.configuration-model","i-lucide-sliders-horizontal",{"title":77,"path":78,"stem":79,"icon":80},"The error-handling model","\u002Fdocs\u002Fconcepts\u002Ferror-handling-model","1.docs\u002F2.concepts\u002F8.error-handling-model","i-lucide-triangle-alert",{"title":82,"path":83,"stem":84,"icon":85},"How StoatFlow differs from Kafka Streams","\u002Fdocs\u002Fconcepts\u002Fhow-stoatflow-differs-from-ks","1.docs\u002F2.concepts\u002F9.how-stoatflow-differs-from-ks","i-lucide-arrow-left-right",{"title":87,"path":88,"stem":89,"children":90,"icon":93},"Building Topologies","\u002Fdocs\u002Fbuilding","1.docs\u002F3.building\u002F1.index",[91,94,99,104,109,114,119,124,129,134,139,144],{"title":92,"path":88,"stem":89,"icon":93},"Building topologies","i-lucide-blocks",{"title":95,"path":96,"stem":97,"icon":98},"Error handling and DLQ","\u002Fdocs\u002Fbuilding\u002Ferror-handling-dlq","1.docs\u002F3.building\u002F10.error-handling-dlq","i-lucide-circle-x",{"title":100,"path":101,"stem":102,"icon":103},"State stores","\u002Fdocs\u002Fbuilding\u002Fstate-stores","1.docs\u002F3.building\u002F11.state-stores","i-lucide-database",{"title":105,"path":106,"stem":107,"icon":108},"Testing topologies","\u002Fdocs\u002Fbuilding\u002Ftesting","1.docs\u002F3.building\u002F12.testing","i-lucide-flask-conical",{"title":110,"path":111,"stem":112,"icon":113},"Sources and sinks","\u002Fdocs\u002Fbuilding\u002Fstreams-builder","1.docs\u002F3.building\u002F2.streams-builder","i-lucide-import",{"title":115,"path":116,"stem":117,"icon":118},"KStream and KTable operations","\u002Fdocs\u002Fbuilding\u002Fkstream-ktable","1.docs\u002F3.building\u002F3.kstream-ktable","i-lucide-waypoints",{"title":120,"path":121,"stem":122,"icon":123},"Aggregations","\u002Fdocs\u002Fbuilding\u002Faggregations","1.docs\u002F3.building\u002F4.aggregations","i-lucide-sigma",{"title":125,"path":126,"stem":127,"icon":128},"Windowing","\u002Fdocs\u002Fbuilding\u002Fwindowing","1.docs\u002F3.building\u002F5.windowing","i-lucide-calendar-clock",{"title":130,"path":131,"stem":132,"icon":133},"Joins","\u002Fdocs\u002Fbuilding\u002Fjoins","1.docs\u002F3.building\u002F6.joins","i-lucide-git-merge",{"title":135,"path":136,"stem":137,"icon":138},"The Processor API","\u002Fdocs\u002Fbuilding\u002Fprocessor-api","1.docs\u002F3.building\u002F7.processor-api","i-lucide-cpu",{"title":140,"path":141,"stem":142,"icon":143},"Scheduled sources","\u002Fdocs\u002Fbuilding\u002Fscheduled-sources","1.docs\u002F3.building\u002F8.scheduled-sources","i-lucide-timer",{"title":145,"path":146,"stem":147,"icon":148},"Serdes and Avro","\u002Fdocs\u002Fbuilding\u002Fserdes","1.docs\u002F3.building\u002F9.serdes","i-lucide-binary",{"title":150,"path":151,"stem":152,"children":153,"icon":75},"Configuration","\u002Fdocs\u002Fconfiguration","1.docs\u002F4.configuration\u002F1.index",[154,156,161,166,171],{"title":155,"path":151,"stem":152,"icon":75},"How configuration works",{"title":157,"path":158,"stem":159,"icon":160},"Engine configuration (:core)","\u002Fdocs\u002Fconfiguration\u002Fcore-config","1.docs\u002F4.configuration\u002F2.core-config","i-lucide-settings-2",{"title":162,"path":163,"stem":164,"icon":165},"Runtime configuration (:runtime)","\u002Fdocs\u002Fconfiguration\u002Fruntime-config","1.docs\u002F4.configuration\u002F3.runtime-config","i-lucide-server-cog",{"title":167,"path":168,"stem":169,"icon":170},"Defaults, adaptivity, and presets","\u002Fdocs\u002Fconfiguration\u002Fdefaults-and-presets","1.docs\u002F4.configuration\u002F4.defaults-and-presets","i-lucide-gauge",{"title":172,"path":173,"stem":174,"icon":175},"Kafka client configuration","\u002Fdocs\u002Fconfiguration\u002Fkafka-client-config","1.docs\u002F4.configuration\u002F5.kafka-client-config","i-lucide-plug",{"title":177,"path":178,"stem":179,"children":180,"icon":183},"Running in Production","\u002Fdocs\u002Fruntime","1.docs\u002F5.runtime\u002F1.index",[181,184,189,194,199,204,209,214],{"title":182,"path":178,"stem":179,"icon":183},"The runtime","i-lucide-server",{"title":185,"path":186,"stem":187,"icon":188},"The REST API","\u002Fdocs\u002Fruntime\u002Frest-api","1.docs\u002F5.runtime\u002F2.rest-api","i-lucide-globe",{"title":190,"path":191,"stem":192,"icon":193},"Health checks","\u002Fdocs\u002Fruntime\u002Fhealth-checks","1.docs\u002F5.runtime\u002F3.health-checks","i-lucide-heart-pulse",{"title":195,"path":196,"stem":197,"icon":198},"Metrics","\u002Fdocs\u002Fruntime\u002Fmetrics","1.docs\u002F5.runtime\u002F4.metrics","i-lucide-activity",{"title":200,"path":201,"stem":202,"icon":203},"Pause and resume","\u002Fdocs\u002Fruntime\u002Fpause-unpause","1.docs\u002F5.runtime\u002F5.pause-unpause","i-lucide-pause",{"title":205,"path":206,"stem":207,"icon":208},"Plugins and lifecycle hooks","\u002Fdocs\u002Fruntime\u002Fplugins","1.docs\u002F5.runtime\u002F6.plugins","i-lucide-puzzle",{"title":210,"path":211,"stem":212,"icon":213},"Docker images","\u002Fdocs\u002Fruntime\u002Fdocker","1.docs\u002F5.runtime\u002F7.docker","i-lucide-container",{"title":215,"path":216,"stem":217,"icon":218},"GraalVM native image","\u002Fdocs\u002Fruntime\u002Fnative-image","1.docs\u002F5.runtime\u002F8.native-image","i-lucide-zap",{"title":220,"path":221,"stem":222,"children":223,"icon":226},"Deploying & Operating","\u002Fdocs\u002Foperating","1.docs\u002F6.operating\u002F1.index",[224,227,232,237,242,247,252],{"title":225,"path":221,"stem":222,"icon":226},"Deploying and operating","i-lucide-life-buoy",{"title":228,"path":229,"stem":230,"icon":231},"Running on Kubernetes","\u002Fdocs\u002Foperating\u002Fkubernetes","1.docs\u002F6.operating\u002F2.kubernetes","i-lucide-ship",{"title":233,"path":234,"stem":235,"icon":236},"High availability","\u002Fdocs\u002Foperating\u002Fhigh-availability","1.docs\u002F6.operating\u002F3.high-availability","i-lucide-copy",{"title":238,"path":239,"stem":240,"icon":241},"Liveness and readiness probes","\u002Fdocs\u002Foperating\u002Fprobes","1.docs\u002F6.operating\u002F4.probes","i-lucide-stethoscope",{"title":243,"path":244,"stem":245,"icon":246},"Observability","\u002Fdocs\u002Foperating\u002Fobservability","1.docs\u002F6.operating\u002F5.observability","i-lucide-telescope",{"title":248,"path":249,"stem":250,"icon":251},"Tuning under load","\u002Fdocs\u002Foperating\u002Ftuning","1.docs\u002F6.operating\u002F6.tuning","i-lucide-sliders",{"title":253,"path":254,"stem":255,"icon":256},"Production checklist","\u002Fdocs\u002Foperating\u002Fproduction-checklist","1.docs\u002F6.operating\u002F7.production-checklist","i-lucide-clipboard-check",{"title":258,"path":259,"stem":260,"children":261,"icon":85},"Migrating from Kafka Streams","\u002Fdocs\u002Fmigration","1.docs\u002F7.migration\u002F1.index",[262,263,268],{"title":258,"path":259,"stem":260,"icon":85},{"title":264,"path":265,"stem":266,"icon":267},"Migration without carrying state","\u002Fdocs\u002Fmigration\u002Fwithout-data-migration","1.docs\u002F7.migration\u002F2.without-data-migration","i-lucide-sparkles",{"title":269,"path":270,"stem":271,"icon":272},"Migration carrying state","\u002Fdocs\u002Fmigration\u002Fwith-data-migration","1.docs\u002F7.migration\u002F3.with-data-migration","i-lucide-database-backup",{"title":274,"path":275,"stem":276,"children":277,"icon":279},"Reference","\u002Fdocs\u002Freference","1.docs\u002F8.reference\u002F1.index",[278,280,285,290,295,300,305,309],{"title":274,"path":275,"stem":276,"icon":279},"i-lucide-list",{"title":281,"path":282,"stem":283,"icon":284},"Configuration reference","\u002Fdocs\u002Freference\u002Fconfiguration-reference","1.docs\u002F8.reference\u002F2.configuration-reference","i-lucide-table",{"title":286,"path":287,"stem":288,"icon":289},"REST API reference","\u002Fdocs\u002Freference\u002Frest-api-reference","1.docs\u002F8.reference\u002F3.rest-api-reference","i-lucide-network",{"title":291,"path":292,"stem":293,"icon":294},"Gradle plugin reference","\u002Fdocs\u002Freference\u002Fgradle-plugin-reference","1.docs\u002F8.reference\u002F4.gradle-plugin-reference","i-lucide-box",{"title":296,"path":297,"stem":298,"icon":299},"Maven reference","\u002Fdocs\u002Freference\u002Fmaven-reference","1.docs\u002F8.reference\u002F5.maven-reference","i-simple-icons-apachemaven",{"title":301,"path":302,"stem":303,"icon":304},"Kafka Streams compatibility matrix","\u002Fdocs\u002Freference\u002Fks-compatibility-matrix","1.docs\u002F8.reference\u002F6.ks-compatibility-matrix","i-lucide-table-2",{"title":306,"path":307,"stem":308,"icon":170},"Metrics reference","\u002Fdocs\u002Freference\u002Fmetrics-reference","1.docs\u002F8.reference\u002F7.metrics-reference",{"title":310,"path":311,"stem":312,"icon":313},"Glossary","\u002Fdocs\u002Freference\u002Fglossary","1.docs\u002F8.reference\u002F8.glossary","i-lucide-book-a",{"id":315,"title":13,"body":316,"description":1735,"extension":1736,"meta":1737,"navigation":1738,"pageType":1739,"path":14,"seo":1740,"stem":15,"__hash__":1741},"docs\u002F1.docs\u002F1.getting-started\u002F2.modules-overview.md",{"type":317,"value":318,"toc":1718},"minimark",[319,335,399,404,407,414,433,445,573,579,585,590,665,672,843,861,865,868,874,882,1029,1038,1046,1052,1081,1119,1133,1137,1156,1159,1247,1263,1267,1414,1417,1464,1483,1486,1653,1675,1679,1714],[320,321,322,323,327,328,331,332,334],"p",{},"StoatFlow ships as a small set of modules under the ",[324,325,326],"code",{},"io.stoatflow"," group, plus a Gradle convention plugin. This page explains what each one is for and how they fit together, so you can pick the right dependency before you start. For exact coordinates see ",[329,330,18],"a",{"href":19},"; for the plugin setup see ",[329,333,33],{"href":34},".",[336,337,338],"tldr-panel",{},[339,340,341,355,362,374,386],"ul",{},[342,343,344,350,351,354],"li",{},[345,346,347],"strong",{},[324,348,349],{},"stoatflow-runtime"," — the batteries-included production wrapper (YAML config, HTTP admin + health endpoints, Prometheus metrics, graceful shutdown). It pulls in ",[324,352,353],{},"stoatflow-core",", so it's usually the only main dependency you add.",[342,356,357,361],{},[345,358,359],{},[324,360,353],{}," — the DSL and the engine on their own, no runtime scaffolding. Choose this when you want to embed StoatFlow and own the lifecycle yourself.",[342,363,364,369,370,373],{},[345,365,366],{},[324,367,368],{},"stoatflow-test-utils"," — the broker-free ",[324,371,372],{},"TopologyTestDriver"," for unit-testing topologies.",[342,375,376,381,382,385],{},[345,377,378],{},[324,379,380],{},"stoatflow-test-runtime"," — wires that test driver up from your runtime ",[324,383,384],{},"application.yaml",", so tests load the same config your app does.",[342,387,388,391,392,394,395,398],{},[345,389,390],{},"The StoatFlow build conventions"," — the ",[324,393,326],{}," Gradle plugin and the ",[324,396,397],{},"stoatflow-parent"," Maven POM apply the JDK 25 toolchain, the required JVM flags, and a runnable build. Both tools, kept symmetric.",[400,401,403],"h2",{"id":402},"the-two-main-modules","The two main modules",[320,405,406],{},"StoatFlow separates the stream-processing library from the production wrapper around it. You depend on exactly one of them as your main dependency.",[408,409,411,413],"h3",{"id":410},"stoatflow-core-dsl-engine",[324,412,353],{}," — DSL + engine",[320,415,416,418,419,422,423,422,426,429,430,334],{},[324,417,353],{}," is the library proper: the Kafka Streams-compatible DSL (",[324,420,421],{},"StreamsBuilder",", ",[324,424,425],{},"KStream",[324,427,428],{},"KTable",", aggregations, joins, windowing), the Processor API, state stores, watermark strategies, and the single-instance engine that runs them. Everything you write a topology against lives here, under ",[324,431,432],{},"io.stoatflow.core.*",[320,434,435,436,440,441,444],{},"It carries no opinion about ",[437,438,439],"em",{},"how"," your process starts, gets configured, or exposes itself for operations. You construct the engine, hand it a topology and a ",[324,442,443],{},"StreamsConfig",", and drive its lifecycle yourself. The DSL-only example wires it up by hand:",[446,447,449,488],"code-tabs",{"group":448},"lang",[450,451,457],"pre",{"className":452,"code":453,"filename":454,"language":455,"meta":456,"style":456},"language-kotlin shiki shiki-themes vitesse-light","import io.stoatflow.core.StoatFlow\nimport io.stoatflow.core.config.StreamsConfig\nimport io.stoatflow.core.topology.StreamsBuilder\n","Kotlin","kotlin","",[324,458,459,472,480],{"__ignoreMap":456},[460,461,464,468],"span",{"class":462,"line":463},"line",1,[460,465,467],{"class":466},"sbBg2","import",[460,469,471],{"class":470},"sySUi"," io.stoatflow.core.StoatFlow\n",[460,473,475,477],{"class":462,"line":474},2,[460,476,467],{"class":466},[460,478,479],{"class":470}," io.stoatflow.core.config.StreamsConfig\n",[460,481,483,485],{"class":462,"line":482},3,[460,484,467],{"class":466},[460,486,487],{"class":470}," io.stoatflow.core.topology.StreamsBuilder\n",[450,489,494],{"className":490,"code":491,"filename":492,"language":493,"meta":456,"style":456},"language-java shiki shiki-themes vitesse-light","import io.stoatflow.core.StoatFlow;\nimport io.stoatflow.core.config.StreamsConfig;\nimport io.stoatflow.core.topology.StreamsBuilder;\n","Java","java",[324,495,496,523,548],{"__ignoreMap":456},[460,497,498,500,504,507,510,512,515,517,520],{"class":462,"line":463},[460,499,467],{"class":466},[460,501,503],{"class":502},"suHK_"," io",[460,505,334],{"class":506},"sYZai",[460,508,509],{"class":502},"stoatflow",[460,511,334],{"class":506},[460,513,514],{"class":502},"core",[460,516,334],{"class":506},[460,518,519],{"class":502},"StoatFlow",[460,521,522],{"class":506},";\n",[460,524,525,527,529,531,533,535,537,539,542,544,546],{"class":462,"line":474},[460,526,467],{"class":466},[460,528,503],{"class":502},[460,530,334],{"class":506},[460,532,509],{"class":502},[460,534,334],{"class":506},[460,536,514],{"class":502},[460,538,334],{"class":506},[460,540,541],{"class":502},"config",[460,543,334],{"class":506},[460,545,443],{"class":502},[460,547,522],{"class":506},[460,549,550,552,554,556,558,560,562,564,567,569,571],{"class":462,"line":482},[460,551,467],{"class":466},[460,553,503],{"class":502},[460,555,334],{"class":506},[460,557,509],{"class":502},[460,559,334],{"class":506},[460,561,514],{"class":502},[460,563,334],{"class":506},[460,565,566],{"class":502},"topology",[460,568,334],{"class":506},[460,570,421],{"class":502},[460,572,522],{"class":506},[320,574,575,576,578],{},"Depend on ",[324,577,353],{}," directly when you're embedding StoatFlow into an application that already owns configuration, HTTP, and observability — or when you deliberately want a minimal footprint and intend to manage the engine lifecycle yourself.",[408,580,582,584],{"id":581},"stoatflow-runtime-batteries-included-wrapper",[324,583,349],{}," — batteries-included wrapper",[320,586,587,589],{},[324,588,349],{}," is a thin, customer-extensible wrapper around the core engine. It adds the operational scaffolding a production deployment needs:",[339,591,592,601,623,637,646,659],{},[342,593,594,597,598,600],{},[345,595,596],{},"YAML configuration"," loaded from ",[324,599,384],{}," (plus environment-variable and overlay-file overrides).",[342,602,603,606,607,422,610,422,613,422,616,422,619,622],{},[345,604,605],{},"An HTTP server"," exposing admin and introspection endpoints — ",[324,608,609],{},"\u002Finfo",[324,611,612],{},"\u002Fconfig",[324,614,615],{},"\u002Ftopology",[324,617,618],{},"\u002Fstate",[324,620,621],{},"\u002Fwatermarks",", and more.",[342,624,625,628,629,632,633,636],{},[345,626,627],{},"Kubernetes health probes"," at ",[324,630,631],{},"\u002Fhealth\u002Flive"," and ",[324,634,635],{},"\u002Fhealth\u002Fready",", backed by indicators for application state, broker connectivity, Schema Registry, and license validity.",[342,638,639,628,642,645],{},[345,640,641],{},"Prometheus metrics",[324,643,644],{},"\u002Fmetrics"," via Micrometer.",[342,647,648,651,652,422,655,658],{},[345,649,650],{},"A plugin system"," (",[324,653,654],{},"RuntimePlugin",[324,656,657],{},"PluginContext",", lifecycle listeners) for hooking into start\u002Fstop and registering your own handlers, health indicators, or plugins.",[342,660,661,664],{},[345,662,663],{},"Graceful shutdown"," wired into the JVM lifecycle.",[320,666,667,668,671],{},"The entry point is ",[324,669,670],{},"StoatFlowRuntime.fromConfig(...)",", which loads your config, starts the HTTP and metrics server, and runs your topology until terminated:",[446,673,674,757],{"group":448},[450,675,677],{"className":452,"code":676,"filename":454,"language":455,"meta":456,"style":456},"import io.stoatflow.runtime.StoatFlowRuntime\n\nval runtime = StoatFlowRuntime.fromConfig(\n    topologyBuilder = { buildTopology(it) },\n)\nruntime.start()\nruntime.awaitTermination()\n",[324,678,679,686,692,712,729,735,747],{"__ignoreMap":456},[460,680,681,683],{"class":462,"line":463},[460,682,467],{"class":466},[460,684,685],{"class":470}," io.stoatflow.runtime.StoatFlowRuntime\n",[460,687,688],{"class":462,"line":474},[460,689,691],{"emptyLinePlaceholder":690},true,"\n",[460,693,694,697,700,703,706,709],{"class":462,"line":482},[460,695,696],{"class":466},"val",[460,698,699],{"class":502}," runtime ",[460,701,702],{"class":506},"=",[460,704,705],{"class":502}," StoatFlowRuntime.",[460,707,708],{"class":470},"fromConfig",[460,710,711],{"class":502},"(\n",[460,713,715,718,720,723,726],{"class":462,"line":714},4,[460,716,717],{"class":502},"    topologyBuilder ",[460,719,702],{"class":506},[460,721,722],{"class":502}," { ",[460,724,725],{"class":470},"buildTopology",[460,727,728],{"class":502},"(it) },\n",[460,730,732],{"class":462,"line":731},5,[460,733,734],{"class":502},")\n",[460,736,738,741,744],{"class":462,"line":737},6,[460,739,740],{"class":502},"runtime.",[460,742,743],{"class":470},"start",[460,745,746],{"class":502},"()\n",[460,748,750,752,755],{"class":462,"line":749},7,[460,751,740],{"class":502},[460,753,754],{"class":470},"awaitTermination",[460,756,746],{"class":502},[450,758,760],{"className":490,"code":759,"filename":492,"language":493,"meta":456,"style":456},"import io.stoatflow.runtime.StoatFlowRuntime;\n\nvar runtime = StoatFlowRuntime.fromConfig(Main::buildTopology);\nruntime.start();\nruntime.awaitTermination();\n",[324,761,762,784,788,822,833],{"__ignoreMap":456},[460,763,764,766,768,770,772,774,777,779,782],{"class":462,"line":463},[460,765,467],{"class":466},[460,767,503],{"class":502},[460,769,334],{"class":506},[460,771,509],{"class":502},[460,773,334],{"class":506},[460,775,776],{"class":502},"runtime",[460,778,334],{"class":506},[460,780,781],{"class":502},"StoatFlowRuntime",[460,783,522],{"class":506},[460,785,786],{"class":462,"line":474},[460,787,691],{"emptyLinePlaceholder":690},[460,789,790,794,798,801,804,806,808,811,814,817,819],{"class":462,"line":482},[460,791,793],{"class":792},"si04Y","var",[460,795,797],{"class":796},"svycV"," runtime",[460,799,800],{"class":506}," =",[460,802,803],{"class":796}," StoatFlowRuntime",[460,805,334],{"class":506},[460,807,708],{"class":470},[460,809,810],{"class":506},"(",[460,812,813],{"class":502},"Main",[460,815,816],{"class":466},"::",[460,818,725],{"class":502},[460,820,821],{"class":506},");\n",[460,823,824,826,828,830],{"class":462,"line":714},[460,825,776],{"class":796},[460,827,334],{"class":506},[460,829,743],{"class":470},[460,831,832],{"class":506},"();\n",[460,834,835,837,839,841],{"class":462,"line":731},[460,836,776],{"class":796},[460,838,334],{"class":506},[460,840,754],{"class":470},[460,842,832],{"class":506},[320,844,845,847,848,850,851,853,854,856,857,860],{},[324,846,349],{}," depends transitively on ",[324,849,353],{},", so adding it gives you the full DSL plus the wrapper in one dependency. Most applications want this — it's the default the rest of the getting-started guide builds on (see ",[329,852,28],{"href":29},"). The runtime module is published un-obfuscated precisely so you can extend it: writing a custom ",[324,855,654],{},", a custom ",[324,858,859],{},"HealthIndicator",", or an extra HTTP handler all work against types you can see by name.",[400,862,864],{"id":863},"the-testing-modules","The testing modules",[320,866,867],{},"StoatFlow provides two testing modules. Both are test-scoped dependencies, and which one you use follows directly from which main module you chose.",[408,869,871,873],{"id":870},"stoatflow-test-utils-the-broker-free-test-driver",[324,872,368],{}," — the broker-free test driver",[320,875,876,878,879,881],{},[324,877,368],{}," provides ",[324,880,372],{},", an in-memory harness that runs a topology synchronously without a Kafka broker. You pipe records into test input topics and read results from test output topics, with explicit control over timestamps and watermarks. Because processing is synchronous and deterministic, tests are fast and easy to debug. It supports the full DSL — stateful processors, windowing, joins, custom partitioners — and exposes state stores for assertions.",[450,883,885],{"className":452,"code":884,"language":455,"meta":456,"style":456},"val driver = TopologyTestDriver.fromBuilder(builder)\nval input = driver.createInputTopic\u003CString, String>(\"input-topic\", Serdes.String(), Serdes.String())\nval output = driver.createOutputTopic\u003CString, String>(\"output-topic\", Serdes.String(), Serdes.String())\n\ninput.pipeInput(\"key1\", \"hello\")\nval record = output.readRecord()\n",[324,886,887,905,951,988,992,1012],{"__ignoreMap":456},[460,888,889,891,894,896,899,902],{"class":462,"line":463},[460,890,696],{"class":466},[460,892,893],{"class":502}," driver ",[460,895,702],{"class":506},[460,897,898],{"class":502}," TopologyTestDriver.",[460,900,901],{"class":470},"fromBuilder",[460,903,904],{"class":502},"(builder)\n",[460,906,907,909,912,914,917,920,923,927,929,931,934,938,941,943,946,948],{"class":462,"line":474},[460,908,696],{"class":466},[460,910,911],{"class":502}," input ",[460,913,702],{"class":506},[460,915,916],{"class":502}," driver.",[460,918,919],{"class":470},"createInputTopic",[460,921,922],{"class":502},"\u003C",[460,924,926],{"class":925},"sUxyF","String",[460,928,422],{"class":502},[460,930,926],{"class":925},[460,932,933],{"class":502},">(",[460,935,937],{"class":936},"spphp","\"input-topic\"",[460,939,940],{"class":502},", Serdes.",[460,942,926],{"class":470},[460,944,945],{"class":502},"(), Serdes.",[460,947,926],{"class":470},[460,949,950],{"class":502},"())\n",[460,952,953,955,958,960,962,965,967,969,971,973,975,978,980,982,984,986],{"class":462,"line":482},[460,954,696],{"class":466},[460,956,957],{"class":502}," output ",[460,959,702],{"class":506},[460,961,916],{"class":502},[460,963,964],{"class":470},"createOutputTopic",[460,966,922],{"class":502},[460,968,926],{"class":925},[460,970,422],{"class":502},[460,972,926],{"class":925},[460,974,933],{"class":502},[460,976,977],{"class":936},"\"output-topic\"",[460,979,940],{"class":502},[460,981,926],{"class":470},[460,983,945],{"class":502},[460,985,926],{"class":470},[460,987,950],{"class":502},[460,989,990],{"class":462,"line":714},[460,991,691],{"emptyLinePlaceholder":690},[460,993,994,997,1000,1002,1005,1007,1010],{"class":462,"line":731},[460,995,996],{"class":502},"input.",[460,998,999],{"class":470},"pipeInput",[460,1001,810],{"class":502},[460,1003,1004],{"class":936},"\"key1\"",[460,1006,422],{"class":502},[460,1008,1009],{"class":936},"\"hello\"",[460,1011,734],{"class":502},[460,1013,1014,1016,1019,1021,1024,1027],{"class":462,"line":737},[460,1015,696],{"class":466},[460,1017,1018],{"class":502}," record ",[460,1020,702],{"class":506},[460,1022,1023],{"class":502}," output.",[460,1025,1026],{"class":470},"readRecord",[460,1028,746],{"class":502},[320,1030,1031,1033,1034,1037],{},[324,1032,372],{}," is ",[345,1035,1036],{},"not"," thread-safe — create a fresh instance per test case when running tests in parallel.",[320,1039,1040,1041,1043,1044,334],{},"This is the module to add when you depend on ",[324,1042,353],{}," and build your topology in code with no ",[324,1045,384],{},[408,1047,1049,1051],{"id":1048},"stoatflow-test-runtime-the-same-driver-from-your-runtime-config",[324,1050,380],{}," — the same driver, from your runtime config",[320,1053,1054,1056,1057,1059,1060,1063,1064,1066,1067,1069,1070,1072,1073,1076,1077,1080],{},[324,1055,380],{}," is the testing counterpart to ",[324,1058,349],{},". Its ",[324,1061,1062],{},"StoatFlowTestDriver.fromConfig(...)"," factory loads the same ",[324,1065,384],{}," your production app uses and returns a configured ",[324,1068,372],{},". It mirrors the production ",[324,1071,670],{}," API — you pass the same topology builder — so your tests exercise the same config your runtime resolves at startup (env vars with the ",[324,1074,1075],{},"STOATFLOW_"," prefix still take highest priority, and a ",[324,1078,1079],{},"src\u002Ftest\u002Fresources\u002Fapplication.yaml"," shadows the main one).",[450,1082,1084],{"className":452,"code":1083,"language":455,"meta":456,"style":456},"val driver = StoatFlowTestDriver.fromConfig { builder, config ->\n    MyApp.buildTopology(builder)\n}\n",[324,1085,1086,1105,1114],{"__ignoreMap":456},[460,1087,1088,1090,1092,1094,1097,1099,1102],{"class":462,"line":463},[460,1089,696],{"class":466},[460,1091,893],{"class":502},[460,1093,702],{"class":506},[460,1095,1096],{"class":502}," StoatFlowTestDriver.",[460,1098,708],{"class":470},[460,1100,1101],{"class":502}," { builder, config ",[460,1103,1104],{"class":506},"->\n",[460,1106,1107,1110,1112],{"class":462,"line":474},[460,1108,1109],{"class":502},"    MyApp.",[460,1111,725],{"class":470},[460,1113,904],{"class":502},[460,1115,1116],{"class":462,"line":482},[460,1117,1118],{"class":502},"}\n",[320,1120,1121,1122,632,1124,1126,1127,1129,1130,1132],{},"It depends on both ",[324,1123,368],{},[324,1125,349],{},", so you get the in-memory driver and config loading together. The StoatFlow example apps that ship with ",[324,1128,384],{}," use this module for their tests. Reach for it whenever you're on ",[324,1131,349],{}," and want your tests to load configuration the way your deployed app does, rather than reconstructing it in test code.",[400,1134,1136],{"id":1135},"the-build-conventions","The build conventions",[320,1138,1139,1140,632,1143,1146,1147,1150,1151,394,1153,1155],{},"The build conventions are a build-time concern, not a runtime dependency. StoatFlow requires a JDK 25 toolchain and two JVM flags (",[324,1141,1142],{},"--enable-preview",[324,1144,1145],{},"--enable-native-access=ALL-UNNAMED",") at both compile and run time — the conventions apply all of that for you, plus a runnable application setup and a fat-jar build, so you don't hand-maintain the toolchain or argument lists. They ship for ",[345,1148,1149],{},"both build tools, kept symmetric",": the ",[324,1152,326],{},[324,1154,397],{}," Maven POM.",[320,1157,1158],{},"On Gradle, apply the plugin and point it at your main class:",[450,1160,1162],{"className":452,"code":1161,"language":455,"meta":456,"style":456},"\u002F\u002F build.gradle.kts\nplugins {\n    kotlin(\"jvm\") version \"2.4.0\"   \u002F\u002F omit for a Java-only project\n    id(\"io.stoatflow\") version \"\u003Cstoatflow-version>\"\n}\n\nstoatflow {\n    mainClass.set(\"com.example.MainKt\")\n}\n",[324,1163,1164,1170,1178,1197,1212,1216,1220,1226,1242],{"__ignoreMap":456},[460,1165,1166],{"class":462,"line":463},[460,1167,1169],{"class":1168},"s8zF2","\u002F\u002F build.gradle.kts\n",[460,1171,1172,1175],{"class":462,"line":474},[460,1173,1174],{"class":470},"plugins",[460,1176,1177],{"class":502}," {\n",[460,1179,1180,1183,1185,1188,1191,1194],{"class":462,"line":482},[460,1181,1182],{"class":470},"    kotlin",[460,1184,810],{"class":502},[460,1186,1187],{"class":936},"\"jvm\"",[460,1189,1190],{"class":502},") version ",[460,1192,1193],{"class":936},"\"2.4.0\"",[460,1195,1196],{"class":1168},"   \u002F\u002F omit for a Java-only project\n",[460,1198,1199,1202,1204,1207,1209],{"class":462,"line":714},[460,1200,1201],{"class":470},"    id",[460,1203,810],{"class":502},[460,1205,1206],{"class":936},"\"io.stoatflow\"",[460,1208,1190],{"class":502},[460,1210,1211],{"class":936},"\"\u003Cstoatflow-version>\"\n",[460,1213,1214],{"class":462,"line":731},[460,1215,1118],{"class":502},[460,1217,1218],{"class":462,"line":737},[460,1219,691],{"emptyLinePlaceholder":690},[460,1221,1222,1224],{"class":462,"line":749},[460,1223,509],{"class":470},[460,1225,1177],{"class":502},[460,1227,1229,1232,1235,1237,1240],{"class":462,"line":1228},8,[460,1230,1231],{"class":502},"    mainClass.",[460,1233,1234],{"class":470},"set",[460,1236,810],{"class":502},[460,1238,1239],{"class":936},"\"com.example.MainKt\"",[460,1241,734],{"class":502},[460,1243,1245],{"class":462,"line":1244},9,[460,1246,1118],{"class":502},[320,1248,1249,1250,1252,1253,1256,1257,1260,1261,334],{},"On Maven, inherit from ",[324,1251,397],{}," and set ",[324,1254,1255],{},"\u003Cstoatflow.mainClass>"," instead. Both tools also offer opt-in Docker and GraalVM native-image builds, and bundle the same native flags and container entrypoints under the hood, so they can't drift. The full setup for both — including the ",[324,1258,1259],{},"pluginManagement"," repository the Gradle marker resolves from and the Maven BOM — is in ",[329,1262,33],{"href":34},[400,1264,1266],{"id":1265},"how-they-fit-together","How they fit together",[1268,1269,1270,1292],"table",{},[1271,1272,1273],"thead",{},[1274,1275,1276,1280,1283,1286,1289],"tr",{},[1277,1278,1279],"th",{},"Module",[1277,1281,1282],{},"Group \u002F artifact",[1277,1284,1285],{},"Scope",[1277,1287,1288],{},"Depends on",[1277,1290,1291],{},"Use when",[1293,1294,1295,1315,1332,1350,1371,1389],"tbody",{},[1274,1296,1297,1301,1306,1309,1312],{},[1298,1299,1300],"td",{},"Core",[1298,1302,1303],{},[324,1304,1305],{},"io.stoatflow:stoatflow-core",[1298,1307,1308],{},"main",[1298,1310,1311],{},"—",[1298,1313,1314],{},"You want only the DSL + engine and will own the lifecycle, config, and observability yourself.",[1274,1316,1317,1320,1325,1327,1329],{},[1298,1318,1319],{},"Runtime",[1298,1321,1322],{},[324,1323,1324],{},"io.stoatflow:stoatflow-runtime",[1298,1326,1308],{},[1298,1328,514],{},[1298,1330,1331],{},"The default for most apps — batteries-included config, HTTP, health, metrics, graceful shutdown.",[1274,1333,1334,1337,1342,1345,1347],{},[1298,1335,1336],{},"Test utils",[1298,1338,1339],{},[324,1340,1341],{},"io.stoatflow:stoatflow-test-utils",[1298,1343,1344],{},"test",[1298,1346,514],{},[1298,1348,1349],{},"Broker-free unit tests of a topology built in code.",[1274,1351,1352,1355,1360,1362,1365],{},[1298,1353,1354],{},"Test runtime",[1298,1356,1357],{},[324,1358,1359],{},"io.stoatflow:stoatflow-test-runtime",[1298,1361,1344],{},[1298,1363,1364],{},"test-utils, runtime",[1298,1366,1367,1368,1370],{},"Tests that should load the same ",[324,1369,384],{}," as your runtime app.",[1274,1372,1373,1376,1381,1384,1386],{},[1298,1374,1375],{},"Gradle plugin",[1298,1377,1378,1380],{},[324,1379,326],{}," (plugin id)",[1298,1382,1383],{},"build",[1298,1385,1311],{},[1298,1387,1388],{},"Gradle builds — applies the JDK 25 toolchain, required JVM flags, runnable + fat-jar build.",[1274,1390,1391,1394,1403,1405,1407],{},[1298,1392,1393],{},"Maven parent \u002F BOM",[1298,1395,1396,1399,1400],{},[324,1397,1398],{},"io.stoatflow:stoatflow-parent"," · ",[324,1401,1402],{},"stoatflow-bom",[1298,1404,1383],{},[1298,1406,1311],{},[1298,1408,1409,1410,1413],{},"Maven builds — the same conventions via ",[324,1411,1412],{},"\u003Cparent>","; the BOM aligns dependency versions.",[320,1415,1416],{},"The same table as a graph — solid arrows are dependencies, the dashed one is the build-time conventions:",[450,1418,1422],{"className":1419,"code":1420,"language":1421,"meta":456,"style":456},"language-mermaid shiki shiki-themes vitesse-light","flowchart LR\n    APP[\"your app\"] --> RT[\"stoatflow-runtime\"]\n    RT --> CORE[\"stoatflow-core\"]\n    TESTS[\"your tests\"] --> TRT[\"stoatflow-test-runtime\"]\n    TRT --> TU[\"stoatflow-test-utils\"]\n    TRT --> RT\n    TU --> CORE\n    BUILD[\"build conventions —\u003Cbr\u002F>io.stoatflow plugin ·\u003Cbr\u002F>stoatflow-parent POM\"] -.applied to the build.-> APP\n","mermaid",[324,1423,1424,1429,1434,1439,1444,1449,1454,1459],{"__ignoreMap":456},[460,1425,1426],{"class":462,"line":463},[460,1427,1428],{"class":502},"flowchart LR\n",[460,1430,1431],{"class":462,"line":474},[460,1432,1433],{"class":502},"    APP[\"your app\"] --> RT[\"stoatflow-runtime\"]\n",[460,1435,1436],{"class":462,"line":482},[460,1437,1438],{"class":502},"    RT --> CORE[\"stoatflow-core\"]\n",[460,1440,1441],{"class":462,"line":714},[460,1442,1443],{"class":502},"    TESTS[\"your tests\"] --> TRT[\"stoatflow-test-runtime\"]\n",[460,1445,1446],{"class":462,"line":731},[460,1447,1448],{"class":502},"    TRT --> TU[\"stoatflow-test-utils\"]\n",[460,1450,1451],{"class":462,"line":737},[460,1452,1453],{"class":502},"    TRT --> RT\n",[460,1455,1456],{"class":462,"line":749},[460,1457,1458],{"class":502},"    TU --> CORE\n",[460,1460,1461],{"class":462,"line":1228},[460,1462,1463],{"class":502},"    BUILD[\"build conventions —\u003Cbr\u002F>io.stoatflow plugin ·\u003Cbr\u002F>stoatflow-parent POM\"] -.applied to the build.-> APP\n",[320,1465,1466,1467,1469,1470,1472,1473,1475,1476,1478,1479,632,1481,334],{},"In practice the common path is: depend on ",[324,1468,349],{}," for your app, ",[324,1471,380],{}," for your tests, and apply the StoatFlow build conventions (the ",[324,1474,326],{}," Gradle plugin, or the ",[324,1477,397],{}," POM on Maven). A DSL-only embedding swaps the first two for ",[324,1480,353],{},[324,1482,368],{},[320,1484,1485],{},"A minimal dependency block:",[446,1487,1488,1558],{"group":1383},[450,1489,1492],{"className":452,"code":1490,"filename":1491,"language":455,"meta":456,"style":456},"\u002F\u002F build.gradle.kts\ndependencies {\n    implementation(\"io.stoatflow:stoatflow-runtime:\u003Cstoatflow-version>\")\n    \u002F\u002F ...or DSL only:\n    \u002F\u002F implementation(\"io.stoatflow:stoatflow-core:\u003Cstoatflow-version>\")\n\n    testImplementation(\"io.stoatflow:stoatflow-test-runtime:\u003Cstoatflow-version>\")\n    \u002F\u002F ...or, for a core-only project:\n    \u002F\u002F testImplementation(\"io.stoatflow:stoatflow-test-utils:\u003Cstoatflow-version>\")\n}\n","Gradle",[324,1493,1494,1498,1505,1517,1522,1527,1531,1543,1548,1553],{"__ignoreMap":456},[460,1495,1496],{"class":462,"line":463},[460,1497,1169],{"class":1168},[460,1499,1500,1503],{"class":462,"line":474},[460,1501,1502],{"class":470},"dependencies",[460,1504,1177],{"class":502},[460,1506,1507,1510,1512,1515],{"class":462,"line":482},[460,1508,1509],{"class":470},"    implementation",[460,1511,810],{"class":502},[460,1513,1514],{"class":936},"\"io.stoatflow:stoatflow-runtime:\u003Cstoatflow-version>\"",[460,1516,734],{"class":502},[460,1518,1519],{"class":462,"line":714},[460,1520,1521],{"class":1168},"    \u002F\u002F ...or DSL only:\n",[460,1523,1524],{"class":462,"line":731},[460,1525,1526],{"class":1168},"    \u002F\u002F implementation(\"io.stoatflow:stoatflow-core:\u003Cstoatflow-version>\")\n",[460,1528,1529],{"class":462,"line":737},[460,1530,691],{"emptyLinePlaceholder":690},[460,1532,1533,1536,1538,1541],{"class":462,"line":749},[460,1534,1535],{"class":470},"    testImplementation",[460,1537,810],{"class":502},[460,1539,1540],{"class":936},"\"io.stoatflow:stoatflow-test-runtime:\u003Cstoatflow-version>\"",[460,1542,734],{"class":502},[460,1544,1545],{"class":462,"line":1228},[460,1546,1547],{"class":1168},"    \u002F\u002F ...or, for a core-only project:\n",[460,1549,1550],{"class":462,"line":1244},[460,1551,1552],{"class":1168},"    \u002F\u002F testImplementation(\"io.stoatflow:stoatflow-test-utils:\u003Cstoatflow-version>\")\n",[460,1554,1556],{"class":462,"line":1555},10,[460,1557,1118],{"class":502},[450,1559,1564],{"className":1560,"code":1561,"filename":1562,"language":1563,"meta":456,"style":456},"language-xml shiki shiki-themes vitesse-light","\u003C!-- pom.xml -->\n\u003Cdependencies>\n  \u003Cdependency>\n    \u003CgroupId>io.stoatflow\u003C\u002FgroupId>\n    \u003CartifactId>stoatflow-runtime\u003C\u002FartifactId>\n    \u003Cversion>${stoatflow.version}\u003C\u002Fversion>\n  \u003C\u002Fdependency>\n  \u003C!-- ...or DSL only: stoatflow-core -->\n\n  \u003Cdependency>\n    \u003CgroupId>io.stoatflow\u003C\u002FgroupId>\n    \u003CartifactId>stoatflow-test-runtime\u003C\u002FartifactId>\n    \u003Cversion>${stoatflow.version}\u003C\u002Fversion>\n    \u003Cscope>test\u003C\u002Fscope>\n  \u003C\u002Fdependency>\n  \u003C!-- ...or, for a core-only project: stoatflow-test-utils -->\n\u003C\u002Fdependencies>\n","Maven","xml",[324,1565,1566,1571,1576,1581,1586,1591,1596,1601,1606,1610,1614,1619,1625,1630,1636,1641,1647],{"__ignoreMap":456},[460,1567,1568],{"class":462,"line":463},[460,1569,1570],{},"\u003C!-- pom.xml -->\n",[460,1572,1573],{"class":462,"line":474},[460,1574,1575],{},"\u003Cdependencies>\n",[460,1577,1578],{"class":462,"line":482},[460,1579,1580],{},"  \u003Cdependency>\n",[460,1582,1583],{"class":462,"line":714},[460,1584,1585],{},"    \u003CgroupId>io.stoatflow\u003C\u002FgroupId>\n",[460,1587,1588],{"class":462,"line":731},[460,1589,1590],{},"    \u003CartifactId>stoatflow-runtime\u003C\u002FartifactId>\n",[460,1592,1593],{"class":462,"line":737},[460,1594,1595],{},"    \u003Cversion>${stoatflow.version}\u003C\u002Fversion>\n",[460,1597,1598],{"class":462,"line":749},[460,1599,1600],{},"  \u003C\u002Fdependency>\n",[460,1602,1603],{"class":462,"line":1228},[460,1604,1605],{},"  \u003C!-- ...or DSL only: stoatflow-core -->\n",[460,1607,1608],{"class":462,"line":1244},[460,1609,691],{"emptyLinePlaceholder":690},[460,1611,1612],{"class":462,"line":1555},[460,1613,1580],{},[460,1615,1617],{"class":462,"line":1616},11,[460,1618,1585],{},[460,1620,1622],{"class":462,"line":1621},12,[460,1623,1624],{},"    \u003CartifactId>stoatflow-test-runtime\u003C\u002FartifactId>\n",[460,1626,1628],{"class":462,"line":1627},13,[460,1629,1595],{},[460,1631,1633],{"class":462,"line":1632},14,[460,1634,1635],{},"    \u003Cscope>test\u003C\u002Fscope>\n",[460,1637,1639],{"class":462,"line":1638},15,[460,1640,1600],{},[460,1642,1644],{"class":462,"line":1643},16,[460,1645,1646],{},"  \u003C!-- ...or, for a core-only project: stoatflow-test-utils -->\n",[460,1648,1650],{"class":462,"line":1649},17,[460,1651,1652],{},"\u003C\u002Fdependencies>\n",[1654,1655,1658],"callout",{"color":1656,"icon":1657},"info","i-lucide-info",[320,1659,1660,1661,1664,1665,1668,1669,1672,1673,334],{},"StoatFlow is in ",[345,1662,1663],{},"alpha",". The current version is ",[1666,1667],"stoatflow-version",{}," — substitute it for the ",[324,1670,1671],{},"\u003Cstoatflow-version>"," placeholder above. Full coordinates and credential setup are in ",[329,1674,18],{"href":19},[400,1676,1678],{"id":1677},"next-steps","Next steps",[339,1680,1681,1688,1698,1707],{},[342,1682,1683,1687],{},[345,1684,1685],{},[329,1686,18],{"href":19}," — the private Maven repository, credentials, exact coordinates, and JVM toolchain.",[342,1689,1690,1694,1695,1697],{},[345,1691,1692],{},[329,1693,33],{"href":34}," — applying the ",[324,1696,326],{}," Gradle plugin in full.",[342,1699,1700,1704,1705,334],{},[345,1701,1702],{},[329,1703,28],{"href":29}," — build and run a complete word-count app on ",[324,1706,349],{},[342,1708,1709,1713],{},[345,1710,1711],{},[329,1712,47],{"href":48}," — how the single-instance engine runs your topology.",[1715,1716,1717],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sbBg2, html code.shiki .sbBg2{--shiki-default:#1E754F}html pre.shiki code .sySUi, html code.shiki .sySUi{--shiki-default:#59873A}html pre.shiki code .suHK_, html code.shiki .suHK_{--shiki-default:#393A34}html pre.shiki code .sYZai, html code.shiki .sYZai{--shiki-default:#999999}html pre.shiki code .si04Y, html code.shiki .si04Y{--shiki-default:#AB5959}html pre.shiki code .svycV, html code.shiki .svycV{--shiki-default:#B07D48}html pre.shiki code .sUxyF, html code.shiki .sUxyF{--shiki-default:#2E8F82}html pre.shiki code .spphp, html code.shiki .spphp{--shiki-default:#B56959}html pre.shiki code .s8zF2, html code.shiki .s8zF2{--shiki-default:#A0ADA0}",{"title":456,"searchDepth":474,"depth":474,"links":1719},[1720,1726,1732,1733,1734],{"id":402,"depth":474,"text":403,"children":1721},[1722,1724],{"id":410,"depth":482,"text":1723},"stoatflow-core — DSL + engine",{"id":581,"depth":482,"text":1725},"stoatflow-runtime — batteries-included wrapper",{"id":863,"depth":474,"text":864,"children":1727},[1728,1730],{"id":870,"depth":482,"text":1729},"stoatflow-test-utils — the broker-free test driver",{"id":1048,"depth":482,"text":1731},"stoatflow-test-runtime — the same driver, from your runtime config",{"id":1135,"depth":474,"text":1136},{"id":1265,"depth":474,"text":1266},{"id":1677,"depth":474,"text":1678},"The StoatFlow module landscape — core, runtime, the two testing modules, and the io.stoatflow Gradle plugin — and how to decide which to depend on.","md",{},{"icon":16},"explanation",{"title":13,"description":1735},"QVVt9K0VsXEg8dR8g7rkyZKk6HNYq2GbAFufeO9U_Dc",[1743,1745],{"title":10,"path":6,"stem":7,"description":1744,"icon":11,"children":-1},"Install StoatFlow and ship your first topology — or migrate an existing Kafka Streams app.",{"title":18,"path":19,"stem":20,"description":1746,"icon":21,"children":-1},"Add the private StoatFlow Maven repository, pull the dependency, and set up your Gradle or Maven build.",1783713349980]