{"id":18266060,"url":"https://github.com/dokar3/quickjs-kt","last_synced_at":"2026-04-10T03:25:16.086Z","repository":{"id":221455882,"uuid":"753572056","full_name":"dokar3/quickjs-kt","owner":"dokar3","description":"A QuickJS binding for idiomatic Kotlin, with Async/DSL/ES Modules support.","archived":false,"fork":false,"pushed_at":"2025-07-11T01:33:17.000Z","size":5019,"stargazers_count":84,"open_issues_count":25,"forks_count":9,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-07-11T06:34:01.441Z","etag":null,"topics":["javascript","kotlin","kotlin-coroutines","kotlin-multiplatform","quickjs"],"latest_commit_sha":null,"homepage":"","language":"C","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/dokar3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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}},"created_at":"2024-02-06T11:44:15.000Z","updated_at":"2025-06-26T03:05:35.000Z","dependencies_parsed_at":"2024-03-07T06:24:29.165Z","dependency_job_id":"e4afc90b-e1ec-49ff-a448-023db4916d4d","html_url":"https://github.com/dokar3/quickjs-kt","commit_stats":null,"previous_names":["dokar3/quickjs-kt"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/dokar3/quickjs-kt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dokar3%2Fquickjs-kt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dokar3%2Fquickjs-kt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dokar3%2Fquickjs-kt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dokar3%2Fquickjs-kt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dokar3","download_url":"https://codeload.github.com/dokar3/quickjs-kt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dokar3%2Fquickjs-kt/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265474749,"owners_count":23772408,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["javascript","kotlin","kotlin-coroutines","kotlin-multiplatform","quickjs"],"created_at":"2024-11-05T11:21:33.772Z","updated_at":"2026-04-10T03:25:16.075Z","avatar_url":"https://github.com/dokar3.png","language":"C","readme":"\u003cdiv align=\"center\"\u003e\n\u003ch1\u003e QuickJS - KT \u003c/h1\u003e\n\u003cimg alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/io.github.dokar3/quickjs-kt?style=flat-square\u0026color=%23ea197e\"\u003e\n\u003cp\u003eRun your JavaScript code in Kotlin, asynchronously.\u003c/p\u003e\n\u003cimg src=\"./media/async-eval-demo.gif\" alt=\"The async eval demo\"\u003e\n\u003c/div\u003e\n\nThis is a [QuickJS](https://bellard.org/quickjs/) binding for idiomatic Kotlin, inspired by Cash App's [Zipline](https://github.com/cashapp/zipline) (previously Duktape Android) but with more flexibility.\n\n### Another QuickJS wrapper?\n\nThere are a few QuickJS wrappers for Android already. Some written in Java are not Kotlin Multiplatform friendly, and some lack updates.\n\nZipline is great and KMP-friendly, but it focuses on running Kotlin/JS modules. Its API is limited to running arbitrary JavaScript code with platform bindings.\n\nThat's why I created this library, with some good features:\n\n- Simple and idiomatic Kotlin APIs, it's easy to define binding and evaluate arbitrary code\n- Highly integrated with **Kotlin Coroutines**, it is `async` and `suspend`ed. See [#Async](#async)\n- Kotlin Multiplatform targets, including `Android`, `JVM` and `Kotlin/Native`\n- The latest version of QuickJS\n\n# Usages\n\n### Installation\n\nIn `build.gradle.kts`:\n\n```kotlin\nimplementation(\"io.github.dokar3:quickjs-kt:\u003cVERSION\u003e\")\n```\n\nOr in `libs.versions.toml`:\n\n```toml\nquickjs-kt = { module = \"io.github.dokar3:quickjs-kt\", version = \"\u003cVERSION\u003e\" }\n```\n\n### Evaluate\n\nwith DSL (This is recommended if you don't need long-live instances):\n\n```kotlin\ncoroutineScope.launch {\n    val result = quickJs {\n        evaluate\u003cInt\u003e(\"1 + 2\")\n    }\n}\n```\n\nwithout DSL:\n\n```kotlin\nval quickJs = QuickJs.create(Dispatchers.Default)\n\ncoroutineScope.launch {\n    val result = quickJs.evaluate\u003cInt\u003e(\"1 + 2\")\n    quickJs.close()\n}\n```\n\nEvaluate the compiled bytecode:\n\n```kotlin\ncoroutineScope.launch {\n    quickJs {\n        val bytecode = compile(\"1 + 2\")\n        val result = evaluate\u003cInt\u003e(bytecode)\n    }\n}\n```\n\n### Bindings\n\nWith DSL:\n\n```kotlin\nquickJs {\n    define(\"console\") {\n        function(\"log\") { args -\u003e\n            println(args.joinToString(\" \"))\n        }\n    }\n\n    function(\"fetch\") { args -\u003e\n        someClient.request(args[0])\n    }\n\n    function\u003cString, String\u003e(\"greet\") { \"Hello, $it!\" }\n\n    evaluate\u003cAny?\u003e(\n        \"\"\"\n        console.log(\"Hello from JavaScript!\")\n        fetch(\"https://www.example.com\")\n        greet(\"Jack\")\n        \"\"\".trimIndent()\n    )\n}\n```\n\nWith Reflection (JVM only):\n\n```kotlin\nclass Console {\n    fun log(args: Array\u003cAny?\u003e) = TODO()\n}\n\nclass Http {\n    fun fetch(url: String) = TODO()\n}\n\nquickJs {\n    define\u003cConsole\u003e(\"console\", Console())\n    define\u003cHttp\u003e(\"http\", Http())\n\n    evaluate\u003cAny?\u003e(\n        \"\"\"\n        console.log(\"Hello from JavaScript!\")\n        http.fetch(\"https://www.example.com\")\n        \"\"\".trimIndent()\n    )\n}\n```\n\nBinding classes need to be added to Android's ProGuard rules files.\n\n```\n-keep class com.example.Console { *; }\n-keep class com.example.Http { *; }\n```\n\n### Async\n\nThis library gives you the ability to define [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). Within the `QuickJs` instance, a coroutine scope is created to launch async jobs, a job `Dispatcher` can also be passed when creating the instance.\n\n`evaluate()` and `quickJs{}` are `suspend` functions, which make your async jobs await in the caller scope. All pending jobs will be canceled when the caller scope is canceled or the instance is closed.\n\nTo define async functions, easily call `asyncFunction()`:\n\n```kotlin\nquickJs {\n    define(\"http\") {\n        asyncFunction(\"request\") {\n            // Call suspend functions here\n        }\n    }\n\n    asyncFunction(\"fetch\") {\n        // Call suspend functions here\n    }\n}\n```\n\nIn JavaScript, you can use the top level await to easily get the result:\n\n```javascript\nconst resp = await http.request(\"https://www.example.com\");\nconst next = await fetch(\"https://www.example.com\");\n```\n\nOr use `Promise.all()` to run your request concurrently!\n\n```javascript\nconst responses = await Promise.all([\n    fetch(\"https://www.example.com/0\"),\n    fetch(\"https://www.example.com/1\"),\n    fetch(\"https://www.example.com/2\"),\n])\n```\n\n### Modules\n\n[ES Modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) are supported when `evaluate()` or `compile()` has the parameter `asModule = true`.\n\n```kotlin\nquickJs {\n    // ...\n    evaluate\u003cString\u003e(\n        \"\"\"\n            import * as hello from \"hello\";\n            // Use hello\n        \"\"\".trimIndent(),\n        asModule = true,\n    )\n}\n```\n\nModules can be added using `addModule()`  functions, both code and QuickJS bytecode are supported.\n\n```kotlin\nquickJs {\n    val helloModuleCode = \"\"\"\n        export function greeting() {\n            return \"Hi from the hello module!\";\n        }\n    \"\"\".trimIndent()\n    addModule(name = \"hello\", code = helloModuleCode)\n    // OR\n    val bytecode = compile(\n        code = helloModuleCode,\n        filename = \"hello\",\n        asModule = true,\n    )\n    addModule(bytecode)\n    // ...\n}\n```\n\nWhen evaluating ES module code, no return values will be captured, you may need a function binding to receive the result.\n\n```kotlin\nquickJs {\n    // ...\n    var result: Any? = null\n    function(\"returns\") { result = it.first() }\n\n    evaluate\u003cAny?\u003e(\n        \"\"\"\n            import * as hello from \"hello\";\n            // Pass the script result here\n            returns(hello.greeting());\n        \"\"\".trimIndent(),\n        asModule = true,\n    )\n    assertEquals(\"Hi from the hello module!\", result)\n}\n```\n\n### Alias\n\nWant shorter DSL names?\n\n```kotlin\nquickJs {\n    def(\"console\") {\n        prop(\"level\") {\n            getter { \"DEBUG\" }\n        }\n\n        func(\"log\") { }\n    }\n\n    func(\"fetch\") { \"Hello\" }\n\n    asyncFunc(\"delay\") { delay(1000) }\n\n    eval\u003cAny?\u003e(\"fetch()\")\n    eval\u003cAny?\u003e(compile(code = \"fetch()\"))\n}\n```\n\nUse the DSL aliases then!\n\n```diff\n-import com.dokar.quickjs.binding.define\n-import com.dokar.quickjs.binding.function\n-import com.dokar.quickjs.binding.asyncFunction\n-import com.dokar.quickjs.evaluate\n+import com.dokar.quickjs.alias.def\n+import com.dokar.quickjs.alias.func\n+import com.dokar.quickjs.alias.asyncFunc\n+import com.dokar.quickjs.alias.eval\n+import com.dokar.quickjs.alias.prop\n```\n\n# Type mappings\n\nSome built-in types are mapped automatically between C and Kotlin, this table shows how they are\nmapped.\n\n| JavaScript type | Kotlin type                           |\n|-----------------|---------------------------------------|\n| null            | null                                  |\n| undefined       | null (1)                              |\n| boolean         | Boolean                               |\n| Number          | Long/Int/Short/Byte, Double/Float (2) |\n| string          | String                                |\n| Array           | List\u003cAny?\u003e                            |\n| Set             | Set\u003cAny?\u003e                             |\n| Map             | Map\u003cAny?, Any?\u003e                       |\n| Error           | Error                                 |\n| object          | JsObject                              |\n| Int8Array       | ByteArray                             |\n| UInt8Array      | UByteArray                            |\n\n(1) A Kotlin `Unit` will be mapped to a JavaScript `undefined`, conversely, JavaScript `undefined` won't be mapped to Kotlin `Unit`.\n\n(2) When converting a JavaScript `Number` to Kotlin `Int`, `Short`, `Byte` or `Float` and the value is out of range, it will throw\n\n### Custom types\n\n`TypeConverter`s are used to support mapping non-built-in types. You can implement your own type\nconverters:\n\n```kotlin\ndata class FetchParams(val url: String, val method: String)\n\n// interface JsObjectConverter\u003cT : Any?\u003e : TypeConverter\u003cJsObject, T\u003e\nobject FetchParamsConverter : JsObjectConverter\u003cFetchParams\u003e {\n    override val targetType: KType = typeOf\u003cFetchParams\u003e()\n\n    override fun convertToTarget(value: JsObject): FetchParams = FetchParams(\n        url = value[\"url\"] as String,\n        method = value[\"method\"] as String,\n    )\n\n    override fun convertToSource(value: FetchParams): JsObject =\n        mapOf(\"url\" to value.url, \"method\" to value.method).toJsObject()\n}\n\nquickJs {\n    addTypeConverters(FetchParamsConverter)\n\n    asyncFunction\u003cFetchParams, String\u003e(\"fetch\") {\n        // Use the typed fetch params\n        val (url, method) = it\n        TODO()\n    }\n\n    val result = evaluate\u003cString\u003e(\n       \"\"\"await fetch({ url: \"https://example.com\", method: \"GET\" })\"\"\"\n    )\n}\n```\n\nYou can also use the converter from `quickjs-kt-converter-ktxserialization`\nand `quickjs-kt-convereter-moshi` (JVM only).\n\n1. Add the dependency\n\n    ```kotlin\n    implementation(\"io.github.dokar3:quickjs-kt-converter-ktxserialization:\u003cVERSION\u003e\")\n    // Or use the moshi converter\n    implementation(\"io.github.dokar3:quickjs-kt-converter-moshi:\u003cVERSION\u003e\")\n    ```\n\n2. Add the type converters of your classes\n\n    ```kotlin\n    import com.dokar.quickjs.conveter.SerializableConverter\n    // For moshi\n    import com.dokar.quickjs.conveter.JsonClassConverter\n\n    @kotlinx.serialization.Serializable\n    // For moshi\n    @com.squareup.moshi.JsonClass(generateAdapter = true)\n    data class FetchParams(val url: String, val method: String)\n\n    quickJs {\n        addTypeConverters(SerializableConverter\u003cFetchParams\u003e())\n        // For moshi\n        addTypeConverters(JsonClassConverter\u003cFetchParams\u003e())\n\n        asyncFunction\u003cFetchParams, String\u003e(\"fetch\") {\n            // Use the typed fetch params\n            val (url, method) = it\n            TODO()\n        }\n   \n        val result = evaluate\u003cString\u003e(\n            \"\"\"await fetch({ url: \"https://example.com\", method: \"GET\" })\"\"\"\n        )\n   }\n   ```\n\n\u003e [!NOTE]\n\u003e Functions with generic \u003cT, R\u003e require exactly 1 parameter on the JS side, it will throw if no parameter is passed or multiple parameters are passed.\n\n# Error handling\n\nMost of functions may throw:\n\n- `IllegalStateException`, if some function was called after calling `close`\n\n`evaluate()` and `compile()` may throw:\n\n- `QuickJsException`, if a JavaScript error occurred or failed to map a type between JavaScript and Kotlin\n- Other exceptions, if they were occurred in the Kotlin binding\n\nIf you find other suspicious errors, please feel free to open an issue to report\n\n# Samples\n\n- **[js-eval](./samples/js-eval)**: A GUI Compose Multiplatform app to evaluate JS, some minimal JS snippets are builtin\n- **[js-eval-android](./samples/js-eval-android)**: The Android version of the `js-eval` sample\n- **[openai](./samples/openai)**: Like `js-eval` but it has some Web API polyfills to run the bundled [openai-node](https://github.com/openai/openai-node)\n- **[openai-android](./samples/openai-android)**: The Android version of the `openai` sample\n- **[repl](./samples/repl)**: Simple Multiplatform REPL command line tool using [clikt](https://github.com/ajalt/clikt)\n\n# Development\n\nYou may need these tools to build and run this project:\n\n- Java JDK, both Windows, macOS, and Linux JDKs are required if you do a cross-compiling\n- Android SDK and NDK\n- [CMake](https://cmake.org/) The build system\n- [Ninja](https://ninja-build.org/) The build generator for CMake\n- [Zig](https://ziglang.org/) For cross-compiling the JNI libraries\n\n# License\n\n```\nCopyright 2024 dokar3\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdokar3%2Fquickjs-kt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdokar3%2Fquickjs-kt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdokar3%2Fquickjs-kt/lists"}