Skip to content

feat: add Kotlin/Wasm (wasmJs) target - #426

Open
nicolasfara with Copilot wants to merge 8 commits into
masterfrom
copilot/add-wasm-support
Open

feat: add Kotlin/Wasm (wasmJs) target#426
nicolasfara with Copilot wants to merge 8 commits into
masterfrom
copilot/add-wasm-support

Conversation

Copilot AI commented Mar 8, 2026

Copy link
Copy Markdown
Contributor

Adds wasmJs as a supported target while keeping the existing js implementation untouched to avoid regressions.

JS module

Restored to the original 2.0.2 state — no changes from the pre-PR baseline.

wasmJs module (new, standalone)

Mirrors the jsMain structure without sharing code via an intermediate source set:

  • src/wasmJsMain/…/facade/MqttJsFacade.kt@JsModule("mqtt") external declarations using JsAny/Promise (no dynamic)
  • src/wasmJsMain/…/MqttJsClient.kt — full MkttClient implementation; Buffer→ByteArray conversion and JS object construction via top-level js() helper functions (required by Kotlin/Wasm's single-expression restriction)
  • src/wasmJsMain/…/MkttClient.wasmJs.ktactual fun createMqttClient(…) factory

Build

  • wasmJs { browser { … }; nodejs { … }; binaries.library() } added to build.gradle.kts with useMocha { timeout = "1m" } on both sub-targets (prevents CI hangs on broker connection)
  • npm("mqtt", "5.13.3") declared independently in jsMain and wasmJsMain (no shared webMain intermediate source set)

Platform support

Target Browser Node.js
js()
wasmJs()
wasmWasi()

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

- Enable wasmJs target in build.gradle.kts (browser + nodejs + library)
- Add npm('mqtt') dependency for wasmJs source set
- Create webMain intermediate source set (shared by js + wasmJs):
  - MkttClient.web.kt: actual factory function
  - MqttClientWrapper.kt: expect class abstracting platform JS interop
  - MqttJsClient.kt: refactored to delegate to MqttClientWrapper
- Add jsMain/MqttClientWrapper.js.kt: actual using dynamic types + object literals
- Add wasmJsMain/facade/MqttJsFacade.kt: wasmJs facade using JsAny, Promise
- Add wasmJsMain/MqttClientWrapper.wasmJs.kt: wasmJs actual using js() intrinsics
- Delete superseded jsMain files (MkttClient.js.kt, MqttJsClient.kt, adapter/MkttJsAdapter.kt)

Co-authored-by: nicolasfara <11615611+nicolasfara@users.noreply.github.com>
@gitguardian

gitguardian Bot commented Mar 8, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 2 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
28028363 Triggered Generic Password 29b0819 src/wasmJsMain/kotlin/io/github/nicolasfara/facade/MqttJsFacade.kt View secret
15856367 Triggered Generic Password e48dcba src/jsMain/kotlin/io/github/nicolasfara/adapter/MkttJsAdapter.kt View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

- Rename lambda variable 'w' to 'willConfig' in MqttClientWrapper.js.kt
- Add @Suppress annotations for false-positive detekt warnings in wasmJs
  (UnusedParameter: parameters referenced inside js() string literals;
   MatchingDeclarationName: standard KMP platform-suffix naming convention;
   MaxLineLength: js() requires a single non-splittable string literal)

Co-authored-by: nicolasfara <11615611+nicolasfara@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for WASM targets feat: add Kotlin/Wasm (wasmJs) target Mar 8, 2026
@nicolasfara
nicolasfara marked this pull request as ready for review March 8, 2026 14:35
}

actual suspend fun publish(topic: String, message: String, qos: Int) {
@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be avoided? @copilot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — changed the publishAsync(…, options: IClientPublishOptions) overload in the wasmJs facade to accept JsAny instead. Since the underlying JS API treats it as a plain object anyway, the js() result can now be passed directly without any cast or suppression. Fixed in d448338.

Comment thread src/jsMain/kotlin/io/github/nicolasfara/MqttClientWrapper.js.kt Outdated
JS: add typed PacketInfo external interface to JS facade, use it in
on() callback so packet.qos and packet.retain are already typed —
no cast needed.

wasmJs: change publishAsync(…, options) in wasmJs facade to accept
JsAny instead of IClientPublishOptions, so the js() result can be
passed directly without any cast.

Co-authored-by: nicolasfara <11615611+nicolasfara@users.noreply.github.com>
@nicolasfara

Copy link
Copy Markdown
Owner

@copilot update the README.md with new compatibility info

Co-authored-by: nicolasfara <11615611+nicolasfara@users.noreply.github.com>

Copilot AI commented Mar 8, 2026

Copy link
Copy Markdown
Contributor Author

@copilot update the README.md with new compatibility info

Done in 95c01c7. The Wasm row in the Supported Platforms table has been split: wasmJs() is now marked ✅ (browser & Node.js), while wasmWasi() remains ❌.

Comment thread build.gradle.kts
Comment on lines 78 to +83
jsMain.dependencies {
implementation(npm("mqtt", "5.13.3"))
}
wasmJsMain.dependencies {
implementation(npm("mqtt", "5.13.3"))
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the two dependencies (that are the same) can be refactored into a super source set? @copilot

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 014b9a5. The duplicate npm("mqtt", "5.13.3") declarations have been replaced with a single one in webMain, declared via creating with an explicit dependsOn(commonMain). Both jsMain and wasmJsMain then call dependsOn(webMain), so both targets still get the npm dependency transitively.

Co-authored-by: nicolasfara <11615611+nicolasfara@users.noreply.github.com>

Copilot AI commented Mar 8, 2026

Copy link
Copy Markdown
Contributor Author

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • scans-in.gradle.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED -XX:MaxMetaspaceSize=2g -Xmx2g -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@nicolasfara

Copy link
Copy Markdown
Owner

@copilot, the last CI fails, but no timeout and executes forever

Co-authored-by: nicolasfara <11615611+nicolasfara@users.noreply.github.com>

Copilot AI commented Mar 8, 2026

Copy link
Copy Markdown
Contributor Author

@copilot, the last CI fails, but no timeout and executes forever

The wasmJs target was missing Mocha timeouts on both its browser and nodejs subtargets. Without them, tests waiting on broker connections run indefinitely. Fixed in 6335007 by adding useMocha { timeout = "1m" } to both subtargets, matching the existing js target configuration.

Co-authored-by: nicolasfara <11615611+nicolasfara@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants