[{"data":1,"prerenderedAt":1319},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fmigration\u002Fautomated-port":334,"\u002Fdocs\u002Fmigration\u002Fautomated-port-surround":1314},[4,43,91,154,181,224,262,293],{"title":5,"path":6,"stem":7,"children":8,"icon":42},"Getting Started","\u002Fdocs\u002Fgetting-started","1.docs\u002F1.getting-started\u002F1.index",[9,12,17,22,27,32,37],{"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",{"title":38,"path":39,"stem":40,"icon":41},"AI assistants","\u002Fdocs\u002Fgetting-started\u002Fai-assistants","1.docs\u002F1.getting-started\u002F7.ai-assistants","i-lucide-bot",false,{"title":44,"path":45,"stem":46,"children":47,"icon":50},"Core Concepts","\u002Fdocs\u002Fconcepts","1.docs\u002F2.concepts\u002F1.index",[48,51,56,61,66,71,76,81,86],{"title":49,"path":45,"stem":46,"icon":50},"Core concepts","i-lucide-book-open",{"title":52,"path":53,"stem":54,"icon":55},"Architecture","\u002Fdocs\u002Fconcepts\u002Farchitecture","1.docs\u002F2.concepts\u002F2.architecture","i-lucide-layers",{"title":57,"path":58,"stem":59,"icon":60},"Exactly-once semantics","\u002Fdocs\u002Fconcepts\u002Fexactly-once","1.docs\u002F2.concepts\u002F3.exactly-once","i-lucide-shield-check",{"title":62,"path":63,"stem":64,"icon":65},"Lanes and parallelism","\u002Fdocs\u002Fconcepts\u002Flanes-and-parallelism","1.docs\u002F2.concepts\u002F4.lanes-and-parallelism","i-lucide-split",{"title":67,"path":68,"stem":69,"icon":70},"State and thread-safety","\u002Fdocs\u002Fconcepts\u002Fstate-and-thread-safety","1.docs\u002F2.concepts\u002F5.state-and-thread-safety","i-lucide-lock",{"title":72,"path":73,"stem":74,"icon":75},"Event time and watermarks","\u002Fdocs\u002Fconcepts\u002Fevent-time-and-watermarks","1.docs\u002F2.concepts\u002F6.event-time-and-watermarks","i-lucide-clock",{"title":77,"path":78,"stem":79,"icon":80},"The configuration model","\u002Fdocs\u002Fconcepts\u002Fconfiguration-model","1.docs\u002F2.concepts\u002F7.configuration-model","i-lucide-sliders-horizontal",{"title":82,"path":83,"stem":84,"icon":85},"The error-handling model","\u002Fdocs\u002Fconcepts\u002Ferror-handling-model","1.docs\u002F2.concepts\u002F8.error-handling-model","i-lucide-triangle-alert",{"title":87,"path":88,"stem":89,"icon":90},"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":92,"path":93,"stem":94,"children":95,"icon":98},"Building Topologies","\u002Fdocs\u002Fbuilding","1.docs\u002F3.building\u002F1.index",[96,99,104,109,114,119,124,129,134,139,144,149],{"title":97,"path":93,"stem":94,"icon":98},"Building topologies","i-lucide-blocks",{"title":100,"path":101,"stem":102,"icon":103},"Error handling and DLQ","\u002Fdocs\u002Fbuilding\u002Ferror-handling-dlq","1.docs\u002F3.building\u002F10.error-handling-dlq","i-lucide-circle-x",{"title":105,"path":106,"stem":107,"icon":108},"State stores","\u002Fdocs\u002Fbuilding\u002Fstate-stores","1.docs\u002F3.building\u002F11.state-stores","i-lucide-database",{"title":110,"path":111,"stem":112,"icon":113},"Testing topologies","\u002Fdocs\u002Fbuilding\u002Ftesting","1.docs\u002F3.building\u002F12.testing","i-lucide-flask-conical",{"title":115,"path":116,"stem":117,"icon":118},"Sources and sinks","\u002Fdocs\u002Fbuilding\u002Fstreams-builder","1.docs\u002F3.building\u002F2.streams-builder","i-lucide-import",{"title":120,"path":121,"stem":122,"icon":123},"KStream and KTable operations","\u002Fdocs\u002Fbuilding\u002Fkstream-ktable","1.docs\u002F3.building\u002F3.kstream-ktable","i-lucide-waypoints",{"title":125,"path":126,"stem":127,"icon":128},"Aggregations","\u002Fdocs\u002Fbuilding\u002Faggregations","1.docs\u002F3.building\u002F4.aggregations","i-lucide-sigma",{"title":130,"path":131,"stem":132,"icon":133},"Windowing","\u002Fdocs\u002Fbuilding\u002Fwindowing","1.docs\u002F3.building\u002F5.windowing","i-lucide-calendar-clock",{"title":135,"path":136,"stem":137,"icon":138},"Joins","\u002Fdocs\u002Fbuilding\u002Fjoins","1.docs\u002F3.building\u002F6.joins","i-lucide-git-merge",{"title":140,"path":141,"stem":142,"icon":143},"The Processor API","\u002Fdocs\u002Fbuilding\u002Fprocessor-api","1.docs\u002F3.building\u002F7.processor-api","i-lucide-cpu",{"title":145,"path":146,"stem":147,"icon":148},"Scheduled sources","\u002Fdocs\u002Fbuilding\u002Fscheduled-sources","1.docs\u002F3.building\u002F8.scheduled-sources","i-lucide-timer",{"title":150,"path":151,"stem":152,"icon":153},"Serdes and Avro","\u002Fdocs\u002Fbuilding\u002Fserdes","1.docs\u002F3.building\u002F9.serdes","i-lucide-binary",{"title":155,"path":156,"stem":157,"children":158,"icon":80},"Configuration","\u002Fdocs\u002Fconfiguration","1.docs\u002F4.configuration\u002F1.index",[159,161,166,171,176],{"title":160,"path":156,"stem":157,"icon":80},"How configuration works",{"title":162,"path":163,"stem":164,"icon":165},"Engine configuration (:core)","\u002Fdocs\u002Fconfiguration\u002Fcore-config","1.docs\u002F4.configuration\u002F2.core-config","i-lucide-settings-2",{"title":167,"path":168,"stem":169,"icon":170},"Runtime configuration (:runtime)","\u002Fdocs\u002Fconfiguration\u002Fruntime-config","1.docs\u002F4.configuration\u002F3.runtime-config","i-lucide-server-cog",{"title":172,"path":173,"stem":174,"icon":175},"Defaults, adaptivity, and presets","\u002Fdocs\u002Fconfiguration\u002Fdefaults-and-presets","1.docs\u002F4.configuration\u002F4.defaults-and-presets","i-lucide-gauge",{"title":177,"path":178,"stem":179,"icon":180},"Kafka client configuration","\u002Fdocs\u002Fconfiguration\u002Fkafka-client-config","1.docs\u002F4.configuration\u002F5.kafka-client-config","i-lucide-plug",{"title":182,"path":183,"stem":184,"children":185,"icon":188},"Running in Production","\u002Fdocs\u002Fruntime","1.docs\u002F5.runtime\u002F1.index",[186,189,194,199,204,209,214,219],{"title":187,"path":183,"stem":184,"icon":188},"The runtime","i-lucide-server",{"title":190,"path":191,"stem":192,"icon":193},"The REST API","\u002Fdocs\u002Fruntime\u002Frest-api","1.docs\u002F5.runtime\u002F2.rest-api","i-lucide-globe",{"title":195,"path":196,"stem":197,"icon":198},"Health checks","\u002Fdocs\u002Fruntime\u002Fhealth-checks","1.docs\u002F5.runtime\u002F3.health-checks","i-lucide-heart-pulse",{"title":200,"path":201,"stem":202,"icon":203},"Metrics","\u002Fdocs\u002Fruntime\u002Fmetrics","1.docs\u002F5.runtime\u002F4.metrics","i-lucide-activity",{"title":205,"path":206,"stem":207,"icon":208},"Pause and resume","\u002Fdocs\u002Fruntime\u002Fpause-unpause","1.docs\u002F5.runtime\u002F5.pause-unpause","i-lucide-pause",{"title":210,"path":211,"stem":212,"icon":213},"Plugins and lifecycle hooks","\u002Fdocs\u002Fruntime\u002Fplugins","1.docs\u002F5.runtime\u002F6.plugins","i-lucide-puzzle",{"title":215,"path":216,"stem":217,"icon":218},"Docker images","\u002Fdocs\u002Fruntime\u002Fdocker","1.docs\u002F5.runtime\u002F7.docker","i-lucide-container",{"title":220,"path":221,"stem":222,"icon":223},"GraalVM native image","\u002Fdocs\u002Fruntime\u002Fnative-image","1.docs\u002F5.runtime\u002F8.native-image","i-lucide-zap",{"title":225,"path":226,"stem":227,"children":228,"icon":231},"Deploying & Operating","\u002Fdocs\u002Foperating","1.docs\u002F6.operating\u002F1.index",[229,232,237,242,247,252,257],{"title":230,"path":226,"stem":227,"icon":231},"Deploying and operating","i-lucide-life-buoy",{"title":233,"path":234,"stem":235,"icon":236},"Running on Kubernetes","\u002Fdocs\u002Foperating\u002Fkubernetes","1.docs\u002F6.operating\u002F2.kubernetes","i-lucide-ship",{"title":238,"path":239,"stem":240,"icon":241},"High availability","\u002Fdocs\u002Foperating\u002Fhigh-availability","1.docs\u002F6.operating\u002F3.high-availability","i-lucide-copy",{"title":243,"path":244,"stem":245,"icon":246},"Liveness and readiness probes","\u002Fdocs\u002Foperating\u002Fprobes","1.docs\u002F6.operating\u002F4.probes","i-lucide-stethoscope",{"title":248,"path":249,"stem":250,"icon":251},"Observability","\u002Fdocs\u002Foperating\u002Fobservability","1.docs\u002F6.operating\u002F5.observability","i-lucide-telescope",{"title":253,"path":254,"stem":255,"icon":256},"Tuning under load","\u002Fdocs\u002Foperating\u002Ftuning","1.docs\u002F6.operating\u002F6.tuning","i-lucide-sliders",{"title":258,"path":259,"stem":260,"icon":261},"Production checklist","\u002Fdocs\u002Foperating\u002Fproduction-checklist","1.docs\u002F6.operating\u002F7.production-checklist","i-lucide-clipboard-check",{"title":263,"path":264,"stem":265,"children":266,"icon":90},"Migrating from Kafka Streams","\u002Fdocs\u002Fmigration","1.docs\u002F7.migration\u002F1.index",[267,268,273,278,283,288],{"title":263,"path":264,"stem":265,"icon":90},{"title":269,"path":270,"stem":271,"icon":272},"Automated port","\u002Fdocs\u002Fmigration\u002Fautomated-port","1.docs\u002F7.migration\u002F2.automated-port","i-lucide-wand-sparkles",{"title":274,"path":275,"stem":276,"icon":277},"Migration without carrying state","\u002Fdocs\u002Fmigration\u002Fwithout-data-migration","1.docs\u002F7.migration\u002F3.without-data-migration","i-lucide-sparkles",{"title":279,"path":280,"stem":281,"icon":282},"Migration carrying state","\u002Fdocs\u002Fmigration\u002Fwith-data-migration","1.docs\u002F7.migration\u002F4.with-data-migration","i-lucide-database-backup",{"title":284,"path":285,"stem":286,"icon":287},"The migration tool","\u002Fdocs\u002Fmigration\u002Fmigration-tool","1.docs\u002F7.migration\u002F5.migration-tool","i-lucide-truck",{"title":289,"path":290,"stem":291,"icon":292},"Reusing your Kafka Streams dashboards","\u002Fdocs\u002Fmigration\u002Freusing-kafka-streams-dashboards","1.docs\u002F7.migration\u002F6.reusing-kafka-streams-dashboards","i-lucide-line-chart",{"title":294,"path":295,"stem":296,"children":297,"icon":299},"Reference","\u002Fdocs\u002Freference","1.docs\u002F8.reference\u002F1.index",[298,300,305,310,315,320,325,329],{"title":294,"path":295,"stem":296,"icon":299},"i-lucide-list",{"title":301,"path":302,"stem":303,"icon":304},"Configuration reference","\u002Fdocs\u002Freference\u002Fconfiguration-reference","1.docs\u002F8.reference\u002F2.configuration-reference","i-lucide-table",{"title":306,"path":307,"stem":308,"icon":309},"REST API reference","\u002Fdocs\u002Freference\u002Frest-api-reference","1.docs\u002F8.reference\u002F3.rest-api-reference","i-lucide-network",{"title":311,"path":312,"stem":313,"icon":314},"Gradle plugin reference","\u002Fdocs\u002Freference\u002Fgradle-plugin-reference","1.docs\u002F8.reference\u002F4.gradle-plugin-reference","i-lucide-box",{"title":316,"path":317,"stem":318,"icon":319},"Maven reference","\u002Fdocs\u002Freference\u002Fmaven-reference","1.docs\u002F8.reference\u002F5.maven-reference","i-simple-icons-apachemaven",{"title":321,"path":322,"stem":323,"icon":324},"Kafka Streams compatibility matrix","\u002Fdocs\u002Freference\u002Fks-compatibility-matrix","1.docs\u002F8.reference\u002F6.ks-compatibility-matrix","i-lucide-table-2",{"title":326,"path":327,"stem":328,"icon":175},"Metrics reference","\u002Fdocs\u002Freference\u002Fmetrics-reference","1.docs\u002F8.reference\u002F7.metrics-reference",{"title":330,"path":331,"stem":332,"icon":333},"Glossary","\u002Fdocs\u002Freference\u002Fglossary","1.docs\u002F8.reference\u002F8.glossary","i-lucide-book-a",{"id":335,"title":269,"body":336,"description":1307,"extension":1308,"meta":1309,"navigation":1310,"pageType":1311,"path":270,"seo":1312,"stem":271,"__hash__":1313},"docs\u002F1.docs\u002F7.migration\u002F2.automated-port.md",{"type":337,"value":338,"toc":1299},"minimark",[339,348,388,393,396,411,418,434,863,880,884,890,999,1003,1018,1147,1166,1173,1177,1222,1225,1229,1266,1270,1273,1289,1295],[340,341,342,343,347],"p",{},"Most of a port from Kafka Streams to StoatFlow is import replacement. The ",[344,345,346],"code",{},"stoatflow-openrewrite-recipe"," recipe does that replacement for you — on Java and Kotlin alike — rewrites the application entry point, swaps your Maven coordinates, and then tells you, precisely, about the handful of things it refused to change on your behalf.",[349,350,351],"tldr-panel",{},[352,353,354,370,376,382],"ul",{},[355,356,357,361,362,365,366,369],"li",{},[358,359,360],"strong",{},"One command, with a dry run first."," ",[344,363,364],{},"rewriteDryRun"," writes a diff and changes nothing; ",[344,367,368],{},"rewriteRun"," applies it.",[355,371,372,375],{},[358,373,374],{},"It reads types, not text."," The recipe operates on a type-attributed syntax tree, so it never rewrites a package name that happens to appear inside a string literal or a comment — and it fixes the eight types a naive prefix sweep sends to a package that doesn't exist.",[355,377,378,381],{},[358,379,380],{},"It never guesses."," Semantics that changed, types with no StoatFlow equivalent, an entry point whose builder it cannot resolve: each is flagged inline and listed in a CSV report, never silently rewritten.",[355,383,384,387],{},[358,385,386],{},"Run it against a project that still compiles."," The rules match resolved types; anything that does not type-check is skipped and reported. JDK 17+ (25 included) with a current OpenRewrite plugin.",[389,390,392],"h2",{"id":391},"before-you-run-it","Before you run it",[340,394,395],{},"Any JDK from 17 up — 25 included — runs the migration. One version caveat, for Kotlin codebases only:",[397,398,400],"callout",{"color":399,"icon":85},"warning",[340,401,402,405,406,410],{},[358,403,404],{},"On JDK 25, use a current OpenRewrite plugin."," Plugin releases from before February 2026 bundle a Kotlin compiler that throws on Java 25 — every Kotlin source then fails to parse, and because the migration rules match on resolved types they skip those files ",[407,408,409],"em",{},"without an error",": the run reports success and changes nothing. The plugin versions in the snippets below are past the fix. The recipe reports each unparsed file either way, so a silent skip is never invisible.",[340,412,413,414,417],{},"The recipe must also run against a project that ",[358,415,416],{},"still compiles against Kafka Streams",". The rules match resolved types, so a source file that doesn't type-check is skipped. Migrate a green build.",[340,419,420,421,425,426,429,430,433],{},"Finally, you need repository access — the same credentials you use to depend on StoatFlow at all. Follow ",[422,423,424],"a",{"href":19},"Store your credentials"," if you have not already. For Maven, the repository must be visible to ",[344,427,428],{},"\u003CpluginRepositories>"," as well as ",[344,431,432],{},"\u003Crepositories>",", because the recipe resolves as a plugin dependency:",[435,436,438,574],"code-tabs",{"group":437},"build",[439,440,446],"pre",{"className":441,"code":442,"filename":443,"language":444,"meta":445,"style":445},"language-xml shiki shiki-themes vitesse-light","\u003C!-- ~\u002F.m2\u002Fsettings.xml — add a profile so the repo is visible to BOTH lists -->\n\u003Cprofiles>\n  \u003Cprofile>\n    \u003Cid>stoatflow\u003C\u002Fid>\n    \u003Crepositories>\n      \u003Crepository>\n        \u003Cid>stoatflow-releases\u003C\u002Fid>\n        \u003Curl>https:\u002F\u002Fmaven.stoatflow.io\u002Freleases\u003C\u002Furl>\n      \u003C\u002Frepository>\n    \u003C\u002Frepositories>\n    \u003CpluginRepositories>\n      \u003CpluginRepository>\n        \u003Cid>stoatflow-releases\u003C\u002Fid>\n        \u003Curl>https:\u002F\u002Fmaven.stoatflow.io\u002Freleases\u003C\u002Furl>\n      \u003C\u002FpluginRepository>\n    \u003C\u002FpluginRepositories>\n  \u003C\u002Fprofile>\n\u003C\u002Fprofiles>\n\u003CactiveProfiles>\n  \u003CactiveProfile>stoatflow\u003C\u002FactiveProfile>\n\u003C\u002FactiveProfiles>\n","Maven","xml","",[344,447,448,456,462,468,474,480,486,492,498,504,510,516,522,527,532,538,544,550,556,562,568],{"__ignoreMap":445},[449,450,453],"span",{"class":451,"line":452},"line",1,[449,454,455],{},"\u003C!-- ~\u002F.m2\u002Fsettings.xml — add a profile so the repo is visible to BOTH lists -->\n",[449,457,459],{"class":451,"line":458},2,[449,460,461],{},"\u003Cprofiles>\n",[449,463,465],{"class":451,"line":464},3,[449,466,467],{},"  \u003Cprofile>\n",[449,469,471],{"class":451,"line":470},4,[449,472,473],{},"    \u003Cid>stoatflow\u003C\u002Fid>\n",[449,475,477],{"class":451,"line":476},5,[449,478,479],{},"    \u003Crepositories>\n",[449,481,483],{"class":451,"line":482},6,[449,484,485],{},"      \u003Crepository>\n",[449,487,489],{"class":451,"line":488},7,[449,490,491],{},"        \u003Cid>stoatflow-releases\u003C\u002Fid>\n",[449,493,495],{"class":451,"line":494},8,[449,496,497],{},"        \u003Curl>https:\u002F\u002Fmaven.stoatflow.io\u002Freleases\u003C\u002Furl>\n",[449,499,501],{"class":451,"line":500},9,[449,502,503],{},"      \u003C\u002Frepository>\n",[449,505,507],{"class":451,"line":506},10,[449,508,509],{},"    \u003C\u002Frepositories>\n",[449,511,513],{"class":451,"line":512},11,[449,514,515],{},"    \u003CpluginRepositories>\n",[449,517,519],{"class":451,"line":518},12,[449,520,521],{},"      \u003CpluginRepository>\n",[449,523,525],{"class":451,"line":524},13,[449,526,491],{},[449,528,530],{"class":451,"line":529},14,[449,531,497],{},[449,533,535],{"class":451,"line":534},15,[449,536,537],{},"      \u003C\u002FpluginRepository>\n",[449,539,541],{"class":451,"line":540},16,[449,542,543],{},"    \u003C\u002FpluginRepositories>\n",[449,545,547],{"class":451,"line":546},17,[449,548,549],{},"  \u003C\u002Fprofile>\n",[449,551,553],{"class":451,"line":552},18,[449,554,555],{},"\u003C\u002Fprofiles>\n",[449,557,559],{"class":451,"line":558},19,[449,560,561],{},"\u003CactiveProfiles>\n",[449,563,565],{"class":451,"line":564},20,[449,566,567],{},"  \u003CactiveProfile>stoatflow\u003C\u002FactiveProfile>\n",[449,569,571],{"class":451,"line":570},21,[449,572,573],{},"\u003C\u002FactiveProfiles>\n",[439,575,580],{"className":576,"code":577,"filename":578,"language":579,"meta":445,"style":445},"language-kotlin shiki shiki-themes vitesse-light","\u002F\u002F rewrite.init.gradle.kts — no change to your build files at all.\ninitscript {\n    repositories { maven { url = uri(\"https:\u002F\u002Fplugins.gradle.org\u002Fm2\") } }\n    dependencies { classpath(\"org.openrewrite:plugin:7.39.0\") }\n}\n\nrootProject {\n    apply(plugin = \"org.openrewrite.rewrite\")\n    repositories {\n        mavenCentral()\n        maven {\n            url = uri(\"https:\u002F\u002Fmaven.stoatflow.io\u002Freleases\")\n            credentials {\n                username = providers.gradleProperty(\"stoatflowRepoUser\").get()\n                password = providers.gradleProperty(\"stoatflowRepoToken\").get()\n            }\n        }\n    }\n    dependencies { add(\"rewrite\", \"io.stoatflow:stoatflow-openrewrite-recipe:VERSION\") }\n    configure\u003Corg.openrewrite.gradle.RewriteExtension> {\n        activeRecipe(\"io.stoatflow.rewrite.MigrateFromKafkaStreams\")\n    }\n}\n","Gradle","kotlin",[344,581,582,588,598,629,647,652,658,665,681,687,695,702,718,725,751,773,778,783,788,810,841,853,858],{"__ignoreMap":445},[449,583,584],{"class":451,"line":452},[449,585,587],{"class":586},"s8zF2","\u002F\u002F rewrite.init.gradle.kts — no change to your build files at all.\n",[449,589,590,594],{"class":451,"line":458},[449,591,593],{"class":592},"sySUi","initscript",[449,595,597],{"class":596},"suHK_"," {\n",[449,599,600,603,606,609,612,616,619,622,626],{"class":451,"line":464},[449,601,602],{"class":592},"    repositories",[449,604,605],{"class":596}," { ",[449,607,608],{"class":592},"maven",[449,610,611],{"class":596}," { url ",[449,613,615],{"class":614},"sYZai","=",[449,617,618],{"class":592}," uri",[449,620,621],{"class":596},"(",[449,623,625],{"class":624},"spphp","\"https:\u002F\u002Fplugins.gradle.org\u002Fm2\"",[449,627,628],{"class":596},") } }\n",[449,630,631,634,636,639,641,644],{"class":451,"line":470},[449,632,633],{"class":592},"    dependencies",[449,635,605],{"class":596},[449,637,638],{"class":592},"classpath",[449,640,621],{"class":596},[449,642,643],{"class":624},"\"org.openrewrite:plugin:7.39.0\"",[449,645,646],{"class":596},") }\n",[449,648,649],{"class":451,"line":476},[449,650,651],{"class":596},"}\n",[449,653,654],{"class":451,"line":482},[449,655,657],{"emptyLinePlaceholder":656},true,"\n",[449,659,660,663],{"class":451,"line":488},[449,661,662],{"class":592},"rootProject",[449,664,597],{"class":596},[449,666,667,670,673,675,678],{"class":451,"line":494},[449,668,669],{"class":592},"    apply",[449,671,672],{"class":596},"(plugin ",[449,674,615],{"class":614},[449,676,677],{"class":624}," \"org.openrewrite.rewrite\"",[449,679,680],{"class":596},")\n",[449,682,683,685],{"class":451,"line":500},[449,684,602],{"class":592},[449,686,597],{"class":596},[449,688,689,692],{"class":451,"line":506},[449,690,691],{"class":592},"        mavenCentral",[449,693,694],{"class":596},"()\n",[449,696,697,700],{"class":451,"line":512},[449,698,699],{"class":592},"        maven",[449,701,597],{"class":596},[449,703,704,707,709,711,713,716],{"class":451,"line":518},[449,705,706],{"class":596},"            url ",[449,708,615],{"class":614},[449,710,618],{"class":592},[449,712,621],{"class":596},[449,714,715],{"class":624},"\"https:\u002F\u002Fmaven.stoatflow.io\u002Freleases\"",[449,717,680],{"class":596},[449,719,720,723],{"class":451,"line":524},[449,721,722],{"class":592},"            credentials",[449,724,597],{"class":596},[449,726,727,730,732,735,738,740,743,746,749],{"class":451,"line":529},[449,728,729],{"class":596},"                username ",[449,731,615],{"class":614},[449,733,734],{"class":596}," providers.",[449,736,737],{"class":592},"gradleProperty",[449,739,621],{"class":596},[449,741,742],{"class":624},"\"stoatflowRepoUser\"",[449,744,745],{"class":596},").",[449,747,748],{"class":592},"get",[449,750,694],{"class":596},[449,752,753,756,758,760,762,764,767,769,771],{"class":451,"line":534},[449,754,755],{"class":596},"                password ",[449,757,615],{"class":614},[449,759,734],{"class":596},[449,761,737],{"class":592},[449,763,621],{"class":596},[449,765,766],{"class":624},"\"stoatflowRepoToken\"",[449,768,745],{"class":596},[449,770,748],{"class":592},[449,772,694],{"class":596},[449,774,775],{"class":451,"line":540},[449,776,777],{"class":596},"            }\n",[449,779,780],{"class":451,"line":546},[449,781,782],{"class":596},"        }\n",[449,784,785],{"class":451,"line":552},[449,786,787],{"class":596},"    }\n",[449,789,790,792,794,797,799,802,805,808],{"class":451,"line":558},[449,791,633],{"class":592},[449,793,605],{"class":596},[449,795,796],{"class":592},"add",[449,798,621],{"class":596},[449,800,801],{"class":624},"\"rewrite\"",[449,803,804],{"class":596},", ",[449,806,807],{"class":624},"\"io.stoatflow:stoatflow-openrewrite-recipe:VERSION\"",[449,809,646],{"class":596},[449,811,812,815,818,822,825,828,830,833,835,838],{"class":451,"line":564},[449,813,814],{"class":592},"    configure",[449,816,817],{"class":596},"\u003C",[449,819,821],{"class":820},"sUxyF","org",[449,823,824],{"class":596},".",[449,826,827],{"class":820},"openrewrite",[449,829,824],{"class":596},[449,831,832],{"class":820},"gradle",[449,834,824],{"class":596},[449,836,837],{"class":820},"RewriteExtension",[449,839,840],{"class":596},"> {\n",[449,842,843,846,848,851],{"class":451,"line":570},[449,844,845],{"class":592},"        activeRecipe",[449,847,621],{"class":596},[449,849,850],{"class":624},"\"io.stoatflow.rewrite.MigrateFromKafkaStreams\"",[449,852,680],{"class":596},[449,854,856],{"class":451,"line":855},22,[449,857,787],{"class":596},[449,859,861],{"class":451,"line":860},23,[449,862,651],{"class":596},[397,864,866],{"color":865,"icon":26},"info",[340,867,868,871,872,875,876,879],{},[358,869,870],{},"If resolution fails, check the credentials before the coordinates."," A 401 from a ",[407,873,874],{},"plugin"," repository surfaces as a generic ",[344,877,878],{},"Could not resolve io.stoatflow:stoatflow-openrewrite-recipe",", with no mention of authentication.",[389,881,883],{"id":882},"run-it","Run it",[340,885,886,887,824],{},"The recipe version is the StoatFlow version you are adopting — they ship in lockstep. The current version is ",[888,889],"stoatflow-version",{},[435,891,892,956],{"group":437},[439,893,897],{"className":894,"code":895,"filename":443,"language":896,"meta":445,"style":445},"language-bash shiki shiki-themes vitesse-light","# 1. Preview. Writes target\u002Frewrite\u002Frewrite.patch and changes nothing.\nmvn org.openrewrite.maven:rewrite-maven-plugin:dryRun \\\n  -Drewrite.recipeArtifactCoordinates=io.stoatflow:stoatflow-openrewrite-recipe:VERSION \\\n  -Drewrite.activeRecipes=io.stoatflow.rewrite.MigrateFromKafkaStreams\n\n# 2. Apply.\nmvn org.openrewrite.maven:rewrite-maven-plugin:run \\\n  -Drewrite.recipeArtifactCoordinates=io.stoatflow:stoatflow-openrewrite-recipe:VERSION \\\n  -Drewrite.activeRecipes=io.stoatflow.rewrite.MigrateFromKafkaStreams\n","bash",[344,898,899,904,916,923,928,932,937,946,952],{"__ignoreMap":445},[449,900,901],{"class":451,"line":452},[449,902,903],{"class":586},"# 1. Preview. Writes target\u002Frewrite\u002Frewrite.patch and changes nothing.\n",[449,905,906,909,912],{"class":451,"line":458},[449,907,908],{"class":592},"mvn",[449,910,911],{"class":624}," org.openrewrite.maven:rewrite-maven-plugin:dryRun",[449,913,915],{"class":914},"sEi1f"," \\\n",[449,917,918,921],{"class":451,"line":464},[449,919,920],{"class":914},"  -Drewrite.recipeArtifactCoordinates=io.stoatflow:stoatflow-openrewrite-recipe:VERSION",[449,922,915],{"class":914},[449,924,925],{"class":451,"line":470},[449,926,927],{"class":914},"  -Drewrite.activeRecipes=io.stoatflow.rewrite.MigrateFromKafkaStreams\n",[449,929,930],{"class":451,"line":476},[449,931,657],{"emptyLinePlaceholder":656},[449,933,934],{"class":451,"line":482},[449,935,936],{"class":586},"# 2. Apply.\n",[449,938,939,941,944],{"class":451,"line":488},[449,940,908],{"class":592},[449,942,943],{"class":624}," org.openrewrite.maven:rewrite-maven-plugin:run",[449,945,915],{"class":914},[449,947,948,950],{"class":451,"line":494},[449,949,920],{"class":914},[449,951,915],{"class":914},[449,953,954],{"class":451,"line":500},[449,955,927],{"class":914},[439,957,959],{"className":894,"code":958,"filename":578,"language":896,"meta":445,"style":445},"# 1. Preview. Writes build\u002Freports\u002Frewrite\u002Frewrite.patch and changes nothing.\n.\u002Fgradlew --init-script rewrite.init.gradle.kts rewriteDryRun\n\n# 2. Apply.\n.\u002Fgradlew --init-script rewrite.init.gradle.kts rewriteRun\n",[344,960,961,966,980,984,988],{"__ignoreMap":445},[449,962,963],{"class":451,"line":452},[449,964,965],{"class":586},"# 1. Preview. Writes build\u002Freports\u002Frewrite\u002Frewrite.patch and changes nothing.\n",[449,967,968,971,974,977],{"class":451,"line":458},[449,969,970],{"class":592},".\u002Fgradlew",[449,972,973],{"class":914}," --init-script",[449,975,976],{"class":624}," rewrite.init.gradle.kts",[449,978,979],{"class":624}," rewriteDryRun\n",[449,981,982],{"class":451,"line":464},[449,983,657],{"emptyLinePlaceholder":656},[449,985,986],{"class":451,"line":470},[449,987,936],{"class":586},[449,989,990,992,994,996],{"class":451,"line":476},[449,991,970],{"class":592},[449,993,973],{"class":914},[449,995,976],{"class":624},[449,997,998],{"class":624}," rewriteRun\n",[389,1000,1002],{"id":1001},"what-it-did-and-what-it-left-you","What it did, and what it left you",[340,1004,1005,1006,1009,1010,1013,1014,1017],{},"Read the diff, then read the ",[344,1007,1008],{},"PortingResiduals"," CSV the run exported alongside it. Every ",[344,1011,1012],{},"~~>"," marker in the diff, and every row in that table, names the entry in the ",[422,1015,1016],{"href":322},"KS compatibility matrix"," that explains what to do.",[1019,1020,1021,1034],"table",{},[1022,1023,1024],"thead",{},[1025,1026,1027,1031],"tr",{},[1028,1029,1030],"th",{},"The recipe rewrote",[1028,1032,1033],{},"The recipe flagged",[1035,1036,1037,1068,1093,1118,1140],"tbody",{},[1025,1038,1039,1057],{},[1040,1041,1042,1043,1046,1047,804,1050,804,1053,1056],"td",{},"All ",[344,1044,1045],{},"org.apache.kafka.streams.*"," imports, including the eight types a prefix sweep mis-routes (",[344,1048,1049],{},"StateStore",[344,1051,1052],{},"TimestampExtractor",[344,1054,1055],{},"StreamPartitioner",", …)",[1040,1058,1059,1060,1063,1064,1067],{},"The exactly-once default. Kafka Streams defaults to ",[344,1061,1062],{},"at_least_once","; StoatFlow defaults to exactly-once. If you never set ",[344,1065,1066],{},"processing.guarantee",", your ported application silently becomes transactional.",[1025,1069,1070,1080],{},[1040,1071,1072,1075,1076,1079],{},[344,1073,1074],{},"new KafkaStreams(topology, props)"," → ",[344,1077,1078],{},"StoatFlow.fromBuilder(new StreamsConfig(props), builder)",", when the builder is in reach",[1040,1081,1082,1083,804,1086,804,1089,1092],{},"Types with no StoatFlow equivalent: Interactive Queries v2, ",[344,1084,1085],{},"StreamsMetrics",[344,1087,1088],{},"TaskMetadata",[344,1090,1091],{},"processor.To",", the record-metadata accessors.",[1025,1094,1095,1104],{},[1040,1096,1097,1075,1100,1103],{},[344,1098,1099],{},"streams.state()",[344,1101,1102],{},"streams.kafkaStreamsState()",", so state comparisons keep Kafka Streams semantics",[1040,1105,1106,1107,1110,1111,1114,1115,824],{},"Shapes that compile but throw at runtime: ",[344,1108,1109],{},"Suppressed.maxBytes",", multicast on ",[344,1112,1113],{},"repartition()",", a hand-rolled ",[344,1116,1117],{},"BytesStoreSupplier",[1025,1119,1120,1133],{},[1040,1121,1122,1125,1126,804,1129,1132],{},[344,1123,1124],{},"JoinWindows"," field access, ",[344,1127,1128],{},"ValueTransformerWithKey.init",[344,1130,1131],{},"StateListener.onChange"," parameter types",[1040,1134,1135,1136,1139],{},"An entry point whose ",[344,1137,1138],{},"StreamsBuilder"," it could not resolve — because guessing would produce code that compiles and drops every processor.",[1025,1141,1142,1145],{},[1040,1143,1144],{},"Maven dependency coordinates and the repository declaration",[1040,1146],{},[340,1148,1149,1150,1153,1154,1157,1158,1161,1162,1165],{},"Gradle build files are left alone by design; apply the five-line change yourself. Swap ",[344,1151,1152],{},"org.apache.kafka:kafka-streams"," for ",[344,1155,1156],{},"io.stoatflow:stoatflow-core",", add the repository, and move to a JDK 25 toolchain with ",[344,1159,1160],{},"--enable-preview"," — the ",[344,1163,1164],{},"io.stoatflow"," Gradle plugin does the last part for you.",[340,1167,1168,1169,1172],{},"Then build. ",[358,1170,1171],{},"Remaining compile errors should map one-to-one onto flagged rows."," Anything else is a recipe bug, and worth reporting.",[389,1174,1176],{"id":1175},"the-one-thing-worth-understanding","The one thing worth understanding",[340,1178,1179,1180,1183,1184,1187,1188,1191,1192,1195,1196,1198,1199,1202,1203,1205,1206,1209,1210,1213,1214,1217,1218,1221],{},"Kafka Streams has a single ",[344,1181,1182],{},"KafkaStreams.State",". StoatFlow has two enums: a richer native ",[344,1185,1186],{},"StoatFlow.State",", returned by ",[344,1189,1190],{},"state()",", and a Kafka Streams-exact ",[344,1193,1194],{},"KafkaStreamsState"," mirror. The recipe maps ",[344,1197,1182],{}," onto the mirror ",[358,1200,1201],{},"and"," redirects ",[344,1204,1190],{}," to ",[344,1207,1208],{},"kafkaStreamsState()",", because either half alone is dangerous: ",[344,1211,1212],{},"streams.state() == KafkaStreamsState.RUNNING"," would then compare two unrelated enums. In Java that is a compile error. In Kotlin it compiles with only a warning and is ",[358,1215,1216],{},"always false at runtime"," — and ",[344,1219,1220],{},".equals(...)"," is silently always false in both languages.",[340,1223,1224],{},"You do not have to think about this; the recipe handles it, and flags anything it could not. It is worth knowing because it is the one place a mechanical port could otherwise go quietly wrong.",[389,1226,1228],{"id":1227},"kotlin-null-key-lambdas","Kotlin null-key lambdas",[340,1230,1231,1232,1235,1236,804,1239,804,1242,1245,1246,1249,1250,1253,1254,1257,1258,1261,1262,1265],{},"One Kotlin-only thing the recipe can't fix, because it is a language artifact rather than an import: over a topic that may carry ",[358,1233,1234],{},"null keys",", declare the key parameter of a stateless operator lambda nullable — ",[344,1237,1238],{},"map { _: K?, v -> … }",[344,1240,1241],{},"filter { _: K?, v -> … }",[344,1243,1244],{},"selectKey { _: K?, v -> … }",". A non-nullable inferred key parameter (even ",[344,1247,1248],{},"_",") makes the Kotlin compiler insert a ",[344,1251,1252],{},"checkNotNullParameter"," null-check, so a null key throws ",[344,1255,1256],{},"NullPointerException: Parameter specified as non-null is null"," inside your compiled lambda — ",[358,1259,1260],{},"on Kafka Streams too",", not just StoatFlow. Both frameworks invoke your lambda with the raw (null) key; the nullable declaration suppresses the check. Aggregations (",[344,1263,1264],{},"groupBy(...).count\u002Freduce\u002Faggregate",") and the stream–table INNER join already drop null-key records before your lambda runs, matching Kafka Streams — no change needed there.",[389,1267,1269],{"id":1268},"after-the-port","After the port",[340,1271,1272],{},"You now have a compiling StoatFlow application and a decision to make about your existing state.",[1274,1275,1276,1283],"path-cards",{},[1277,1278,1280],"path-card",{"icon":277,"title":1279,"to":275},"Without data migration",[340,1281,1282],{},"Reprocess from the input topics. The simplest path — pick it whenever you can.",[1277,1284,1286],{"icon":108,"title":1285,"to":280},"With data migration",[340,1287,1288],{},"Carry your accumulated state across rather than recomputing it.",[340,1290,1291,1292,1294],{},"If you prefer no tooling at all, the manual import table in ",[422,1293,1279],{"href":275}," remains a complete fallback.",[1296,1297,1298],"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 .s8zF2, html code.shiki .s8zF2{--shiki-default:#A0ADA0}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 .spphp, html code.shiki .spphp{--shiki-default:#B56959}html pre.shiki code .sUxyF, html code.shiki .sUxyF{--shiki-default:#2E8F82}html pre.shiki code .sEi1f, html code.shiki .sEi1f{--shiki-default:#A65E2B}",{"title":445,"searchDepth":458,"depth":458,"links":1300},[1301,1302,1303,1304,1305,1306],{"id":391,"depth":458,"text":392},{"id":882,"depth":458,"text":883},{"id":1001,"depth":458,"text":1002},{"id":1175,"depth":458,"text":1176},{"id":1227,"depth":458,"text":1228},{"id":1268,"depth":458,"text":1269},"Migrate a Kafka Streams application's imports, entry point and build to StoatFlow with a single OpenRewrite command, across Java and Kotlin, with every judgment call flagged rather than guessed.","md",{},{"icon":272},"how-to",{"title":269,"description":1307},"mQnEfqxdwy5SL1MtNq0QWw89V7JGfQP5_F3n93HU82M",[1315,1317],{"title":263,"path":264,"stem":265,"description":1316,"icon":90,"children":-1},"What carries over unchanged when you move a Kafka Streams topology to StoatFlow, what changes operationally, and how to decide between a green-field cutover and a state-carrying migration.",{"title":274,"path":275,"stem":276,"description":1318,"icon":277,"children":-1},"Green-field cutover from Kafka Streams — point StoatFlow at the same source topics with a fresh consumer group, let stateful operators rebuild from changelog\u002Fsource, then switch traffic. The dependency, build, and config swap, with the before\u002Fafter grounded in the map-filter example.",1786987429102]