{"id":43358199,"url":"https://github.com/hyperdevs-team/mini-kotlin","last_synced_at":"2026-02-02T03:18:43.577Z","repository":{"id":39560797,"uuid":"180545927","full_name":"hyperdevs-team/mini-kotlin","owner":"hyperdevs-team","description":"Minimal Flux architecture written in Kotlin.","archived":false,"fork":false,"pushed_at":"2024-06-27T20:14:57.000Z","size":597,"stargazers_count":22,"open_issues_count":0,"forks_count":5,"subscribers_count":14,"default_branch":"master","last_synced_at":"2024-06-28T21:01:22.756Z","etag":null,"topics":["android","android-architecture","flux","kotlin","redux","state-management"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hyperdevs-team.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-10T09:15:12.000Z","updated_at":"2024-06-27T20:14:37.000Z","dependencies_parsed_at":"2023-02-17T00:46:03.842Z","dependency_job_id":"a6280a24-16b9-4485-852c-109387204a65","html_url":"https://github.com/hyperdevs-team/mini-kotlin","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/hyperdevs-team/mini-kotlin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperdevs-team%2Fmini-kotlin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperdevs-team%2Fmini-kotlin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperdevs-team%2Fmini-kotlin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperdevs-team%2Fmini-kotlin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hyperdevs-team","download_url":"https://codeload.github.com/hyperdevs-team/mini-kotlin/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperdevs-team%2Fmini-kotlin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29003221,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-02T01:32:03.847Z","status":"online","status_checked_at":"2026-02-02T02:00:07.448Z","response_time":58,"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":["android","android-architecture","flux","kotlin","redux","state-management"],"created_at":"2026-02-02T03:18:42.880Z","updated_at":"2026-02-02T03:18:43.571Z","avatar_url":"https://github.com/hyperdevs-team.png","language":"Kotlin","readme":"# Mini\n[![Release](https://jitpack.io/v/hyperdevs-team/mini-kotlin.svg)](https://jitpack.io/#hyperdevs-team/mini-kotlin)\n\nMini is a minimal Flux architecture written in Kotlin that also adds a mix of useful features to build UIs fast.\n\n## Purpose\nYou should use this library if you aim to develop a reactive application with good performance (no reflection using code-gen).\nFeature development using Mini is fast compared to traditional architectures (like CLEAN or MVP), low boilerplate and state based models make feature integration and bugfixing easy as well as removing several families of problems like concurrency or view consistency across screens.\n\n## Setting Up\n### Import the library\n\nFirst, add the following dependencies to your main `build.gradle` so you can import the library dependencies:\n\n\u003cdetails open\u003e\u003csummary\u003eGroovy\u003c/summary\u003e\n\n```groovy\nbuildscript {\n    repositories { \n        maven { url \"https://jitpack.io\" }\n    }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eKotlin\u003c/summary\u003e\n\n```kotlin\nbuildscript {\n    repositories { \n        maven(\"https://jitpack.io\")\n    }\n}\n```\n\n\u003c/details\u003e\n\nThen, add the following dependencies to your module's `build.gradle`:\n\n\u003cdetails open\u003e\u003csummary\u003eGroovy\u003c/summary\u003e\n\n```groovy\ndependencies {\n    def mini_version = \"\u003clatest_version\u003e\"\n    // Minimum working dependencies\n    implementation \"com.github.hyperdevs-team.mini-kotlin:mini-android:$mini_version\"\n    // Use kapt as your annotation processor\n    kapt \"com.github.hyperdevs-team.mini-kotlin:mini-processor:$mini_version\"\n    // Or ksp if you prefer using Kotlin Symbol Processing (requires extra dependencies)\n    ksp \"com.github.hyperdevs-team.mini-kotlin:mini-processor:$mini_version\"\n\n    // Kodein helper libraries\n    implementation \"com.github.hyperdevs-team.mini-kotlin:mini-kodein:$mini_version\"\n    implementation \"com.github.hyperdevs-team.mini-kotlin:mini-kodein-android:$mini_version\"\n\n    // Kodein helper library for view models scoped to the Navigation component's graph in Jetpack Compose\n    implementation \"com.github.hyperdevs-team.mini-kotlin:mini-kodein-android-compose:$mini_version\"\n\n    // Android Testing helper libraries\n    androidTestImplementation \"com.github.hyperdevs-team.mini-kotlin:mini-testing:$mini_version\"\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eKotlin\u003c/summary\u003e\n\n```kotlin\ndependencies {\n    val miniVersion = \"\u003clatest_version\u003e\"\n    // Minimum working dependencies\n    implementation(\"com.github.hyperdevs-team.mini-kotlin:mini-android:$miniVersion\")\n    // Use kapt as your annotation processor\n    kapt(\"com.github.hyperdevs-team.mini-kotlin:mini-processor:$miniVersion\")\n    // Or ksp if you prefer using Kotlin Symbol Processing (requires extra dependencies)\n    ksp(\"com.github.hyperdevs-team.mini-kotlin:mini-processor:$miniVersion\")\n\n    // Kodein helper libraries\n    implementation(\"com.github.hyperdevs-team.mini-kotlin:mini-kodein:$miniVersion\")\n    implementation(\"com.github.hyperdevs-team.mini-kotlin:mini-kodein-android:$miniVersion\")\n\n    // Kodein helper library for view models scoped to the Navigation component's graph in Jetpack Compose\n    implementation(\"com.github.hyperdevs-team.mini-kotlin:mini-kodein-android-compose:$miniVersion\")\n\n    // Android Testing helper libraries\n    androidTestImplementation(\"com.github.hyperdevs-team.mini-kotlin:mini-testing:$miniVersion\")\n}\n```\n\n\u003c/details\u003e\n\nIf you want, you can also use *Kotlin Symbol Processing (KSP)* instead of KAPT. Keep in mind that \n[KSP may have some gotchas that can be worked around](#ksp-gotchas), so double check before using this\nand report any issue that you find while working with KSP.\n\n\u003cdetails\u003e\u003csummary\u003eKSP extra dependencies\u003c/summary\u003e\n\n\u003cdetails open\u003e\u003csummary\u003eGroovy\u003c/summary\u003e\n\nAdd this to your main `build.gradle`:\n\n```groovy\nbuildscript {\n    ext {\n        ksp_version = \"\u003clatest_ksp_version\u003e\"\n    }\n\n    dependencies {\n        classpath \"com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:$ksp_version\"\n    }\n}\n```\n\nAnd this to your module's `build.gradle`\n```groovy\napply plugin: \"com.google.devtools.ksp\"\n\nksp \"com.github.hyperdevs-team.mini-kotlin:mini-processor:$mini_version\"\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eKotlin\u003c/summary\u003e\n\nAdd this to your main `build.gradle.kts`:\n```kotlin\nbuildscript {\n    dependencies {\n        val kspVersion = \u003clatest_ksp_version\u003e\n        classpath(\"com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:${kspVersion}\")\n    }\n}\n```\n\nAnd this to your module's `build.gradle.kts`\n```groovy\nplugins {\n    id \"com.google.devtools.ksp\"\n}\n\nksp(\"com.github.hyperdevs-team.mini-kotlin:mini-processor:${miniVersion}\")\n```\n\n\u003c/details\u003e\n\n\u003c/details\u003e\n\n### JDK\nEnsure that your project has compatibility with Java 17:\n\nFor Kotlin projects:\n```groovy \ntasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {\n    kotlinOptions {\n        jvmTarget = \"17\"\n    }\n}\n```\n\nFor Android:\n```groovy\nandroid {\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_17\n        targetCompatibility = JavaVersion.VERSION_17\n    }\n\n    kotlinOptions {\n        jvmTarget = \"17\"\n    }\n}\n```\n\n## Concepts\n### Actions\nAn `Action` is a simple class that usually represents a use case. It can also contain a payload that includes data to perform said action. When an action is triggered, it will be delivered via `dispatcher` to the `stores` that are going to do something with the action to change their state.\n\nFor example, we may want to log in to a service. We would create an action like this one:\n```kotlin\n@Action\ndata class LoginAction(val username: String, val password: String)\n```\n\nWhen we receive the response from the server, we'll dispatch another action with the result:\n```kotlin\n@Action\ndata class LoginCompletedAction(val loginTask: Task, val user: User?)\n```\n\nActions will usually be triggered from `Views`, `ViewModel`s or `Controllers`, depending on the architecture of your choice.\n\n### Dispatcher\nThe `Dispatcher` is the hub that manages all data flow in a Flux application. It is basically a holder of store callbacks: each store registers itself and provides a callback for an action.\n\nOne important thing is that the dispatching is always performed in the same thread (usually the main thread) to avoid possible side-effects.\n\nWe can dispatch actions in the following ways:\n\n```kotlin\n// Dispatch an action in the main thread synchronously\ndispatcher.dispatch(action = LoginAction(username = \"user\", password = \"123\"))\n\n// Dispatch an action with the given scope\ndispatcher.dispatchOn(action = LoginAction(username = \"user\", password = \"123\"), scope = coroutineScope)\n```\n\n### Store\nThe `Store`s are holders for application state and state mutation logic. In order to do so they expose pure reducer functions that are later invoked by the dispatcher. A `Store` is a type of a `StateContainer`, which is exactly that: a container of states.\n\nA `State` is a plain object (usually a `data class`) that holds all information needed to display the view. States should always be immutable. State classes should avoid using framework-specific elements (View, Camera, Cursor...) in order to ease testing.\n\nStores subscribe to actions to change the application state after a `dispatch`. Mini generates the code that links dispatcher actions and stores using the `@Reducer` annotation over a **non-private function that receives an Action as parameter**.\n\n```kotlin\ndata class SessionState(val loginTask: Task = taskIdle(), val loggedUser: User? = null)\n\nclass SessionStore(val controller: SessionController) : Store\u003cSessionState\u003e() {\n    @Reducer\n    fun login(action: LoginAction): SessionState {\n        controller.login(action.username, action.password)\n        return state.copy(loginTask = taskRunning(), loggedUser = null)\n    }\n\n    @Reducer\n    fun onLoginCompleted(action: LoginCompletedAction): SessionState {\n        return state.copy(loginTask = action.loginTask, loggedUser = action.user)\n    }\n}\n```\n\n### View changes\nEach `StateContainer` exposes a Kotlin `Flow` that emits changes produced on the state, allowing a `View` or a `ViewModel` to listen to those changes and react accordingly to update the UI with the new `Store` state.\n\n```kotlin  \nmainStore.flow()\n    .onEach { state -\u003e\n        // Do whatever you want\n    }\n    .launchIn(coroutineScope)\n```  \n\n### Tasks\nA `Task` is a basic object to represent an ongoing process. They should be used in the state of our `StateContainer` (a `Store`, for example) to represent ongoing processes that must be represented in the UI.\n\nYou can also use `TypedTask` to save metadata related the current task. \n\n**IMPORTANT: Do not use TypedTask to hold values that must survive multiple task executions. Save them as a variable in the state instead.**\n\n### Example\nGiven the example `Store`s and `Action`s explained before, the workflow would be:\n\n- View dispatches `LoginAction`.\n- Store changes the `loginTask` of its state to `loading` (or running state) and call a function in a `SessionController` to perform the login asynchronously.\n- The view shows an Spinner when `loginTask` is in running state.\n- The asynchronous call ends and `LoginCompletedAction` is dispatched, returning a null `User` and an error state `Task` if the asynchronous work failed or a success `Task` and a `User` if the work finished successfully.\n- The Store changes its state to the given values from `LoginCompletedAction`.\n- The view will react (for example, redirecting to another home view) if the task was success or shows an error if not.\n\nYou can execute another sample in the `app` package. It contains two different samples executing two types of `StateContainer`s:\n- `StoreSampleActivity` class uses a `Store` as a `StateContainer`.\n- `ViewModelSampleActivity` class uses a `ViewModel` as a `StateContainer`.\n\n## How to use\n### Setting up Mini\nYou'll need to add the following snippet to the class that initializes your application (for example, in Android you would set this in your `Application`'s `onCreate` method).\n\n```kotlin\nval stores = listOf\u003cStore\u003c*\u003e\u003e() // Here you'll set-up you store list, you can retrieve it using your preferred DI framework\nval dispatcher = MiniGen.newDispatcher() // Create a new dispatcher\n\n// Initialize Mini\nstoreSubscriptions = MiniGen.subscribe(dispatcher, stores)\nstores.forEach { store -\u003e\n    store.initialize()\n}\n\n// Optional: add logging middleware to log action events\ndispatcher.addMiddleware(LoggerMiddleware(stores)) { tag, msg -\u003e\n    Log.d(tag, msg)\n}\n```\n\nAs soon as you do this, you'll have Mini up and running. You'll then need to declare your `Action`s, `Store`s and `State` as mentioned previously. The sample [app](app) contains examples regarding app configuration.\n\n## Advanced usages\n### Kotlin Flow Utils\nMini includes some utility extensions over Kotlin `Flow` to make easier listen state changes over the `StateContainer`s.\n\n- `select`: Will emit only distinct values over the given `map` clause.\n- `selectNotNull`: Like `select` but also avoiding null values.\n- `onEachChange`: Emits a value when the values goes from one value to another.\n- `onEachDisable`: Emits when the value goes from true to false.\n- `onEachEnable`: Emits when the value goes from false to true.\n\nYou can see all extensions in [StoreFlow](mini-common/src/main/java/mini/StoreFlow.kt).\n\n### Navigation and UI loops\nIn order to avoid loops when working with navigation based on a process result after dispatching an `Action`, you will need to do something like this\n\nFor example:\n```kotlin\nfun login(username: String, password: String) {\n    dispatcher.dispatch(LoginAction(username, password))\n    sessionStore.flow()\n        .takeUntil { it.isTerminal }\n        .onEach {\n                // Do your stuff\n        }\n        .launchIn(coroutineScope)\n}\n```\n\n### Merging state from multiple stores\nSometimes we want to use get data from multiple stores at the same time. You can do this by using `mergeStates`:\n\n```kotlin\nmergeStates\u003cAny\u003e {\n        merge(userStore) { this }\n        merge(downloadsStore) { this }\n    }.select { (userState, downloadsState) -\u003e\n        CombinedState(userState, downloadState)\n    }\n        .onEach { \n            // Do your stuff\n         }\n        .llaunchIn(coroutineScope)\n```\n\n### Logging\nMini includes a custom `LoggerMiddleware` to log any change in your `StateContainer` states produced from an `Action`. This will allow you to keep track of your actions, changes and side-effects more easily. \nTo add the `LoggerMiddleware` to your application you just need to add a single instance of it to your `Dispatcher`.\n```kotlin\nval loggerMiddleware = CustomLoggerMiddleware(stores().values)\ndispatcher.addMiddleware(loggerMiddleware)\n```\n\n## Testing with Mini\nMini includes an extra library called mini-testing with a few methods and `TestRule`s to simplify your UI tests with this framework.\n\n- `TestDispatcherRule` : This rule will intercept any action that arrives to the `Dispatcher`, avoiding any call to `Store`s. If we include this rule we will need to change the states manually in our tests.\n- `CleanStateRule` : This rule resets the state of the `Store`s before and after each test.\n\nExample of an Android test checking that an action is correctly dispatched:\n\n```kotlin\n@get:Rule\nval testDispatcher = testDispatcherRule()\n\n@Test\nfun login_button_dispatch_login_action() {\n    onView(withId(R.id.username_edit_text)).perform(typeText(\"someUsername\"))\n    onView(withId(R.id.password_edit_text)).perform(typeText(\"somePassword\"))\n    onView(withId(R.id.login_button)).perform(click())\n    \n    assertThat(testDispatcher.actions, contains(LoginAction(someUsername, somePassword)))\n}\n```\n\nExample of an Android test checking that a `View` correctly changes with an specific state:\n\n```kotlin\n@get:Rule\nval cleanState = cleanStateRule()\n\n@Test\nfun login_redirects_to_home_with_success_task() {\n     //Set login state to success\n     onUiSync {\n         val loggedUser = User(email = MockModels.anyEmail, uid = MockModels.anyId, username = MockModels.anyUsername, photoUrl = MockModels.anyPhoto)\n         val state = SessionState().copy(loginRequestState = requestSuccess(), verified = false, loggedIn = true, loggedUser = loggedUser)\n         sessionStore.setTestState(state)\n     }\n     //Redirect to Email verification activity\n     intended(hasComponent(HomeActivity::class.java.name))\n}\n```\n\n## Kodein support\n[Kodein](https://github.com/kosi-libs/Kodein) is a very simple and yet very useful dependency retrieval container. it is very easy to use and configure.\n\nThe library `mini-kodein` aims to ease working with Kodein and Mini by providing some utility methods to bind objects like `Store`s by relying on Kodein's retrieval capabilities.\n\n```kotlin\nobject UserDIModule : BaseDIModule() {\n    override val builder: DI.Builder.() -\u003e Unit = {\n        bindStore { UserStore(instance()) } // binds the store as a singleton and adds it to a seo of stores\n        bind\u003cUserController\u003e() with singleton { UserControllerImpl(instance()) }\n    }\n}\n```\n\n## Android-specific features\n### Proguard/R8\nEach of the libraries contain a sensible proguard file that your project can consume in order to run you app on Proguard or R8.\nNo additional steps have to be done in order to use them apart from enabling minify in your project.\n\n### Kodein Android utils\nThe library `mini-kodein-android` has some utility methods in order to inject an Android's `ViewModel` in a `DIAware` `Activity` or `Fragment`.\nIn order to use these methods, bind the Android's `ViewModelProvider.Factory` instance with Kodein:\n```kotlin\n// Use any tag to differ between the injected `Context` or `Application` if you are binding also `Context` with Kodein\nbind\u003cApplication\u003e(\"appTag\") with singleton { app }\nbind\u003cViewModelProvider.Factory\u003e() with singleton { DIViewModelFactory(di.direct) }\n```\nTo inject a `ViewModel` without parameters, bind it as follows:\n```kotlin\nbindViewModel { MainViewModel(instance(\"appTag\") }\n```\nAnd in your `DIAware` `Activity` or `Fragment`:\n```kotlin\nprivate val mainViewModel: MainViewModel by viewModel()\n```\n\n### Kodein and Jetpack Compose utils\nThe library `mini-kodein-android-compose` has some utility methods in order to inject an Android's `ViewModel` in the scope of a Navigation component graph. This is useful as in Jetpack Compose it is common to have only one or few `Activities` and no `Fragment`s so, in order to scope the lifecycle of the `ViewModel` not for all the life of the `Activity`, we can scope it to any route existing in the `NavBackStackEntry`.\n\nIn order to use it, do the same as above, but instead of injecting the ViewModel scoped to a route of the Navigation, the `NavHost` composable must be inside an `DIAware Activity`, and then do as follows:\n```kotlin\ncomposable(route = \"home\") { navBackStackEntry -\u003e\n    val homeViewModel: HomeViewModel by navBackStackEntry.viewModel(contextDI())\n    HomeScreen(homeViewModel, ...)\n}\n```\nIn case you want to pass an argument to a ViewModel, you need to bind the factory of that kind of Android's ViewModel.\nYou can do this in both `mini-kodein-android`, and `mini-kodein-android-compose`.\nFor example, given a `ViewModel` that you want to pass a `String` param, it would be:\n```kotlin\nbindViewModelFactory\u003cHomeViewModelWithParameter, ViewModelProvider.Factory\u003e { param -\u003e\n    TypedViewModelFactory(HomeViewModelWithParameter::class, instance(\"appTag\"), param as String)\n}\n```\nAnd to retrieve it with the given param in its constructor:\n```kotlin\nval param = \"Hello World!\"\nval homeViewModelWithParameter: HomeViewModelWithParameter by navBackStackEntry.viewModel(contextDI(), param)\n```\n\n## Tips and tricks\n### Improve compilation speed\nIn order to speed up the compilation process for `kapt`, it is recommended to add the following settings in\nyour `gradle.properties`:\n```groovy\n## Improves kapt speed with parallel annotation processing tasks, may impact in memory usage\nkapt.use.worker.api=true\n## Enables Gradle build cache\norg.gradle.caching=true\n```\n\n## Known issues\n### KSP gotchas\n#### KSP code is not recognized by the IntelliJ IDEs\nYou may find that KSP generated sources are not indexed by IntelliJ IDEs. You can solve this by\ndeclaring the proper source sets in your build.gradle:\n\nFor Android apps:\n```groovy\napplicationVariants.all { variant -\u003e\n    kotlin.sourceSets {\n        def flavors = variant.productFlavors.indexed().collect { index, item -\u003e\n            def flavorName = item.name\n            if (index \u003e 0) return flavorName.capitalize() else flavorName\n        }.join(\"\")\n\n        debug {\n            getByName(flavors) {\n                kotlin.srcDirs += \"build/generated/ksp/${flavors}Debug/kotlin\"\n            }\n        }\n\n        release {\n            getByName(flavors) {\n                kotlin.srcDirs += \"build/generated/ksp/${flavors}Release/kotlin\"\n            }\n        }\n    }\n}\n```\n\n#### KSP code generates code for test source sets\nYou may encounter that KSP also runs over test source sets, so if you set any code related to Mini\nin test sources, it will generate code that may override your main source set generated code.\n\nA workaround to avoid this is to disable any KSP task for test source sets:\n```groovy\nafterEvaluate {\n    tasks.matching {\n        it.name.startsWith(\"ksp\") \u0026\u0026 it.name.endsWith(\"TestKotlin\")\n    }.configureEach { it.enabled = false }\n}\n```\n\n## Acknowledgements\nThe work in this repository up to April 30th, 2021 was done by [bq](https://github.com/bq).\nThanks for all the work!!\n\n## License\nThis project is licensed under the Apache Software License, Version 2.0.\n```\n   Copyright 2021 HyperDevs\n   \n   Copyright 2019 BQ\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhyperdevs-team%2Fmini-kotlin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhyperdevs-team%2Fmini-kotlin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhyperdevs-team%2Fmini-kotlin/lists"}