{"id":37021865,"url":"https://github.com/pelagornis/kotlin-rex","last_synced_at":"2026-01-14T02:36:07.054Z","repository":{"id":318731062,"uuid":"1052702553","full_name":"pelagornis/kotlin-rex","owner":"pelagornis","description":"Rex is a modular, state management architecture","archived":false,"fork":false,"pushed_at":"2025-10-12T06:22:52.000Z","size":179,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-13T16:41:29.579Z","etag":null,"topics":["kotlin","pelagornis","rex"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pelagornis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-08T12:35:54.000Z","updated_at":"2025-10-12T06:22:05.000Z","dependencies_parsed_at":"2025-10-13T16:41:38.744Z","dependency_job_id":"d5dbe7a7-4317-4840-a818-3d93b40f5048","html_url":"https://github.com/pelagornis/kotlin-rex","commit_stats":null,"previous_names":["pelagornis/kotlin-rex"],"tags_count":16,"template":false,"template_full_name":"pelagornis/github-template","purl":"pkg:github/pelagornis/kotlin-rex","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pelagornis%2Fkotlin-rex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pelagornis%2Fkotlin-rex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pelagornis%2Fkotlin-rex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pelagornis%2Fkotlin-rex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pelagornis","download_url":"https://codeload.github.com/pelagornis/kotlin-rex/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pelagornis%2Fkotlin-rex/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408711,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kotlin","pelagornis","rex"],"created_at":"2026-01-14T02:36:06.400Z","updated_at":"2026-01-14T02:36:07.041Z","avatar_url":"https://github.com/pelagornis.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Kotlin Rex 🦖\n\n**Kotlin Rex** is a type-safe and predictable state management library for Android. Based on Redux/Flux architecture patterns, it elegantly handles asynchronous operations using Kotlin Coroutines.\n\n## Features ✨\n\n- 🎯 **Type Safety**: Complete type safety leveraging Kotlin's powerful type system\n- 🔄 **Predictable State Management**: Manage state changes predictably with unidirectional data flow\n- ⚡ **Coroutines-based**: Efficient asynchronous processing using Kotlin Coroutines\n- 🔌 **Middleware Support**: Various middlewares including logging and time-travel debugging\n- 🎨 **Effect System**: Powerful and flexible side effect handling\n- 🚌 **EventBus**: Built-in EventBus for inter-component event communication\n- 🧩 **Modular**: Each component can be used independently\n\n## Requirements 📋\n\n- Android API 24+ (Android 7.0+)\n- Kotlin 2.0+\n- Android Gradle Plugin 8.0+\n\n## Installation 🔧\n\n### Maven Central (Recommended)\n\nAdd the dependency to your `build.gradle.kts`:\n\n```kotlin\ndependencies {\n    implementation(\"com.pelagornis:rex:1.0.0\")\n}\n```\n\n### GitHub Packages\n\nAdd the GitHub Packages repository:\n\n```kotlin\nrepositories {\n    maven {\n        url = uri(\"https://maven.pkg.github.com/pelagornis/kotlin-rex\")\n        credentials {\n            username = findProperty(\"gpr.user\") as String? ?: System.getenv(\"GITHUB_USERNAME\")\n            password = findProperty(\"gpr.key\") as String? ?: System.getenv(\"GITHUB_TOKEN\")\n        }\n    }\n}\n\ndependencies {\n    implementation(\"com.pelagornis:rex:1.0.0\")\n}\n```\n\n### Local Maven (For Development)\n\n```bash\n# Clone and build locally\ngit clone https://github.com/pelagornis/kotlin-rex.git\ncd kotlin-rex\n./gradlew :library:publishToMavenLocal\n```\n\nThen in your project:\n\n```kotlin\nrepositories {\n    mavenLocal()\n}\n\ndependencies {\n    implementation(\"com.pelagornis:rex:1.0.0\")\n}\n```\n\nFor detailed publishing instructions, see:\n\n- **[USER_TOKEN_SETUP.md](USER_TOKEN_SETUP.md)** - Maven Central User Token setup (2024 updated) ⭐\n- [PUBLISHING.md](PUBLISHING.md) - Complete publishing guide\n- [GPG_SETUP.md](GPG_SETUP.md) - GPG key setup\n- [SONATYPE_SETUP.md](SONATYPE_SETUP.md) - Sonatype account setup\n\n## Core Concepts 📚\n\n### State\n\nAn immutable data structure representing the application state.\n\n```kotlin\ndata class AppState(\n    val count: Int = 0,\n    val isLoading: Boolean = false,\n    val errorMessage: String? = null,\n    val lastUpdated: Long = System.currentTimeMillis()\n) : StateType\n```\n\n### Action\n\nEvents that describe state changes.\n\n```kotlin\nsealed class AppAction : ActionType {\n    object Increment : AppAction()\n    object Decrement : AppAction()\n    data class SetCount(val count: Int) : AppAction()\n    object LoadData : AppAction()\n    data class DataLoaded(val data: String) : AppAction()\n    data class ErrorOccurred(val message: String) : AppAction()\n}\n```\n\n### Reducer\n\nA pure function that takes the current state and an action, then returns a new state and effects to execute.\n\n```kotlin\nclass AppReducer : Reducer\u003cAppState, AppAction\u003e {\n    override fun reduce(state: AppState, action: AppAction): Pair\u003cAppState, List\u003cEffect\u003cAppAction\u003e\u003e\u003e {\n        return when (action) {\n            is AppAction.Increment -\u003e {\n                state.copy(count = state.count + 1) to emptyList()\n            }\n            is AppAction.Decrement -\u003e {\n                state.copy(count = state.count - 1) to emptyList()\n            }\n            is AppAction.SetCount -\u003e {\n                state.copy(count = action.count) to emptyList()\n            }\n            is AppAction.LoadData -\u003e {\n                val effect = Effect\u003cAppAction\u003e { emitter -\u003e\n                    try {\n                        val data = fetchDataFromApi()\n                        emitter.send(AppAction.DataLoaded(data))\n                    } catch (e: Exception) {\n                        emitter.send(AppAction.ErrorOccurred(e.message ?: \"Unknown error\"))\n                    }\n                }\n                state.copy(isLoading = true) to listOf(effect)\n            }\n            is AppAction.DataLoaded -\u003e {\n                state.copy(isLoading = false) to emptyList()\n            }\n            is AppAction.ErrorOccurred -\u003e {\n                state.copy(isLoading = false, errorMessage = action.message) to emptyList()\n            }\n        }\n    }\n\n    private suspend fun fetchDataFromApi(): String {\n        // API call logic\n        return \"Data from API\"\n    }\n}\n```\n\n### Store\n\nThe central repository that holds the state, processes actions, and notifies subscribers of state changes.\n\n```kotlin\nclass MyViewModel : ViewModel() {\n    private val store = Store(\n        initialState = AppState(),\n        reducer = AppReducer(),\n        middlewares = listOf(LoggingMiddleware())\n    )\n\n    val state: StateFlow\u003cAppState\u003e = store.state\n\n    fun dispatch(action: AppAction) {\n        store.dispatch(action)\n    }\n\n    override fun onCleared() {\n        super.onCleared()\n        store.clear()\n    }\n}\n```\n\n## Usage Examples 🚀\n\n### 1. Basic Usage\n\n```kotlin\n// 1. Define State\ndata class CounterState(\n    val count: Int = 0\n) : StateType\n\n// 2. Define Actions\nsealed class CounterAction : ActionType {\n    object Increment : CounterAction()\n    object Decrement : CounterAction()\n}\n\n// 3. Implement Reducer\nclass CounterReducer : Reducer\u003cCounterState, CounterAction\u003e {\n    override fun reduce(\n        state: CounterState,\n        action: CounterAction\n    ): Pair\u003cCounterState, List\u003cEffect\u003cCounterAction\u003e\u003e\u003e {\n        return when (action) {\n            is CounterAction.Increment -\u003e\n                state.copy(count = state.count + 1) to emptyList()\n            is CounterAction.Decrement -\u003e\n                state.copy(count = state.count - 1) to emptyList()\n        }\n    }\n}\n\n// 4. Create and Use Store\nval store = Store(\n    initialState = CounterState(),\n    reducer = CounterReducer()\n)\n\n// Subscribe to state changes\nstore.subscribe { state -\u003e\n    println(\"Current count: ${state.count}\")\n}\n\n// Dispatch actions\nstore.dispatch(CounterAction.Increment)\nstore.dispatch(CounterAction.Increment)\nstore.dispatch(CounterAction.Decrement)\n```\n\n### 2. Using Effects\n\nEffects handle side effects like asynchronous operations, network requests, and timers.\n\n```kotlin\n// Basic Effect\nval effect = Effect\u003cAppAction\u003e { emitter -\u003e\n    val result = performNetworkRequest()\n    emitter.send(AppAction.RequestSuccess(result))\n}\n\n// Delayed Effect\nval delayedEffect = Effect.delayed(\n    action = AppAction.ShowMessage(\"Hello!\"),\n    delayMillis = 1000\n)\n\n// Retryable Effect\nval retryEffect = Effect.retry(\n    effect = networkEffect,\n    maxAttempts = 3,\n    delayMillis = 1000,\n    shouldRetry = { error -\u003e error is NetworkException },\n    onError = { error -\u003e\n        Log.e(\"Effect\", \"Failed after retries: $error\")\n    }\n)\n\n// Combining Multiple Effects\nval combinedEffect = Effect.combine(effect1, effect2, effect3)\n```\n\n### 3. Using Middleware\n\nMiddleware can intercept and process actions before they reach the reducer.\n\n```kotlin\n// Custom Middleware\nclass AnalyticsMiddleware : Middleware\u003cAppState, AppAction\u003e {\n    override suspend fun process(\n        state: AppState,\n        action: AppAction,\n        emit: (AppAction) -\u003e Unit\n    ): List\u003cEffect\u003cAppAction\u003e\u003e {\n        // Send action to analytics tool\n        Analytics.logEvent(action.javaClass.simpleName)\n        return emptyList()\n    }\n}\n\n// Apply Middleware to Store\nval store = Store(\n    initialState = AppState(),\n    reducer = AppReducer(),\n    middlewares = listOf(\n        LoggingMiddleware(),\n        AnalyticsMiddleware(),\n        TimeTravelMiddleware()\n    )\n)\n```\n\n### 4. Using EventBus\n\nUse EventBus to send and receive events between components.\n\n```kotlin\n// Define Events\nsealed class AppEvent : EventType {\n    data class ShowToast(val message: String) : AppEvent()\n    object NavigateToHome : AppEvent()\n}\n\n// Use EventBus\nval eventBus = store.getEventBus()\n\n// Subscribe to specific event type\neventBus.subscribe\u003cAppEvent.ShowToast\u003e { event -\u003e\n    Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()\n}\n\n// Subscribe to all events\neventBus.subscribe { event -\u003e\n    when (event) {\n        is AppEvent.ShowToast -\u003e showToast(event.message)\n        is AppEvent.NavigateToHome -\u003e navigateToHome()\n    }\n}\n\n// Publish event\neventBus.publish(AppEvent.ShowToast(\"Hello, World!\"))\n```\n\n### 5. Integration with Jetpack Compose\n\n```kotlin\n@Composable\nfun CounterScreen(viewModel: CounterViewModel = viewModel()) {\n    val state by viewModel.state.collectAsState()\n\n    Column(\n        modifier = Modifier.fillMaxSize(),\n        horizontalAlignment = Alignment.CenterHorizontally,\n        verticalArrangement = Arrangement.Center\n    ) {\n        Text(\n            text = \"Count: ${state.count}\",\n            style = MaterialTheme.typography.headlineLarge\n        )\n\n        Spacer(modifier = Modifier.height(16.dp))\n\n        Row(\n            horizontalArrangement = Arrangement.spacedBy(16.dp)\n        ) {\n            Button(onClick = { viewModel.dispatch(CounterAction.Decrement) }) {\n                Text(\"-\")\n            }\n            Button(onClick = { viewModel.dispatch(CounterAction.Increment) }) {\n                Text(\"+\")\n            }\n        }\n    }\n}\n```\n\n## Built-in Middleware 📦\n\n### LoggingMiddleware\n\nLogs all actions and state changes.\n\n```kotlin\nval store = Store(\n    initialState = AppState(),\n    reducer = AppReducer(),\n    middlewares = listOf(LoggingMiddleware())\n)\n```\n\n### TimeTravelMiddleware\n\nTracks state history to enable time-travel debugging.\n\n```kotlin\nval timeTravelMiddleware = TimeTravelMiddleware\u003cAppState, AppAction\u003e()\n\nval store = Store(\n    initialState = AppState(),\n    reducer = AppReducer(),\n    middlewares = listOf(timeTravelMiddleware)\n)\n\n// Undo to previous state\ntimeTravelMiddleware.undo()\n\n// Redo to next state\ntimeTravelMiddleware.redo()\n\n// View history\nval history = timeTravelMiddleware.getHistory()\n```\n\n## Architecture 🏗️\n\n```\n┌─────────────────────────────────────────────┐\n│                    View                     │\n│  (Activity, Fragment, Composable)           │\n└────────────────┬────────────────────────────┘\n                 │ dispatch(action)\n                 ▼\n┌─────────────────────────────────────────────┐\n│                   Store                     │\n│  ┌─────────────────────────────────────┐   │\n│  │          Middleware Chain           │   │\n│  │  ┌────────┐ ┌────────┐ ┌────────┐  │   │\n│  │  │ Logger │→│Analytics│→│TimeTravel│ │   │\n│  │  └────────┘ └────────┘ └────────┘  │   │\n│  └─────────────────────────────────────┘   │\n│                   ▼                         │\n│  ┌─────────────────────────────────────┐   │\n│  │            Reducer                  │   │\n│  │  (state, action) → (state, effects) │   │\n│  └─────────────────────────────────────┘   │\n│                   ▼                         │\n│  ┌─────────────────────────────────────┐   │\n│  │          New State                  │   │\n│  └─────────────────────────────────────┘   │\n│                   ▼                         │\n│  ┌─────────────────────────────────────┐   │\n│  │      Execute Effects                │   │\n│  │  (async operations, side effects)   │   │\n│  └─────────────────────────────────────┘   │\n└────────────────┬────────────────────────────┘\n                 │ state updates\n                 ▼\n┌─────────────────────────────────────────────┐\n│              Subscribers                    │\n│     (UI updates via StateFlow)              │\n└─────────────────────────────────────────────┘\n```\n\n## Best Practices 💡\n\n1. **Keep State Immutable**: Always use the `copy()` method of `data class` to create a new state.\n\n2. **Keep Reducers Pure**: Don't cause side effects inside reducers; separate them into Effects.\n\n3. **Make Actions Clear**: Action names should clearly express \"what happened\".\n\n4. **Make Effects Reusable**: Create reusable Effects for common asynchronous operations.\n\n5. **Single Responsibility for Middleware**: Each Middleware should perform one clear role.\n\n6. **Manage Store in ViewModel**: In Android, create and manage Store in ViewModel.\n\n## Testing 🧪\n\n```kotlin\nclass CounterReducerTest {\n    private lateinit var reducer: CounterReducer\n\n    @Before\n    fun setup() {\n        reducer = CounterReducer()\n    }\n\n    @Test\n    fun `increment action increases count by one`() {\n        val initialState = CounterState(count = 0)\n        val action = CounterAction.Increment\n\n        val (newState, effects) = reducer.reduce(initialState, action)\n\n        assertEquals(1, newState.count)\n        assertTrue(effects.isEmpty())\n    }\n\n    @Test\n    fun `decrement action decreases count by one`() {\n        val initialState = CounterState(count = 5)\n        val action = CounterAction.Decrement\n\n        val (newState, effects) = reducer.reduce(initialState, action)\n\n        assertEquals(4, newState.count)\n    }\n}\n```\n\n## Publishing 📦\n\n### Quick Publish to All Repositories\n\n```bash\n# Publish to GitHub Packages + Maven Central\n./gradlew :library:publish\n```\n\n### GitHub Actions (Recommended)\n\n1. Go to **Actions** tab\n2. Select **Publish Library** workflow\n3. Choose release type:\n   - `all` - Publish to all repositories (GitHub Packages + Maven Central) ⭐\n   - `github` - GitHub Packages only\n   - `sonatype` - Maven Central only\n   - `local` - Local testing only\n\n### Auto-Deploy on Release\n\nCreate a release on GitHub to automatically publish to all repositories:\n\n```bash\ngit tag v0.1.2\ngit push origin v0.1.2\n```\n\nFor detailed instructions, see [PUBLISHING.md](PUBLISHING.md) and [GPG_SETUP.md](GPG_SETUP.md).\n\n## Example Project 📱\n\nCheck out the complete working example in the `example` module of this repository.\n\n```bash\n./gradlew :example:assembleDebug\n```\n\n## Contributing 🤝\n\nContributions are always welcome! Please refer to [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License 📄\n\n**kotlin-rex** is distributed under the MIT License. See the [LICENSE](LICENSE) file for more details.\n\n## Credits 👏\n\nKotlin Rex is developed and maintained by [Pelagornis](https://github.com/pelagornis).\n\n---\n\nMade with ❤️ by Pelagornis\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpelagornis%2Fkotlin-rex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpelagornis%2Fkotlin-rex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpelagornis%2Fkotlin-rex/lists"}