Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 19 additions & 69 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,39 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v6
- name: Run Android Lint (krelay)
run: ./gradlew :krelay:lintDebug --no-daemon
- name: Check public API is unchanged
run: ./gradlew :krelay:apiCheck :krelay-compose:apiCheck --no-daemon

# ──────────────────────────────────────────────────────────────────────────
test-android:
name: Unit Tests (JVM)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v6
- name: Run krelay unit tests
run: ./gradlew :krelay:testDebugUnitTest --no-daemon
- name: Run krelay-flow unit tests
run: ./gradlew :krelay-flow:testDebugUnitTest :krelay-flow:jvmTest --no-daemon
- name: Run JVM and Wasm tests
run: ./gradlew :krelay:jvmTest :krelay:wasmJsBrowserTest :krelay-flow:wasmJsBrowserTest --no-daemon
- name: Run krelay-testing unit tests
run: ./gradlew :krelay-testing:testDebugUnitTest --no-daemon
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: android-test-results
path: |
Expand All @@ -54,17 +58,17 @@ jobs:
name: Unit Tests (iOS Simulator)
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v6
- name: Run krelay iOS simulator tests
run: ./gradlew :krelay:iosSimulatorArm64Test --no-daemon
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ios-test-results
path: krelay/build/reports/
Expand All @@ -75,12 +79,12 @@ jobs:
runs-on: macos-14
needs: [ lint, test-android, test-ios ]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v6
- name: Build krelay (all targets)
run: ./gradlew :krelay:assemble --no-daemon
- name: Build krelay-flow (all targets)
Expand All @@ -98,61 +102,7 @@ jobs:
- name: Generate Dokka docs
run: ./gradlew :krelay:dokkaHtmlCustom --no-daemon
- name: Upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: krelay-artifacts
path: krelay/build/outputs/

# ──────────────────────────────────────────────────────────────────────────
publish-snapshot:
name: Publish Snapshot
runs-on: macos-14
needs: [ build ]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- name: Publish krelay to OSSRH
run: ./gradlew :krelay:publishAllPublicationsToOSSRHRepository --no-daemon
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
continue-on-error: true
- name: Publish krelay-flow to OSSRH
run: ./gradlew :krelay-flow:publishAllPublicationsToOSSRHRepository --no-daemon
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
continue-on-error: true
- name: Publish krelay-compose to OSSRH
run: ./gradlew :krelay-compose:publishAllPublicationsToOSSRHRepository --no-daemon
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
continue-on-error: true
- name: Publish krelay-testing to OSSRH
run: ./gradlew :krelay-testing:publishAllPublicationsToOSSRHRepository --no-daemon
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
continue-on-error: true
- name: Publish krelay-bom to OSSRH
run: ./gradlew :krelay-bom:publishAllPublicationsToOSSRHRepository --no-daemon
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
continue-on-error: true
69 changes: 47 additions & 22 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
outputs:
version: ${{ steps.parse.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Parse version from tag
id: parse
run: |
Expand All @@ -42,17 +42,17 @@ jobs:
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v6
- name: Run unit tests (Android & JVM & Wasm)
run: ./gradlew :krelay:testDebugUnitTest :krelay:jvmTest :krelay:wasmJsBrowserTest :krelay-flow:testDebugUnitTest :krelay-flow:jvmTest :krelay-flow:wasmJsBrowserTest --no-daemon
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: jvm-wasm-test-results
path: |
Expand All @@ -65,17 +65,17 @@ jobs:
runs-on: macos-14
needs: validate
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v6
- name: Run iOS simulator tests
run: ./gradlew :krelay:iosSimulatorArm64Test --no-daemon
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ios-test-results
path: krelay/build/reports/
Expand All @@ -86,12 +86,12 @@ jobs:
runs-on: ubuntu-latest
needs: validate
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v6
- name: Check public API is unchanged
run: ./gradlew :krelay:apiCheck :krelay-compose:apiCheck --no-daemon

Expand All @@ -101,12 +101,12 @@ jobs:
runs-on: macos-14
needs: [ test-jvm, test-ios, api-check ]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v6
- name: Build krelay
run: ./gradlew :krelay:assemble --no-daemon
- name: Build krelay-flow
Expand All @@ -122,7 +122,7 @@ jobs:
- name: Generate Dokka docs
run: ./gradlew :krelay:dokkaHtmlCustom --no-daemon
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: release-artifacts
path: |
Expand All @@ -136,12 +136,25 @@ jobs:
needs: build
environment: maven-central
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
java-version: '17'
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v3
- uses: gradle/actions/setup-gradle@v6

- name: Verify publishing secrets are set
run: |
missing=0
for v in OSSRH_USERNAME OSSRH_PASSWORD SIGNING_KEY SIGNING_PASSWORD; do
if [ -z "${!v}" ]; then echo "::error::Secret $v is empty or not set in the maven-central environment"; missing=1; fi
done
exit $missing
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}

- name: Publish krelay
run: ./gradlew :krelay:publishAllPublicationsToOSSRHRepository --no-daemon
Expand Down Expand Up @@ -183,13 +196,25 @@ jobs:
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}

# Legacy OSSRH (s01) was retired; uploads now land in the Central Portal
# staging API and must be handed over to the Portal to be published.
- name: Hand staged repository over to Central Portal
run: |
AUTH=$(printf '%s:%s' "$OSSRH_USERNAME" "$OSSRH_PASSWORD" | base64)
curl --fail-with-body -X POST \
-H "Authorization: Bearer $AUTH" \
"https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/dev.brewkits?publishing_type=automatic"
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}

# ──────────────────────────────────────────────────────────────────────────
github-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: publish
needs: [ validate, publish ]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Extract release notes from CHANGELOG.md
id: changelog
run: |
Expand All @@ -200,7 +225,7 @@ jobs:
echo "$NOTES" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ github.ref_name }}
name: "KRelay ${{ needs.validate.outputs.version }}"
Expand Down
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [2.3.0] - 2026-10-01

### Added
- **Structured logging**: `KRelayLog` with `KRelayLogLevel` (`DEBUG`/`INFO`/`WARN`/`ERROR`), a pluggable `KRelayLogSink` and a `minLevel` filter. Route KRelay output to Logcat, os_log, Timber, Datadog, etc. The default `ConsoleLogSink` prints to the console as before. A throwing sink never breaks dispatching.
- **Metrics reporter**: `KRelayMetricsReporter` + `KRelayMetricEvent` / `KRelayMetricType`, registered via `KRelayMetrics.addReporter()` / `removeReporter()`. Receives dispatch, queue, replay, expiry and clear events for forwarding to Firebase Performance, Datadog or any custom backend. The core module stays dependency-free — no SDK exporters are bundled; see `docs/MONITORING.md`.
- **`dispatchPersistedSuspend`** (`krelay-flow`): suspend overload of `dispatchPersisted` that runs factory lookup, queueing and the persistence write on `Dispatchers.Default`, so storage I/O never blocks the calling coroutine.

- **Test coverage tooling**: Kover is applied to `krelay` (`./gradlew :krelay:koverHtmlReport`). Core sources (excluding the bundled `samples` package) are at ~90% line coverage on JVM.
- **New test suites**: `performance/PerformanceTest` (throughput and scaling guards), `security/SecurityTest` (malformed/tampered persistence, key confusion, flooding, hostile callbacks) and `unit/CoverageGapTest` (singleton KClass entry points, error isolation, expiry, persistence restore edge cases, diagnostics).

### Fixed
- **Quadratic enqueue cost**: every enqueue scanned the whole queue for expired actions and read the clock per element. Queueing 20k actions took ~66 s on the iOS simulator (milliseconds on JVM). The sweep now runs only when the oldest queued action can actually have expired (~0.05 s for the same workload).
- **Persisted-action factory key collision**: factories were looked up by `"$featureKey::$actionKey"`, so `("a", "b::c")` and `("a::b", "c")` shared one slot. Keys are now length-prefixed.
- **`KRelay.getMetrics`-style internal helper omitted `cleared`**: `getMetricsInternal` (used for iOS interop) now reports the same five counters as `getMetrics<T>()`.
- **`krelay-compose` instrumented tests did not compile** (missing test dependencies and runner); they now run (`connectedDebugAndroidTest`).
- **Demo app**: implementations registered inline were garbage-collected (KRelay holds them weakly), so dispatches were queued; the selected demo was lost on rotation; `moko-permissions` 0.18.0 broke the Kotlin/Native 2.3 iOS link and was upgraded to 0.20.1 (modular API).
- **Per-instance metrics were never populated**: `KRelayInstanceImpl` recorded metrics without its `scopeName`, so `KRelayMetrics.getInstanceMetrics(scope)` always returned an empty map. Dispatch, queue, replay and clear events are now attributed to their instance.
- **Expired actions were never counted**: `recordExpiry` was never called; expired actions dropped on replay are now recorded.

### Changed
- **Toolchain**: Kotlin 2.1.0 → 2.3.21, Compose Multiplatform 1.8.0-alpha03 → 1.10.3, Decompose 3.4.0-alpha03 → 3.5.0. Public API dumps gained only compiler-generated synthetic `$default` entries.
- **Security**: the stale JS `kotlin-js-store/yarn.lock` was removed; the wasm toolchain no longer resolves the vulnerable `qs`, `serialize-javascript` and `webpack` versions flagged by Dependabot (dev/test-only, never shipped).
- **Warnings** are now logged as `[KRelay][scope][WARN] …` (previously `[KRelay][scope] [WARN] …`).
- **CI/CD**: Maven Central publishing moved to the Central Portal OSSRH staging API (`s01.oss.sonatype.org` is retired); `github-release` now declares `needs: validate`; GitHub Actions bumped to Node 24-ready majors.

---

## [2.2.0] - 2026-09-05

### Added
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ KRelay now provides a **Bill of Materials (BOM)** to automatically align version
sourceSets {
commonMain.dependencies {
// 1. (Recommended) Import the BOM
api(platform("dev.brewkits:krelay-bom:2.2.0"))
api(platform("dev.brewkits:krelay-bom:2.3.0"))

// 2. Add dependencies without specifying versions
implementation("dev.brewkits:krelay")
Expand Down Expand Up @@ -399,6 +399,7 @@ See [Integration Guides](docs/INTEGRATION_GUIDES.md) for step-by-step examples.

| KRelay | Kotlin | AGP | Android minSdk | iOS | Desktop (JVM) | WasmJs |
|---|---|---|---|---|---|---|
| 2.3.x | 2.2+ (built with 2.3.21) | 8.x | 24 | 14.0+ | ✅ | ✅ |
| 2.2.x | 2.1.x | 8.x | 24 | 14.0+ | ✅ | ✅ |
| 2.1.x | 2.1.x | 8.x | 24 | 14.0+ | — | — |
| 2.0.x | 2.1.x | 8.x | 24 | 14.0+ | — | — |
Expand All @@ -412,6 +413,23 @@ See [Integration Guides](docs/INTEGRATION_GUIDES.md) for step-by-step examples.
## What's New

<details open>
<summary><strong>v2.3.0 — Observability</strong> <em>(Oct 2026)</em></summary>

- **Structured logging** — `KRelayLog` with severity levels and pluggable `KRelayLogSink`.
- **Metrics reporter** — `KRelayMetricsReporter` forwards dispatch / queue / replay / expiry / clear events to Firebase, Datadog or your own backend. See [Monitoring & Logging](docs/MONITORING.md).
- **`dispatchPersistedSuspend`** in `krelay-flow` — persist from coroutines without blocking on storage I/O.
- **Fixes** — per-instance metrics are now actually attributed to their scope; expired actions are counted.
- **Toolchain** — built with Kotlin 2.3.21 and Compose Multiplatform 1.10.3. Upgrade guide: [Migration to v2.3](docs/MIGRATION_V2_3.md).
</details>

<details>
<summary><strong>v2.2.0 — Desktop, Web & Flow Adapter</strong> <em>(Sep 2026)</em></summary>

- JVM Desktop and Kotlin/WasmJs targets
- `krelay-flow` artifact with the `relayTo` operator
</details>

<details>
<summary><strong>v2.1.1 — QA Hardening & Ecosystem Infrastructure</strong> <em>(Sep 2026)</em></summary>

- **`krelay-testing` artifact** — `FakeKRelayInstance` with a full assertion API (`assertDispatched`, `assertNotDispatched`, `executeLastDispatch`) for clean, mock-free unit testing.
Expand Down Expand Up @@ -457,6 +475,8 @@ See [Integration Guides](docs/INTEGRATION_GUIDES.md) for step-by-step examples.
| [Architecture](docs/ARCHITECTURE.md) | Internals deep dive |
| [API Reference](docs/QUICK_REFERENCE.md) | Full API cheat sheet |
| [Managing Warnings](docs/MANAGING_WARNINGS.md) | Suppress `@OptIn` at module level |
| [Monitoring & Logging](docs/MONITORING.md) | `KRelayLog`, `KRelayMetricsReporter` |
| [Migration to v2.3](docs/MIGRATION_V2_3.md) | Toolchain requirements and behaviour changes |
| [Migration to v2.0](docs/MIGRATION_V2.md) | Upgrading from v1.x |

---
Expand Down
Loading
Loading