diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..74bcfcc
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+# ABI references must retain the Kotlin generator's exact trailing blank lines.
+api/*.api whitespace=-blank-at-eof
+api-full/*.api whitespace=-blank-at-eof
diff --git a/.github/actions/setup-build/action.yml b/.github/actions/setup-build/action.yml
new file mode 100644
index 0000000..e27d53a
--- /dev/null
+++ b/.github/actions/setup-build/action.yml
@@ -0,0 +1,43 @@
+name: Set up library build
+description: Install the JVM runtimes and restore Gradle and Kotlin Native caches.
+inputs:
+ cache-encryption-key:
+ description: Encryption key for the Gradle configuration cache
+ required: false
+ gradle-cache-read-only:
+ description: Restore Gradle User Home without saving publication-job state
+ default: 'false'
+ target-profile:
+ description: Separate cached Native and configuration state for normal and full targets
+ default: default
+runs:
+ using: composite
+ steps:
+ - uses: actions/setup-java@v6
+ with:
+ distribution: temurin
+ java-version: |
+ 17
+ 25
+ - uses: gradle/actions/setup-gradle@v6
+ with:
+ cache-read-only: ${{ inputs.gradle-cache-read-only }}
+ cache-encryption-key: ${{ inputs.cache-encryption-key }}
+ - uses: actions/cache@v6
+ with:
+ path: ~/.konan
+ key: ${{ runner.os }}-${{ runner.arch }}-konan-${{ inputs.target-profile }}-${{ hashFiles('build.gradle.kts', 'settings.gradle.kts', 'gradle.properties', 'gradle/wrapper/gradle-wrapper.properties', 'gradle/*.versions.toml') }}
+ restore-keys: |
+ ${{ runner.os }}-${{ runner.arch }}-konan-${{ inputs.target-profile }}-
+ ${{ runner.os }}-${{ runner.arch }}-konan-
+ # setup-gradle v6 caches Gradle User Home; persist project entries explicitly.
+ # GRADLE_ENCRYPTION_KEY is exported by setup-gradle and Gradle encrypts these files.
+ - name: Cache encrypted configuration state
+ if: ${{ inputs.cache-encryption-key != '' }}
+ uses: actions/cache@v6
+ with:
+ path: |
+ .gradle/configuration-cache
+ consumer/.gradle/configuration-cache
+ key: ${{ runner.os }}-${{ runner.arch }}-configuration-${{ inputs.target-profile }}-${{ hashFiles('**/*.gradle.kts', '**/gradle.properties', 'gradle/wrapper/gradle-wrapper.properties') }}-${{ github.sha }}
+ restore-keys: ${{ runner.os }}-${{ runner.arch }}-configuration-${{ inputs.target-profile }}-${{ hashFiles('**/*.gradle.kts', '**/gradle.properties', 'gradle/wrapper/gradle-wrapper.properties') }}-
diff --git a/.github/scripts/release-version.py b/.github/scripts/release-version.py
new file mode 100644
index 0000000..2667ec5
--- /dev/null
+++ b/.github/scripts/release-version.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+"""Resolve release, snapshot and GitHub-only release-candidate versions."""
+
+import os
+import re
+import subprocess
+from pathlib import Path
+
+CORE = r"(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)"
+STABLE = re.compile(CORE)
+RC = re.compile(rf"({CORE})-rc\.(0|[1-9][0-9]*)")
+VERSION = re.compile(rf"{CORE}(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?")
+
+
+def resolve(tags, ref_type, ref_name):
+ if ref_type == "tag":
+ if not ref_name.startswith("v"):
+ raise ValueError("Release tags must start with v")
+ version = ref_name[1:]
+ elif ref_type == "branch" and ref_name == "main":
+ stable = [tuple(map(int, tag[1:].split("."))) for tag in tags
+ if tag.startswith("v") and STABLE.fullmatch(tag[1:])]
+ latest = max(stable, default=(0, 0, 0))
+ version = f"{latest[0]}.{latest[1]}.{latest[2] + 1}-SNAPSHOT"
+ else:
+ raise ValueError("Releases require a version tag; automatic snapshots use main")
+ match = VERSION.fullmatch(version)
+ if not match or (match[1] and any(part.isascii() and part.isdigit() and len(part) > 1 and part.startswith("0")
+ for part in match[1].split("."))):
+ raise ValueError(f"Invalid release version: {version}")
+ return {
+ "version": version,
+ "is_snapshot": str(version.endswith("-SNAPSHOT")).lower(),
+ "is_prerelease": str(match[1] is not None).lower(),
+ "test_release": str(RC.fullmatch(version) is not None).lower(),
+ "release_tag": f"v{version}",
+ }
+
+
+if __name__ == "__main__":
+ tags = subprocess.check_output(["git", "tag", "--list"], text=True).splitlines()
+ try:
+ values = resolve(tags, os.environ["GITHUB_REF_TYPE"], os.environ["GITHUB_REF_NAME"])
+ except ValueError as error:
+ raise SystemExit(str(error)) from error
+ values["revision"] = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip()
+ with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
+ output.writelines(f"{key}={value}\n" for key, value in values.items())
+ print(f"Resolved {values['version']}; GitHub-only RC: {values['test_release']}")
diff --git a/.github/scripts/test-release-version.py b/.github/scripts/test-release-version.py
new file mode 100644
index 0000000..2ba9f48
--- /dev/null
+++ b/.github/scripts/test-release-version.py
@@ -0,0 +1,45 @@
+"""Exercise exact release-tag routing and automatic main snapshots."""
+
+import importlib.util
+import unittest
+from pathlib import Path
+
+spec = importlib.util.spec_from_file_location("versions", Path(__file__).with_name("release-version.py"))
+versions = importlib.util.module_from_spec(spec)
+spec.loader.exec_module(versions)
+
+
+class ReleaseVersionTest(unittest.TestCase):
+ def test_main_snapshot_ignores_prerelease_tags(self):
+ tags = ["v1.9.0", "v1.10.0", "v2.0.0-rc.9"]
+ result = versions.resolve(tags, "branch", "main")
+ self.assertEqual("1.10.1-SNAPSHOT", result["version"])
+ self.assertEqual("true", result["is_snapshot"])
+ self.assertEqual("false", result["test_release"])
+ self.assertEqual("0.0.1-SNAPSHOT", versions.resolve([], "branch", "main")["version"])
+
+ def test_rc_tag_is_used_exactly_without_incrementing(self):
+ result = versions.resolve(["v1.3.0", "v1.3.1-rc.10"], "tag", "v1.3.1-rc.2")
+ self.assertEqual("1.3.1-rc.2", result["version"])
+ self.assertEqual("v1.3.1-rc.2", result["release_tag"])
+ self.assertEqual("true", result["test_release"])
+ self.assertEqual("true", result["is_prerelease"])
+ self.assertEqual("false", result["is_snapshot"])
+
+ def test_stable_tag_routes_to_central(self):
+ result = versions.resolve(["v1.3.0"], "tag", "v1.4.0")
+ self.assertEqual("1.4.0", result["version"])
+ self.assertEqual("false", result["test_release"])
+ self.assertEqual("false", result["is_prerelease"])
+ self.assertEqual("false", result["is_snapshot"])
+
+ def test_invalid_tags_and_non_main_branches_fail(self):
+ for tag in ["v01.3.1", "v1.3.1-rc.01", "v1.3.1-rc..1", "v1.3.1-", "1.3.1"]:
+ with self.subTest(tag=tag), self.assertRaises(ValueError):
+ versions.resolve([], "tag", tag)
+ with self.assertRaisesRegex(ValueError, "require a version tag"):
+ versions.resolve([], "branch", "feature")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/.github/scripts/test-verification.py b/.github/scripts/test-verification.py
new file mode 100644
index 0000000..e71e64b
--- /dev/null
+++ b/.github/scripts/test-verification.py
@@ -0,0 +1,41 @@
+"""Checks that the CI report guard rejects green builds without passing tests."""
+
+import importlib.util
+import tempfile
+import unittest
+from pathlib import Path
+
+spec = importlib.util.spec_from_file_location("reports", Path(__file__).with_name("verify-test-results.py"))
+reports = importlib.util.module_from_spec(spec)
+spec.loader.exec_module(reports)
+
+
+class ReportVerificationTest(unittest.TestCase):
+ def test_missing_reports(self):
+ with tempfile.TemporaryDirectory() as directory:
+ with self.assertRaisesRegex(ValueError, "No test reports"):
+ reports.verify(directory)
+
+ def test_rejects_empty_skipped_and_failing_results(self):
+ examples = [
+ '',
+ '',
+ '',
+ '',
+ ]
+ for xml in examples:
+ with self.subTest(xml=xml), tempfile.TemporaryDirectory() as directory:
+ Path(directory, "TEST-example.xml").write_text(xml)
+ with self.assertRaises(ValueError):
+ reports.verify(directory)
+
+ def test_accepts_executed_tests_alongside_skipped_tests(self):
+ with tempfile.TemporaryDirectory() as directory:
+ Path(directory, "TEST-example.xml").write_text(
+ ''
+ )
+ reports.verify(directory)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/.github/scripts/verify-consumer.sh b/.github/scripts/verify-consumer.sh
new file mode 100644
index 0000000..fc5d544
--- /dev/null
+++ b/.github/scripts/verify-consumer.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# The fixture deliberately uses no KBuild conventions or build platform.
+set -euo pipefail
+version=${1:?usage: verify-consumer.sh VERSION}
+repository="$(pwd)/build/staging-repo"
+./gradlew -p consumer run "-Pconsumer.repository=$repository" "-Pconsumer.version=$version"
+./gradlew -p consumer run "-Pconsumer.repository=$repository" "-Pconsumer.version=$version" -Pconsumer.pomOnly=true
+if [[ $# -gt 1 ]]; then
+ ./gradlew -p consumer run "-Pconsumer.version=$version" "-Pconsumer.githubRepository=$2"
+fi
diff --git a/.github/scripts/verify-publication.py b/.github/scripts/verify-publication.py
new file mode 100644
index 0000000..5d7168b
--- /dev/null
+++ b/.github/scripts/verify-publication.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+"""Verify the complete staged KMP publication before independent consumer resolution."""
+
+import argparse
+import hashlib
+import json
+import re
+import xml.etree.ElementTree as ET
+from pathlib import Path
+from zipfile import ZipFile
+
+
+def verify(repository, version, full=False, github=False):
+ settings = Path(__file__).resolve().parents[2] / "settings.gradle.kts"
+ library = re.search(r'rootProject\.name\s*=\s*"([^"\n]+)"', settings.read_text()).group(1)
+ repository = Path(repository).resolve()
+ base = repository if github else repository / "com/eignex"
+ targets = ["jvm", "linuxx64"]
+ if full:
+ targets += ["js", "wasm-js", "wasm-wasi", "linuxarm64", "macosarm64", "mingwx64", "iosx64", "iosarm64", "iossimulatorarm64"]
+ modules = [library] + [library + "-" + target for target in targets]
+ for module in modules:
+ directory = base if github else base / module / version
+ stem = f"{module}-{version}"
+ metadata = directory / f"{stem}.module"
+ pom = directory / f"{stem}.pom"
+ data = json.loads(metadata.read_text())
+ ET.parse(pom)
+ if module == library:
+ published = {variant["available-at"]["module"] for variant in data["variants"] if "available-at" in variant}
+ if published != set(modules[1:]):
+ raise ValueError(f"Publication targets do not match the requested profile: {sorted(published)}")
+ if "kbuild-platform" in metadata.read_text() or "kbuild-platform" in pom.read_text():
+ raise ValueError(f"Build platform leaked into {module}")
+ for variant in data["variants"]:
+ redirect = variant.get("available-at")
+ if redirect and not (directory / redirect["url"]).is_file():
+ raise ValueError(f"Missing target metadata: {redirect['url']}")
+ for artifact in variant.get("files", []):
+ path = directory / artifact["url"]
+ if not path.is_file() or path.stat().st_size != artifact["size"]:
+ raise ValueError(f"Missing or truncated artifact: {path}")
+ if hashlib.sha256(path.read_bytes()).hexdigest() != artifact["sha256"]:
+ raise ValueError(f"Artifact checksum mismatch: {path}")
+ for dependency in variant.get("dependencies", []):
+ if not any(dependency.get("version", {}).get(key) for key in ("requires", "strictly", "prefers")):
+ raise ValueError(f"Versionless dependency in {module}: {dependency}")
+ for classifier in ("sources", "javadoc"):
+ with ZipFile(directory / f"{stem}-{classifier}.jar") as archive:
+ if not any(name != "META-INF/MANIFEST.MF" and not name.endswith("/") for name in archive.namelist()):
+ raise ValueError(f"Empty {classifier} artifact for {module}")
+ if module == library + "-jvm":
+ for variant in data["variants"]:
+ if (variant.get("attributes", {}).get("org.gradle.category") == "library"
+ and variant["attributes"].get("org.gradle.usage") in ("java-api", "java-runtime")):
+ if variant["attributes"].get("org.gradle.jvm.version") != 17:
+ raise ValueError("Published JVM requirement must remain Java 17")
+ with ZipFile(directory / f"{stem}.jar") as archive:
+ for name in archive.namelist():
+ if name.endswith(".class") and int.from_bytes(archive.read(name)[6:8], "big") > 61:
+ raise ValueError(f"Bytecode above Java 17: {name}")
+ if github:
+ checksums = dict(line.split(" ", 1)[::-1] for line in (base / "SHA256SUMS").read_text().splitlines())
+ files = {path.name for path in base.iterdir() if path.is_file() and path.name != "SHA256SUMS"}
+ if set(checksums) != files:
+ raise ValueError("GitHub checksum manifest does not cover every release asset")
+ for name, digest in checksums.items():
+ if hashlib.sha256((base / name).read_bytes()).hexdigest() != digest:
+ raise ValueError(f"GitHub asset checksum mismatch: {name}")
+ print(f"Verified {len(modules)} complete publications at {version}")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("repository")
+ parser.add_argument("version")
+ parser.add_argument("--full", action="store_true", help="Require the complete release target set")
+ parser.add_argument("--github", action="store_true", help="Verify flat GitHub Release assets and their checksum manifest")
+ args = parser.parse_args()
+ verify(args.repository, args.version, args.full, args.github)
diff --git a/.github/scripts/verify-test-results.py b/.github/scripts/verify-test-results.py
new file mode 100644
index 0000000..557a002
--- /dev/null
+++ b/.github/scripts/verify-test-results.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+"""Reject missing, empty, fully skipped, or failing expected test targets."""
+
+import sys
+import xml.etree.ElementTree as ET
+from pathlib import Path
+
+
+def verify(directory):
+ reports = sorted(Path(directory).glob("TEST-*.xml"))
+ if not reports:
+ raise ValueError(f"No test reports in {directory}")
+ executed = 0
+ for report in reports:
+ suite = ET.parse(report).getroot()
+ cases = list(suite.iter("testcase"))
+ if (int(suite.get("failures", 0)) or int(suite.get("errors", 0))
+ or any(case.find("failure") is not None or case.find("error") is not None for case in cases)):
+ raise ValueError(f"Failing test report: {report}")
+ executed += sum(case.find("skipped") is None for case in cases)
+ if not executed:
+ raise ValueError(f"No executed tests in {directory}")
+ print(f"Verified {executed} tests in {directory}")
+
+
+if __name__ == "__main__":
+ if len(sys.argv) < 2:
+ sys.exit("usage: verify-test-results.py DIRECTORY...")
+ try:
+ for directory in sys.argv[1:]:
+ verify(directory)
+ except (ValueError, ET.ParseError) as error:
+ sys.exit(str(error))
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 20b6a39..cabba7e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,11 +5,23 @@ on:
branches: [ main ]
pull_request:
workflow_dispatch:
+ inputs:
+ fullTargets:
+ description: Validate all publication targets and extra native hosts
+ type: boolean
+ default: false
workflow_call:
inputs:
ref:
type: string
default: ''
+ ciVersion:
+ description: Version to stage and verify; releases use their actual version
+ type: string
+ default: '0.0.0-ci'
+ fullTargets:
+ type: boolean
+ default: false
secrets:
GRADLE_ENCRYPTION_KEY:
required: false
@@ -38,34 +50,58 @@ jobs:
persist-credentials: false
fetch-depth: 2
- - name: Set up JDK
- uses: actions/setup-java@v6
+ - name: Set up build
+ uses: ./.github/actions/setup-build
with:
- distribution: temurin
- java-version: 25
-
- # Build jobs persist dependency and encrypted configuration caches.
- - name: Set up Gradle
- uses: gradle/actions/setup-gradle@v6
- with:
- cache-read-only: false
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
+ target-profile: ${{ inputs.fullTargets && 'full' || 'default' }}
- - name: Cache Kotlin/Native toolchain
- uses: actions/cache@v6
- with:
- path: ~/.konan
- key: ${{ runner.os }}-${{ runner.arch }}-konan-${{ hashFiles('**/*.gradle.kts', '**/gradle.properties', '**/gradle-wrapper.properties', '**/*.versions.toml') }}
- restore-keys: ${{ runner.os }}-${{ runner.arch }}-konan-
+ - name: Validate CI guards
+ run: |
+ python3 .github/scripts/test-verification.py
+ python3 .github/scripts/test-release-version.py
- - name: Grant execute permission for Gradle
- run: chmod +x gradlew
+ # Routine builds use a stable private version for configuration-cache reuse.
+ # Releases rerun this gate at their actual publication version.
+ # The independent consumer treats only this local artifact as changing.
+ # One graph shares compilation, lint, docs and publication artifacts.
+ - name: Check library and stage publications
+ env:
+ CONSUMER_VERSION: ${{ inputs.ciVersion || '0.0.0-ci' }}
+ FULL_TARGETS: ${{ inputs.fullTargets || false }}
+ run: |
+ target_args=()
+ if [[ "$FULL_TARGETS" == true ]]; then target_args+=(-Ptargets.full); fi
+ ./gradlew check lintDocs koverXmlReport publishAllPublicationsToLocalStagingRepository \
+ -PciVersion="$CONSUMER_VERSION" "${target_args[@]}"
- - name: Run linter
- run: ./gradlew lintDocs
+ - name: Require executed tests for the selected target set
+ env:
+ FULL_TARGETS: ${{ inputs.fullTargets || false }}
+ run: |
+ reports=(build/test-results/jvmTest build/test-results/jvmTestJarOnJava17
+ build/test-results/jvmTestJarOnJava25 build/test-results/linuxX64Test)
+ if [[ "$FULL_TARGETS" == true ]]; then
+ reports+=(build/test-results/jsBrowserTest build/test-results/jsNodeTest
+ build/test-results/wasmJsBrowserTest build/test-results/wasmJsNodeTest
+ build/test-results/wasmWasiNodeTest)
+ fi
+ python3 .github/scripts/verify-test-results.py "${reports[@]}"
- - name: Run tests with coverage
- run: ./gradlew check koverXmlReport
+ - name: Verify staged publications and independent consumers
+ env:
+ CONSUMER_VERSION: ${{ inputs.ciVersion || '0.0.0-ci' }}
+ FULL_TARGETS: ${{ inputs.fullTargets || false }}
+ run: |
+ target_args=()
+ if [[ "$FULL_TARGETS" == true ]]; then target_args+=(--full); fi
+ python3 .github/scripts/verify-publication.py build/staging-repo "$CONSUMER_VERSION" "${target_args[@]}"
+ bash .github/scripts/verify-consumer.sh "$CONSUMER_VERSION"
+
+ - name: Require committed JavaScript and Wasm lockfiles
+ run: |
+ git diff --exit-code -- kotlin-js-store
+ test -z "$(git ls-files --others --exclude-standard -- kotlin-js-store)"
- name: Upload test reports
if: always()
@@ -84,3 +120,57 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
files: build/reports/kover/report.xml
fail_ci_if_error: false
+
+ native:
+ if: ${{ inputs.fullTargets == true }}
+ name: Native tests (${{ matrix.os }})
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: macos-15
+ target: macosArm64
+ python: python3
+ - os: windows-latest
+ target: mingwX64
+ python: python
+ runs-on: ${{ matrix.os }}
+ timeout-minutes: 15
+ steps:
+ - uses: actions/checkout@v7
+ with:
+ ref: ${{ inputs.ref || github.sha }}
+ persist-credentials: false
+ - name: Set up build
+ uses: ./.github/actions/setup-build
+ with:
+ cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
+ target-profile: full
+ # Native checks only: lint, coverage and web tests belong to the Linux job.
+ - name: Run native tests
+ shell: bash
+ run: ./gradlew ${{ matrix.target }}Test -Ptargets.full --max-workers=2 -Pkotlin.native.parallelThreads=2
+ - name: Require executed native tests
+ shell: bash
+ run: ${{ matrix.python }} .github/scripts/verify-test-results.py build/test-results/${{ matrix.target }}Test
+ # Extra native hosts and simulator linking are part of explicit full validation only.
+ - name: Run iOS simulator tests
+ if: runner.os == 'macOS'
+ shell: bash
+ run: |
+ device=$(xcrun simctl list devices available --json | python3 -c '
+ import json, sys
+ devices = json.load(sys.stdin)["devices"]
+ print(next(d["udid"] for runtime, items in devices.items() if ".iOS-" in runtime
+ for d in items if d["name"].startswith("iPhone")))')
+ ./gradlew iosSimulatorArm64Test -Ptargets.full --max-workers=2 -Pkotlin.native.parallelThreads=2 -PiosSimulatorDevice="$device"
+ python3 .github/scripts/verify-test-results.py build/test-results/iosSimulatorArm64Test
+ - name: Upload native test reports
+ if: always()
+ uses: actions/upload-artifact@v7
+ with:
+ name: test-reports-${{ matrix.target }}
+ path: |
+ build/reports/tests/
+ build/test-results/
+ if-no-files-found: ignore
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index aa53edb..8655118 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -7,12 +7,6 @@ on:
branches: [main]
push:
tags: ['v*']
- workflow_dispatch:
- inputs:
- dryRun:
- description: Run the release build and publish nothing
- type: boolean
- default: false
permissions:
contents: read
@@ -36,7 +30,8 @@ jobs:
version: ${{ steps.version.outputs.version }}
is_snapshot: ${{ steps.version.outputs.is_snapshot }}
is_prerelease: ${{ steps.version.outputs.is_prerelease }}
- dry_run: ${{ steps.version.outputs.dry_run }}
+ test_release: ${{ steps.version.outputs.test_release }}
+ release_tag: ${{ steps.version.outputs.release_tag }}
revision: ${{ steps.version.outputs.revision }}
steps:
- name: Checkout
@@ -47,52 +42,18 @@ jobs:
fetch-depth: 0
- name: Resolve publication version
id: version
- env:
- DRY_RUN: ${{ inputs.dryRun || false }}
- run: |
- next_patch() {
- local tag major minor patch
- while IFS= read -r tag; do
- if [[ "$tag" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
- major=${BASH_REMATCH[1]}
- minor=${BASH_REMATCH[2]}
- patch=${BASH_REMATCH[3]}
- printf '%s.%s.%s\n' "$major" "$minor" "$((10#$patch + 1))"
- return
- fi
- done < <(git tag --list 'v*' --sort=-v:refname)
- echo '0.0.1'
- }
- if [[ "$DRY_RUN" == true ]]; then
- version=$(next_patch)
- elif [[ "$GITHUB_REF_TYPE" == tag ]]; then
- version=${GITHUB_REF_NAME#v}
- else
- version="$(next_patch)-SNAPSHOT"
- fi
- if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
- echo "::error::invalid publication version: $version" >&2
- exit 1
- fi
- is_snapshot=false
- [[ "$version" != *-SNAPSHOT ]] || is_snapshot=true
- is_prerelease=false
- [[ "$version" != *-* ]] || is_prerelease=true
- {
- echo "version=$version"
- echo "is_snapshot=$is_snapshot"
- echo "is_prerelease=$is_prerelease"
- echo "dry_run=$DRY_RUN"
- echo "revision=$(git rev-parse HEAD)"
- } >> "$GITHUB_OUTPUT"
+ run: python3 .github/scripts/release-version.py
- # Automatic snapshots already passed this gate.
+ # Releases rerun Linux checks at the release version alongside macOS/iOS and Windows.
+ # Publication waits for every platform. Snapshots reuse the normal main build.
validate:
if: github.event_name != 'workflow_run'
needs: prepare
uses: ./.github/workflows/build.yml
with:
ref: ${{ needs.prepare.outputs.revision }}
+ ciVersion: ${{ needs.prepare.outputs.version }}
+ fullTargets: true
secrets:
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
@@ -110,60 +71,70 @@ jobs:
env:
RELEASE_VERSION: ${{ needs.prepare.outputs.version }}
IS_SNAPSHOT: ${{ needs.prepare.outputs.is_snapshot }}
- DRY_RUN: ${{ needs.prepare.outputs.dry_run }}
+ IS_TEST_RELEASE: ${{ needs.prepare.outputs.test_release }}
+ RELEASE_TAG: ${{ needs.prepare.outputs.release_tag }}
steps:
- name: Checkout
uses: actions/checkout@v7
with:
ref: ${{ needs.prepare.outputs.revision }}
persist-credentials: false
- - name: Set up JDK
- uses: actions/setup-java@v6
+ - name: Set up build
+ uses: ./.github/actions/setup-build
with:
- distribution: temurin
- java-version: 25
- - name: Set up Gradle
- uses: gradle/actions/setup-gradle@v6
- with:
- cache-read-only: true
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
- - name: Restore Kotlin/Native toolchain
- uses: actions/cache/restore@v6
- with:
- path: ~/.konan
- key: ${{ runner.os }}-${{ runner.arch }}-konan-${{ hashFiles('**/*.gradle.kts', '**/gradle.properties', '**/gradle-wrapper.properties', '**/*.versions.toml') }}
- restore-keys: ${{ runner.os }}-${{ runner.arch }}-konan-
+ gradle-cache-read-only: 'true'
+ target-profile: full
- - name: Build and Test
+ # The reusable validation job already ran the full checks and uploaded test reports.
+ # Recheck the publication job's staged artifacts and consumer resolution before publishing.
+ - name: Verify release publication
if: env.IS_SNAPSHOT == 'false'
- run: ./gradlew build -PciVersion="$RELEASE_VERSION"
- - name: Upload test reports
- if: always() && env.IS_SNAPSHOT == 'false'
+ run: |
+ ./gradlew publishAllPublicationsToLocalStagingRepository -Ptargets.full -PciVersion="$RELEASE_VERSION"
+ python3 .github/scripts/verify-publication.py build/staging-repo "$RELEASE_VERSION" --full
+ bash .github/scripts/verify-consumer.sh "$RELEASE_VERSION"
+ - name: Stage and verify GitHub RC assets
+ if: env.IS_TEST_RELEASE == 'true'
+ run: |
+ ./gradlew githubReleaseAssets -Ptargets.full -PciVersion="$RELEASE_VERSION"
+ python3 .github/scripts/verify-publication.py build/github-release-assets "$RELEASE_VERSION" --full --github
+ bash .github/scripts/verify-consumer.sh "$RELEASE_VERSION" "$PWD/build/github-release-assets"
+ - name: Upload validated RC assets for inspection
+ if: env.IS_TEST_RELEASE == 'true'
uses: actions/upload-artifact@v7
with:
- name: release-test-reports
- path: |
- **/build/reports/tests/
- **/build/test-results/
- if-no-files-found: ignore
+ name: test-release-assets
+ path: build/github-release-assets/
+ if-no-files-found: error
+ - name: Publish RC to GitHub only
+ if: env.IS_TEST_RELEASE == 'true'
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: ./gradlew publishToGitHubRelease -Ptargets.full -PciVersion="$RELEASE_VERSION"
+ - name: Verify anonymous consumer from GitHub
+ if: env.IS_TEST_RELEASE == 'true'
+ run: >-
+ ./gradlew -p consumer run -Pconsumer.version="$RELEASE_VERSION"
+ -Pconsumer.githubRepository="https://github.com/$GITHUB_REPOSITORY/releases/download/$RELEASE_TAG"
- name: Publish to Maven Central (Portal)
- if: env.IS_SNAPSHOT == 'false' && env.DRY_RUN != 'true'
+ if: env.IS_SNAPSHOT == 'false' && env.IS_TEST_RELEASE != 'true'
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_PORTAL_USER }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
- run: ./gradlew publishToMavenCentral -PciVersion="$RELEASE_VERSION"
+ run: ./gradlew publishToMavenCentral -Ptargets.full -PciVersion="$RELEASE_VERSION"
- name: Publish to Sonatype Snapshots
- if: env.IS_SNAPSHOT == 'true' && env.DRY_RUN != 'true'
+ if: env.IS_SNAPSHOT == 'true'
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_PORTAL_USER }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
- run: ./gradlew publishAllPublicationsToSonatypeSnapshotsRepository -PciVersion="$RELEASE_VERSION"
+ run: ./gradlew publishAllPublicationsToSonatypeSnapshotsRepository -Ptargets.full -PciVersion="$RELEASE_VERSION"
- name: Create GitHub release
- if: github.ref_type == 'tag' && env.IS_SNAPSHOT == 'false' && env.DRY_RUN != 'true'
+ if: github.ref_type == 'tag' && env.IS_SNAPSHOT == 'false' && env.IS_TEST_RELEASE != 'true'
uses: softprops/action-gh-release@v3
with:
prerelease: ${{ needs.prepare.outputs.is_prerelease }}
diff --git a/.gitignore b/.gitignore
index 3ed6d5a..27d164e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,6 @@ out/
.specstory/
.aider*
*.local.md
+
+### Python CI helpers ###
+__pycache__/
diff --git a/README.md b/README.md
index ada402c..829d670 100644
--- a/README.md
+++ b/README.md
@@ -109,3 +109,50 @@ Here is what the text "any byte data" (13 bytes) looks like in each:
For even shorter strings, BaseRadix can use a large range of Unicode
characters.
+
+## Build validation
+
+`./gradlew check lintDocs koverXmlReport` checks JVM and Linux x64.
+JS, Wasm, WASI and other native targets are configured only with `-Ptargets.full`. JVM checks also
+run the packaged JAR on Java 17 and 25. CI stages the configured publications,
+checks their metadata and artifacts, and runs an independent Java consumer using
+both Gradle metadata and POM-only resolution.
+
+Normal pull-request and main builds use one Linux job. Tagged stable and RC releases
+enable `-Ptargets.full`, with focused macOS ARM64, iOS simulator and Windows checks.
+The build workflow's manual `fullTargets` input runs the same gate without publishing.
+Automatic snapshots publish all targets without repeating the full runtime gate.
+Releases rerun the full Linux gate at the actual release version, in parallel with
+the macOS/iOS and Windows jobs. Publication waits for every platform to pass and
+rechecks its staged artifacts and independent consumers before publishing.
+
+ABI references live in `api/` for normal checks and `api-full/` for full checks.
+When intentionally updating the API, run `./gradlew updateKotlinAbi` and
+`./gradlew updateKotlinAbi -Ptargets.full`. JavaScript and Wasm dependency lockfiles
+under `kotlin-js-store/` must be committed; use
+`./gradlew kotlinUpgradeYarnLock kotlinWasmUpgradeYarnLock -Ptargets.full` to update them.
+
+CI caches Gradle dependency/task outputs, the host-specific Native toolchain, and
+Gradle-encrypted configuration state separately. Configuration state is persisted
+only when the encryption secret is available. The local CI publication uses a
+stable version so source-only commits can reuse configuration state; the consumer
+always refreshes that local module, while external dependencies remain cached.
+
+### Tagged releases
+
+Push an explicit SemVer RC tag to run the full parallel release gate and publish
+all 12 publications with `SHA256SUMS` to a GitHub prerelease:
+
+```sh
+git tag v1.4.1-rc.1
+git push origin v1.4.1-rc.1
+```
+
+The version comes directly from the tag. Release workflows never create tags or
+increment RC numbers. RCs publish to GitHub only; stable tags such as `v1.4.1`
+publish to Maven Central and create a GitHub release after validation.
+There is no manual release dispatch.
+
+GitHub RC assets include JVM/native/web artifacts, sources, documentation and
+Gradle metadata. They can be consumed anonymously using the flat Ivy repository
+pattern in [the independent consumer](consumer/settings.gradle.kts).
diff --git a/api-full/kencode.api b/api-full/kencode.api
new file mode 100644
index 0000000..fd5f173
--- /dev/null
+++ b/api-full/kencode.api
@@ -0,0 +1,323 @@
+public abstract interface class com/eignex/kencode/Alphabet {
+ public abstract fun get (I)C
+ public abstract fun getSize ()I
+ public abstract fun indexOf (C)I
+}
+
+public final class com/eignex/kencode/Base36 : com/eignex/kencode/BaseRadix {
+ public static final field INSTANCE Lcom/eignex/kencode/Base36;
+}
+
+public final class com/eignex/kencode/Base62 : com/eignex/kencode/BaseRadix {
+ public static final field INSTANCE Lcom/eignex/kencode/Base62;
+}
+
+public class com/eignex/kencode/Base64 : com/eignex/kencode/ByteEncoding {
+ public static final field Default Lcom/eignex/kencode/Base64$Default;
+ public fun ([C)V
+ public fun decode (Ljava/lang/CharSequence;)[B
+ public fun encode ([BII)Ljava/lang/String;
+ public final fun getAlphabet ()[C
+}
+
+public final class com/eignex/kencode/Base64$Default : com/eignex/kencode/Base64 {
+}
+
+public final class com/eignex/kencode/Base64Kt {
+ public static final field BASE_64 Ljava/lang/String;
+ public static final field BASE_64_URL Ljava/lang/String;
+}
+
+public final class com/eignex/kencode/Base64Url : com/eignex/kencode/Base64 {
+ public static final field INSTANCE Lcom/eignex/kencode/Base64Url;
+}
+
+public class com/eignex/kencode/Base85 : com/eignex/kencode/ByteEncoding {
+ public static final field Default Lcom/eignex/kencode/Base85$Default;
+ public fun ([C)V
+ public fun decode (Ljava/lang/CharSequence;)[B
+ public fun encode ([BII)Ljava/lang/String;
+}
+
+public final class com/eignex/kencode/Base85$Default : com/eignex/kencode/Base85 {
+}
+
+public final class com/eignex/kencode/Base85Kt {
+ public static final field ASCII85 Ljava/lang/String;
+}
+
+public class com/eignex/kencode/BaseRadix : com/eignex/kencode/ByteEncoding {
+ public fun (Lcom/eignex/kencode/Alphabet;I)V
+ public synthetic fun (Lcom/eignex/kencode/Alphabet;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun (Ljava/lang/String;I)V
+ public synthetic fun (Ljava/lang/String;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun decode (Ljava/lang/CharSequence;)[B
+ public fun encode ([BII)Ljava/lang/String;
+ public final fun getBlockSize ()I
+}
+
+public final class com/eignex/kencode/BaseRadixKt {
+ public static final field BASE_62 Ljava/lang/String;
+}
+
+public abstract interface class com/eignex/kencode/ByteEncoding {
+ public abstract fun decode (Ljava/lang/CharSequence;)[B
+ public abstract fun encode ([BII)Ljava/lang/String;
+ public static synthetic fun encode$default (Lcom/eignex/kencode/ByteEncoding;[BIIILjava/lang/Object;)Ljava/lang/String;
+}
+
+public final class com/eignex/kencode/ByteEncoding$DefaultImpls {
+ public static synthetic fun encode$default (Lcom/eignex/kencode/ByteEncoding;[BIIILjava/lang/Object;)Ljava/lang/String;
+}
+
+public final class com/eignex/kencode/CharAlphabet : com/eignex/kencode/Alphabet {
+ public fun (Ljava/lang/String;)V
+ public fun get (I)C
+ public fun getSize ()I
+ public fun indexOf (C)I
+}
+
+public abstract interface class com/eignex/kencode/Checksum {
+ public abstract fun digest ([B)[B
+ public abstract fun getSize ()I
+}
+
+public final class com/eignex/kencode/CompactZeros : com/eignex/kencode/PayloadTransform {
+ public static final field INSTANCE Lcom/eignex/kencode/CompactZeros;
+ public fun decode ([B)[B
+ public fun encode ([B)[B
+}
+
+public abstract class com/eignex/kencode/Crc : com/eignex/kencode/Checksum {
+ public fun digest ([B)[B
+ public fun getSize ()I
+}
+
+public class com/eignex/kencode/Crc16 : com/eignex/kencode/Crc {
+ public static final field Default Lcom/eignex/kencode/Crc16$Default;
+ public fun ()V
+ public fun (IIZZI)V
+ public synthetic fun (IIZZIILkotlin/jvm/internal/DefaultConstructorMarker;)V
+}
+
+public final class com/eignex/kencode/Crc16$Default : com/eignex/kencode/Crc16 {
+}
+
+public class com/eignex/kencode/Crc32 : com/eignex/kencode/Crc {
+ public static final field Default Lcom/eignex/kencode/Crc32$Default;
+ public fun ()V
+ public fun (IIZZI)V
+ public synthetic fun (IIZZIILkotlin/jvm/internal/DefaultConstructorMarker;)V
+}
+
+public final class com/eignex/kencode/Crc32$Default : com/eignex/kencode/Crc32 {
+}
+
+public class com/eignex/kencode/Crc8 : com/eignex/kencode/Crc {
+ public static final field Default Lcom/eignex/kencode/Crc8$Default;
+ public fun ()V
+ public fun (IIZZI)V
+ public synthetic fun (IIZZIILkotlin/jvm/internal/DefaultConstructorMarker;)V
+}
+
+public final class com/eignex/kencode/Crc8$Default : com/eignex/kencode/Crc8 {
+}
+
+public final class com/eignex/kencode/EncodedConfiguration {
+ public fun ()V
+ public fun (Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;)V
+ public synthetic fun (Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()Lcom/eignex/kencode/ByteEncoding;
+ public final fun component2 ()Lcom/eignex/kencode/PayloadTransform;
+ public final fun component3 ()Lkotlinx/serialization/BinaryFormat;
+ public final fun copy (Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;)Lcom/eignex/kencode/EncodedConfiguration;
+ public static synthetic fun copy$default (Lcom/eignex/kencode/EncodedConfiguration;Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;ILjava/lang/Object;)Lcom/eignex/kencode/EncodedConfiguration;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getBinaryFormat ()Lkotlinx/serialization/BinaryFormat;
+ public final fun getCodec ()Lcom/eignex/kencode/ByteEncoding;
+ public final fun getTransform ()Lcom/eignex/kencode/PayloadTransform;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public class com/eignex/kencode/EncodedFormat : kotlinx/serialization/StringFormat {
+ public static final field Default Lcom/eignex/kencode/EncodedFormat$Default;
+ public fun (Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;)V
+ public synthetic fun (Lcom/eignex/kencode/ByteEncoding;Lcom/eignex/kencode/PayloadTransform;Lkotlinx/serialization/BinaryFormat;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun (Lcom/eignex/kencode/EncodedConfiguration;)V
+ public fun decodeFromString (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/String;)Ljava/lang/Object;
+ public fun encodeToString (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/String;
+ public final fun getConfiguration ()Lcom/eignex/kencode/EncodedConfiguration;
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class com/eignex/kencode/EncodedFormat$Default : com/eignex/kencode/EncodedFormat {
+}
+
+public final class com/eignex/kencode/EncodedFormatBuilder {
+ public fun ()V
+ public final fun getBinaryFormat ()Lkotlinx/serialization/BinaryFormat;
+ public final fun getChecksum ()Lcom/eignex/kencode/Checksum;
+ public final fun getCodec ()Lcom/eignex/kencode/ByteEncoding;
+ public final fun getTransform ()Lcom/eignex/kencode/PayloadTransform;
+ public final fun setBinaryFormat (Lkotlinx/serialization/BinaryFormat;)V
+ public final fun setChecksum (Lcom/eignex/kencode/Checksum;)V
+ public final fun setCodec (Lcom/eignex/kencode/ByteEncoding;)V
+ public final fun setTransform (Lcom/eignex/kencode/PayloadTransform;)V
+}
+
+public final class com/eignex/kencode/EncodedFormatKt {
+ public static final fun EncodedFormat (Lcom/eignex/kencode/EncodedFormat;Lkotlin/jvm/functions/Function1;)Lcom/eignex/kencode/EncodedFormat;
+ public static synthetic fun EncodedFormat$default (Lcom/eignex/kencode/EncodedFormat;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/eignex/kencode/EncodedFormat;
+}
+
+public final class com/eignex/kencode/IntPacking : java/lang/Enum {
+ public static final field DEFAULT Lcom/eignex/kencode/IntPacking;
+ public static final field FIXED Lcom/eignex/kencode/IntPacking;
+ public static final field SIGNED Lcom/eignex/kencode/IntPacking;
+ public static fun getEntries ()Lkotlin/enums/EnumEntries;
+ public static fun valueOf (Ljava/lang/String;)Lcom/eignex/kencode/IntPacking;
+ public static fun values ()[Lcom/eignex/kencode/IntPacking;
+}
+
+public final class com/eignex/kencode/PackedConfiguration {
+ public fun ()V
+ public fun (Lcom/eignex/kencode/IntPacking;)V
+ public synthetic fun (Lcom/eignex/kencode/IntPacking;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public final fun component1 ()Lcom/eignex/kencode/IntPacking;
+ public final fun copy (Lcom/eignex/kencode/IntPacking;)Lcom/eignex/kencode/PackedConfiguration;
+ public static synthetic fun copy$default (Lcom/eignex/kencode/PackedConfiguration;Lcom/eignex/kencode/IntPacking;ILjava/lang/Object;)Lcom/eignex/kencode/PackedConfiguration;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getDefaultEncoding ()Lcom/eignex/kencode/IntPacking;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class com/eignex/kencode/PackedDecoder : kotlinx/serialization/encoding/CompositeDecoder, kotlinx/serialization/encoding/Decoder {
+ public fun ([BLcom/eignex/kencode/PackedConfiguration;Lkotlinx/serialization/modules/SerializersModule;)V
+ public synthetic fun ([BLcom/eignex/kencode/PackedConfiguration;Lkotlinx/serialization/modules/SerializersModule;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder;
+ public fun decodeBoolean ()Z
+ public fun decodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+ public fun decodeByte ()B
+ public fun decodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)B
+ public fun decodeChar ()C
+ public fun decodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)C
+ public fun decodeCollectionSize (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public fun decodeDouble ()D
+ public fun decodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)D
+ public fun decodeElementIndex (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public fun decodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;)I
+ public fun decodeFloat ()F
+ public fun decodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)F
+ public fun decodeInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Decoder;
+ public fun decodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Decoder;
+ public fun decodeInt ()I
+ public fun decodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)I
+ public fun decodeLong ()J
+ public fun decodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)J
+ public fun decodeNotNullMark ()Z
+ public fun decodeNull ()Ljava/lang/Void;
+ public fun decodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun decodeNullableSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public fun decodeSequentially ()Z
+ public fun decodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object;
+ public fun decodeSerializableValue (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+ public fun decodeShort ()S
+ public fun decodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)S
+ public fun decodeString ()Ljava/lang/String;
+ public fun decodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;
+ public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class com/eignex/kencode/PackedEncoder : kotlinx/serialization/encoding/CompositeEncoder, kotlinx/serialization/encoding/Encoder {
+ public fun beginCollection (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder;
+ public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeEncoder;
+ public fun encodeBoolean (Z)V
+ public fun encodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IZ)V
+ public fun encodeByte (B)V
+ public fun encodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IB)V
+ public fun encodeChar (C)V
+ public fun encodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IC)V
+ public fun encodeDouble (D)V
+ public fun encodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ID)V
+ public fun encodeEnum (Lkotlinx/serialization/descriptors/SerialDescriptor;I)V
+ public fun encodeFloat (F)V
+ public fun encodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IF)V
+ public fun encodeInline (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/Encoder;
+ public fun encodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Encoder;
+ public fun encodeInt (I)V
+ public fun encodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;II)V
+ public fun encodeLong (J)V
+ public fun encodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IJ)V
+ public fun encodeNotNullMark ()V
+ public fun encodeNull ()V
+ public fun encodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeNullableSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeSerializableValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+ public fun encodeShort (S)V
+ public fun encodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IS)V
+ public fun encodeString (Ljava/lang/String;)V
+ public fun encodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V
+ public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ public fun shouldEncodeElementDefault (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
+}
+
+public class com/eignex/kencode/PackedFormat : kotlinx/serialization/BinaryFormat {
+ public static final field Default Lcom/eignex/kencode/PackedFormat$Default;
+ public fun ()V
+ public fun (Lcom/eignex/kencode/PackedConfiguration;Lkotlinx/serialization/modules/SerializersModule;)V
+ public synthetic fun (Lcom/eignex/kencode/PackedConfiguration;Lkotlinx/serialization/modules/SerializersModule;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun decodeFromByteArray (Lkotlinx/serialization/DeserializationStrategy;[B)Ljava/lang/Object;
+ public fun encodeToByteArray (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)[B
+ public final fun getConfiguration ()Lcom/eignex/kencode/PackedConfiguration;
+ public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+}
+
+public final class com/eignex/kencode/PackedFormat$Default : com/eignex/kencode/PackedFormat {
+}
+
+public final class com/eignex/kencode/PackedFormatBuilder {
+ public fun ()V
+ public final fun getDefaultEncoding ()Lcom/eignex/kencode/IntPacking;
+ public final fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+ public final fun setDefaultEncoding (Lcom/eignex/kencode/IntPacking;)V
+ public final fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V
+}
+
+public final class com/eignex/kencode/PackedFormatKt {
+ public static final fun PackedFormat (Lcom/eignex/kencode/PackedFormat;Lkotlin/jvm/functions/Function1;)Lcom/eignex/kencode/PackedFormat;
+ public static synthetic fun PackedFormat$default (Lcom/eignex/kencode/PackedFormat;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/eignex/kencode/PackedFormat;
+}
+
+public abstract interface annotation class com/eignex/kencode/PackedType : java/lang/annotation/Annotation {
+ public abstract fun type ()Lcom/eignex/kencode/IntPacking;
+}
+
+public final synthetic class com/eignex/kencode/PackedType$Impl : com/eignex/kencode/PackedType {
+ public fun (Lcom/eignex/kencode/IntPacking;)V
+ public final synthetic fun type ()Lcom/eignex/kencode/IntPacking;
+}
+
+public abstract interface class com/eignex/kencode/PayloadTransform {
+ public abstract fun decode ([B)[B
+ public abstract fun encode ([B)[B
+}
+
+public final class com/eignex/kencode/PayloadTransformKt {
+ public static final fun asTransform (Lcom/eignex/kencode/Checksum;)Lcom/eignex/kencode/PayloadTransform;
+ public static final fun then (Lcom/eignex/kencode/PayloadTransform;Lcom/eignex/kencode/PayloadTransform;)Lcom/eignex/kencode/PayloadTransform;
+}
+
+public final class com/eignex/kencode/UnicodeRangeAlphabet : com/eignex/kencode/Alphabet {
+ public fun ()V
+ public fun (II)V
+ public synthetic fun (IIILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public fun get (I)C
+ public fun getSize ()I
+ public fun indexOf (C)I
+}
+
diff --git a/api-full/kencode.klib.api b/api-full/kencode.klib.api
new file mode 100644
index 0000000..da1e16c
--- /dev/null
+++ b/api-full/kencode.klib.api
@@ -0,0 +1,316 @@
+// Klib ABI Dump
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, mingwX64, wasmJs, wasmWasi]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name:
+open annotation class com.eignex.kencode/PackedType : kotlin/Annotation { // com.eignex.kencode/PackedType|null[0]
+ constructor (com.eignex.kencode/IntPacking) // com.eignex.kencode/PackedType.|(com.eignex.kencode.IntPacking){}[0]
+
+ final val type // com.eignex.kencode/PackedType.type|{}type[0]
+ final fun (): com.eignex.kencode/IntPacking // com.eignex.kencode/PackedType.type.|(){}[0]
+}
+
+final enum class com.eignex.kencode/IntPacking : kotlin/Enum { // com.eignex.kencode/IntPacking|null[0]
+ enum entry DEFAULT // com.eignex.kencode/IntPacking.DEFAULT|null[0]
+ enum entry FIXED // com.eignex.kencode/IntPacking.FIXED|null[0]
+ enum entry SIGNED // com.eignex.kencode/IntPacking.SIGNED|null[0]
+
+ final val entries // com.eignex.kencode/IntPacking.entries|#static{}entries[0]
+ final fun (): kotlin.enums/EnumEntries // com.eignex.kencode/IntPacking.entries.|#static(){}[0]
+
+ final fun valueOf(kotlin/String): com.eignex.kencode/IntPacking // com.eignex.kencode/IntPacking.valueOf|valueOf#static(kotlin.String){}[0]
+ final fun values(): kotlin/Array // com.eignex.kencode/IntPacking.values|values#static(){}[0]
+}
+
+abstract interface com.eignex.kencode/Alphabet { // com.eignex.kencode/Alphabet|null[0]
+ abstract val size // com.eignex.kencode/Alphabet.size|{}size[0]
+ abstract fun (): kotlin/Int // com.eignex.kencode/Alphabet.size.|(){}[0]
+
+ abstract fun get(kotlin/Int): kotlin/Char // com.eignex.kencode/Alphabet.get|get(kotlin.Int){}[0]
+ abstract fun indexOf(kotlin/Char): kotlin/Int // com.eignex.kencode/Alphabet.indexOf|indexOf(kotlin.Char){}[0]
+}
+
+abstract interface com.eignex.kencode/ByteEncoding { // com.eignex.kencode/ByteEncoding|null[0]
+ abstract fun decode(kotlin/CharSequence): kotlin/ByteArray // com.eignex.kencode/ByteEncoding.decode|decode(kotlin.CharSequence){}[0]
+ abstract fun encode(kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...): kotlin/String // com.eignex.kencode/ByteEncoding.encode|encode(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0]
+}
+
+abstract interface com.eignex.kencode/Checksum { // com.eignex.kencode/Checksum|null[0]
+ abstract val size // com.eignex.kencode/Checksum.size|{}size[0]
+ abstract fun (): kotlin/Int // com.eignex.kencode/Checksum.size.|(){}[0]
+
+ abstract fun digest(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/Checksum.digest|digest(kotlin.ByteArray){}[0]
+}
+
+abstract interface com.eignex.kencode/PayloadTransform { // com.eignex.kencode/PayloadTransform|null[0]
+ abstract fun decode(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/PayloadTransform.decode|decode(kotlin.ByteArray){}[0]
+ abstract fun encode(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/PayloadTransform.encode|encode(kotlin.ByteArray){}[0]
+}
+
+abstract class com.eignex.kencode/Crc : com.eignex.kencode/Checksum { // com.eignex.kencode/Crc|null[0]
+ open val size // com.eignex.kencode/Crc.size|{}size[0]
+ open fun (): kotlin/Int // com.eignex.kencode/Crc.size.|(){}[0]
+
+ open fun digest(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/Crc.digest|digest(kotlin.ByteArray){}[0]
+}
+
+final class com.eignex.kencode/CharAlphabet : com.eignex.kencode/Alphabet { // com.eignex.kencode/CharAlphabet|null[0]
+ constructor (kotlin/String) // com.eignex.kencode/CharAlphabet.|(kotlin.String){}[0]
+
+ final val size // com.eignex.kencode/CharAlphabet.size|{}size[0]
+ final fun (): kotlin/Int // com.eignex.kencode/CharAlphabet.size.|(){}[0]
+
+ final fun get(kotlin/Int): kotlin/Char // com.eignex.kencode/CharAlphabet.get|get(kotlin.Int){}[0]
+ final fun indexOf(kotlin/Char): kotlin/Int // com.eignex.kencode/CharAlphabet.indexOf|indexOf(kotlin.Char){}[0]
+}
+
+final class com.eignex.kencode/EncodedConfiguration { // com.eignex.kencode/EncodedConfiguration|null[0]
+ constructor (com.eignex.kencode/ByteEncoding = ..., com.eignex.kencode/PayloadTransform? = ..., kotlinx.serialization/BinaryFormat = ...) // com.eignex.kencode/EncodedConfiguration.|(com.eignex.kencode.ByteEncoding;com.eignex.kencode.PayloadTransform?;kotlinx.serialization.BinaryFormat){}[0]
+
+ final val binaryFormat // com.eignex.kencode/EncodedConfiguration.binaryFormat|{}binaryFormat[0]
+ final fun (): kotlinx.serialization/BinaryFormat // com.eignex.kencode/EncodedConfiguration.binaryFormat.|(){}[0]
+ final val codec // com.eignex.kencode/EncodedConfiguration.codec|{}codec[0]
+ final fun (): com.eignex.kencode/ByteEncoding // com.eignex.kencode/EncodedConfiguration.codec.|(){}[0]
+ final val transform // com.eignex.kencode/EncodedConfiguration.transform|{}transform[0]
+ final fun (): com.eignex.kencode/PayloadTransform? // com.eignex.kencode/EncodedConfiguration.transform.|(){}[0]
+
+ final fun component1(): com.eignex.kencode/ByteEncoding // com.eignex.kencode/EncodedConfiguration.component1|component1(){}[0]
+ final fun component2(): com.eignex.kencode/PayloadTransform? // com.eignex.kencode/EncodedConfiguration.component2|component2(){}[0]
+ final fun component3(): kotlinx.serialization/BinaryFormat // com.eignex.kencode/EncodedConfiguration.component3|component3(){}[0]
+ final fun copy(com.eignex.kencode/ByteEncoding = ..., com.eignex.kencode/PayloadTransform? = ..., kotlinx.serialization/BinaryFormat = ...): com.eignex.kencode/EncodedConfiguration // com.eignex.kencode/EncodedConfiguration.copy|copy(com.eignex.kencode.ByteEncoding;com.eignex.kencode.PayloadTransform?;kotlinx.serialization.BinaryFormat){}[0]
+ final fun equals(kotlin/Any?): kotlin/Boolean // com.eignex.kencode/EncodedConfiguration.equals|equals(kotlin.Any?){}[0]
+ final fun hashCode(): kotlin/Int // com.eignex.kencode/EncodedConfiguration.hashCode|hashCode(){}[0]
+ final fun toString(): kotlin/String // com.eignex.kencode/EncodedConfiguration.toString|toString(){}[0]
+}
+
+final class com.eignex.kencode/EncodedFormatBuilder { // com.eignex.kencode/EncodedFormatBuilder|null[0]
+ constructor () // com.eignex.kencode/EncodedFormatBuilder.|(){}[0]
+
+ final var binaryFormat // com.eignex.kencode/EncodedFormatBuilder.binaryFormat|{}binaryFormat[0]
+ final fun (): kotlinx.serialization/BinaryFormat // com.eignex.kencode/EncodedFormatBuilder.binaryFormat.|(){}[0]
+ final fun (kotlinx.serialization/BinaryFormat) // com.eignex.kencode/EncodedFormatBuilder.binaryFormat.|(kotlinx.serialization.BinaryFormat){}[0]
+ final var checksum // com.eignex.kencode/EncodedFormatBuilder.checksum|{}checksum[0]
+ final fun (): com.eignex.kencode/Checksum? // com.eignex.kencode/EncodedFormatBuilder.checksum.|(){}[0]
+ final fun (com.eignex.kencode/Checksum?) // com.eignex.kencode/EncodedFormatBuilder.checksum.|(com.eignex.kencode.Checksum?){}[0]
+ final var codec // com.eignex.kencode/EncodedFormatBuilder.codec|{}codec[0]
+ final fun (): com.eignex.kencode/ByteEncoding // com.eignex.kencode/EncodedFormatBuilder.codec.|(){}[0]
+ final fun (com.eignex.kencode/ByteEncoding) // com.eignex.kencode/EncodedFormatBuilder.codec.|(com.eignex.kencode.ByteEncoding){}[0]
+ final var transform // com.eignex.kencode/EncodedFormatBuilder.transform|{}transform[0]
+ final fun (): com.eignex.kencode/PayloadTransform? // com.eignex.kencode/EncodedFormatBuilder.transform.|(){}[0]
+ final fun (com.eignex.kencode/PayloadTransform?) // com.eignex.kencode/EncodedFormatBuilder.transform.|(com.eignex.kencode.PayloadTransform?){}[0]
+}
+
+final class com.eignex.kencode/PackedConfiguration { // com.eignex.kencode/PackedConfiguration|null[0]
+ constructor (com.eignex.kencode/IntPacking = ...) // com.eignex.kencode/PackedConfiguration.|(com.eignex.kencode.IntPacking){}[0]
+
+ final val defaultEncoding // com.eignex.kencode/PackedConfiguration.defaultEncoding|{}defaultEncoding[0]
+ final fun (): com.eignex.kencode/IntPacking // com.eignex.kencode/PackedConfiguration.defaultEncoding.|(){}[0]
+
+ final fun component1(): com.eignex.kencode/IntPacking // com.eignex.kencode/PackedConfiguration.component1|component1(){}[0]
+ final fun copy(com.eignex.kencode/IntPacking = ...): com.eignex.kencode/PackedConfiguration // com.eignex.kencode/PackedConfiguration.copy|copy(com.eignex.kencode.IntPacking){}[0]
+ final fun equals(kotlin/Any?): kotlin/Boolean // com.eignex.kencode/PackedConfiguration.equals|equals(kotlin.Any?){}[0]
+ final fun hashCode(): kotlin/Int // com.eignex.kencode/PackedConfiguration.hashCode|hashCode(){}[0]
+ final fun toString(): kotlin/String // com.eignex.kencode/PackedConfiguration.toString|toString(){}[0]
+}
+
+final class com.eignex.kencode/PackedDecoder : kotlinx.serialization.encoding/CompositeDecoder, kotlinx.serialization.encoding/Decoder { // com.eignex.kencode/PackedDecoder|null[0]
+ constructor (kotlin/ByteArray, com.eignex.kencode/PackedConfiguration = ..., kotlinx.serialization.modules/SerializersModule = ...) // com.eignex.kencode/PackedDecoder.|(kotlin.ByteArray;com.eignex.kencode.PackedConfiguration;kotlinx.serialization.modules.SerializersModule){}[0]
+
+ final val serializersModule // com.eignex.kencode/PackedDecoder.serializersModule|{}serializersModule[0]
+ final fun (): kotlinx.serialization.modules/SerializersModule // com.eignex.kencode/PackedDecoder.serializersModule.|(){}[0]
+
+ final fun <#A1: kotlin/Any> decodeNullableSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/DeserializationStrategy<#A1?>, #A1?): #A1? // com.eignex.kencode/PackedDecoder.decodeNullableSerializableElement|decodeNullableSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.DeserializationStrategy<0:0?>;0:0?){0§}[0]
+ final fun <#A1: kotlin/Any?> decodeSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/DeserializationStrategy<#A1>, #A1?): #A1 // com.eignex.kencode/PackedDecoder.decodeSerializableElement|decodeSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.DeserializationStrategy<0:0>;0:0?){0§}[0]
+ final fun <#A1: kotlin/Any?> decodeSerializableValue(kotlinx.serialization/DeserializationStrategy<#A1>): #A1 // com.eignex.kencode/PackedDecoder.decodeSerializableValue|decodeSerializableValue(kotlinx.serialization.DeserializationStrategy<0:0>){0§}[0]
+ final fun beginStructure(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/CompositeDecoder // com.eignex.kencode/PackedDecoder.beginStructure|beginStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+ final fun decodeBoolean(): kotlin/Boolean // com.eignex.kencode/PackedDecoder.decodeBoolean|decodeBoolean(){}[0]
+ final fun decodeBooleanElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Boolean // com.eignex.kencode/PackedDecoder.decodeBooleanElement|decodeBooleanElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun decodeByte(): kotlin/Byte // com.eignex.kencode/PackedDecoder.decodeByte|decodeByte(){}[0]
+ final fun decodeByteElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Byte // com.eignex.kencode/PackedDecoder.decodeByteElement|decodeByteElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun decodeChar(): kotlin/Char // com.eignex.kencode/PackedDecoder.decodeChar|decodeChar(){}[0]
+ final fun decodeCharElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Char // com.eignex.kencode/PackedDecoder.decodeCharElement|decodeCharElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun decodeCollectionSize(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // com.eignex.kencode/PackedDecoder.decodeCollectionSize|decodeCollectionSize(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+ final fun decodeDouble(): kotlin/Double // com.eignex.kencode/PackedDecoder.decodeDouble|decodeDouble(){}[0]
+ final fun decodeDoubleElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Double // com.eignex.kencode/PackedDecoder.decodeDoubleElement|decodeDoubleElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun decodeElementIndex(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // com.eignex.kencode/PackedDecoder.decodeElementIndex|decodeElementIndex(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+ final fun decodeEnum(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // com.eignex.kencode/PackedDecoder.decodeEnum|decodeEnum(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+ final fun decodeFloat(): kotlin/Float // com.eignex.kencode/PackedDecoder.decodeFloat|decodeFloat(){}[0]
+ final fun decodeFloatElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Float // com.eignex.kencode/PackedDecoder.decodeFloatElement|decodeFloatElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun decodeInline(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Decoder // com.eignex.kencode/PackedDecoder.decodeInline|decodeInline(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+ final fun decodeInlineElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/Decoder // com.eignex.kencode/PackedDecoder.decodeInlineElement|decodeInlineElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun decodeInt(): kotlin/Int // com.eignex.kencode/PackedDecoder.decodeInt|decodeInt(){}[0]
+ final fun decodeIntElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Int // com.eignex.kencode/PackedDecoder.decodeIntElement|decodeIntElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun decodeLong(): kotlin/Long // com.eignex.kencode/PackedDecoder.decodeLong|decodeLong(){}[0]
+ final fun decodeLongElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Long // com.eignex.kencode/PackedDecoder.decodeLongElement|decodeLongElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun decodeNotNullMark(): kotlin/Boolean // com.eignex.kencode/PackedDecoder.decodeNotNullMark|decodeNotNullMark(){}[0]
+ final fun decodeNull(): kotlin/Nothing? // com.eignex.kencode/PackedDecoder.decodeNull|decodeNull(){}[0]
+ final fun decodeSequentially(): kotlin/Boolean // com.eignex.kencode/PackedDecoder.decodeSequentially|decodeSequentially(){}[0]
+ final fun decodeShort(): kotlin/Short // com.eignex.kencode/PackedDecoder.decodeShort|decodeShort(){}[0]
+ final fun decodeShortElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Short // com.eignex.kencode/PackedDecoder.decodeShortElement|decodeShortElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun decodeString(): kotlin/String // com.eignex.kencode/PackedDecoder.decodeString|decodeString(){}[0]
+ final fun decodeStringElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/String // com.eignex.kencode/PackedDecoder.decodeStringElement|decodeStringElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun endStructure(kotlinx.serialization.descriptors/SerialDescriptor) // com.eignex.kencode/PackedDecoder.endStructure|endStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+}
+
+final class com.eignex.kencode/PackedEncoder : kotlinx.serialization.encoding/CompositeEncoder, kotlinx.serialization.encoding/Encoder { // com.eignex.kencode/PackedEncoder|null[0]
+ final val serializersModule // com.eignex.kencode/PackedEncoder.serializersModule|{}serializersModule[0]
+ final fun (): kotlinx.serialization.modules/SerializersModule // com.eignex.kencode/PackedEncoder.serializersModule.|(){}[0]
+
+ final fun <#A1: kotlin/Any> encodeNullableSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/SerializationStrategy<#A1>, #A1?) // com.eignex.kencode/PackedEncoder.encodeNullableSerializableElement|encodeNullableSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.SerializationStrategy<0:0>;0:0?){0§}[0]
+ final fun <#A1: kotlin/Any?> encodeSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/SerializationStrategy<#A1>, #A1) // com.eignex.kencode/PackedEncoder.encodeSerializableElement|encodeSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§}[0]
+ final fun <#A1: kotlin/Any?> encodeSerializableValue(kotlinx.serialization/SerializationStrategy<#A1>, #A1) // com.eignex.kencode/PackedEncoder.encodeSerializableValue|encodeSerializableValue(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§}[0]
+ final fun beginCollection(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/CompositeEncoder // com.eignex.kencode/PackedEncoder.beginCollection|beginCollection(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun beginStructure(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/CompositeEncoder // com.eignex.kencode/PackedEncoder.beginStructure|beginStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+ final fun encodeBoolean(kotlin/Boolean) // com.eignex.kencode/PackedEncoder.encodeBoolean|encodeBoolean(kotlin.Boolean){}[0]
+ final fun encodeBooleanElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Boolean) // com.eignex.kencode/PackedEncoder.encodeBooleanElement|encodeBooleanElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Boolean){}[0]
+ final fun encodeByte(kotlin/Byte) // com.eignex.kencode/PackedEncoder.encodeByte|encodeByte(kotlin.Byte){}[0]
+ final fun encodeByteElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Byte) // com.eignex.kencode/PackedEncoder.encodeByteElement|encodeByteElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Byte){}[0]
+ final fun encodeChar(kotlin/Char) // com.eignex.kencode/PackedEncoder.encodeChar|encodeChar(kotlin.Char){}[0]
+ final fun encodeCharElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Char) // com.eignex.kencode/PackedEncoder.encodeCharElement|encodeCharElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Char){}[0]
+ final fun encodeDouble(kotlin/Double) // com.eignex.kencode/PackedEncoder.encodeDouble|encodeDouble(kotlin.Double){}[0]
+ final fun encodeDoubleElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Double) // com.eignex.kencode/PackedEncoder.encodeDoubleElement|encodeDoubleElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Double){}[0]
+ final fun encodeEnum(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int) // com.eignex.kencode/PackedEncoder.encodeEnum|encodeEnum(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun encodeFloat(kotlin/Float) // com.eignex.kencode/PackedEncoder.encodeFloat|encodeFloat(kotlin.Float){}[0]
+ final fun encodeFloatElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Float) // com.eignex.kencode/PackedEncoder.encodeFloatElement|encodeFloatElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Float){}[0]
+ final fun encodeInline(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Encoder // com.eignex.kencode/PackedEncoder.encodeInline|encodeInline(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+ final fun encodeInlineElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/Encoder // com.eignex.kencode/PackedEncoder.encodeInlineElement|encodeInlineElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+ final fun encodeInt(kotlin/Int) // com.eignex.kencode/PackedEncoder.encodeInt|encodeInt(kotlin.Int){}[0]
+ final fun encodeIntElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Int) // com.eignex.kencode/PackedEncoder.encodeIntElement|encodeIntElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Int){}[0]
+ final fun encodeLong(kotlin/Long) // com.eignex.kencode/PackedEncoder.encodeLong|encodeLong(kotlin.Long){}[0]
+ final fun encodeLongElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Long) // com.eignex.kencode/PackedEncoder.encodeLongElement|encodeLongElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Long){}[0]
+ final fun encodeNotNullMark() // com.eignex.kencode/PackedEncoder.encodeNotNullMark|encodeNotNullMark(){}[0]
+ final fun encodeNull() // com.eignex.kencode/PackedEncoder.encodeNull|encodeNull(){}[0]
+ final fun encodeShort(kotlin/Short) // com.eignex.kencode/PackedEncoder.encodeShort|encodeShort(kotlin.Short){}[0]
+ final fun encodeShortElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Short) // com.eignex.kencode/PackedEncoder.encodeShortElement|encodeShortElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Short){}[0]
+ final fun encodeString(kotlin/String) // com.eignex.kencode/PackedEncoder.encodeString|encodeString(kotlin.String){}[0]
+ final fun encodeStringElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/String) // com.eignex.kencode/PackedEncoder.encodeStringElement|encodeStringElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.String){}[0]
+ final fun endStructure(kotlinx.serialization.descriptors/SerialDescriptor) // com.eignex.kencode/PackedEncoder.endStructure|endStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+}
+
+final class com.eignex.kencode/PackedFormatBuilder { // com.eignex.kencode/PackedFormatBuilder|null[0]
+ constructor () // com.eignex.kencode/PackedFormatBuilder.|(){}[0]
+
+ final var defaultEncoding // com.eignex.kencode/PackedFormatBuilder.defaultEncoding|{}defaultEncoding[0]
+ final fun (): com.eignex.kencode/IntPacking // com.eignex.kencode/PackedFormatBuilder.defaultEncoding.|(){}[0]
+ final fun (com.eignex.kencode/IntPacking) // com.eignex.kencode/PackedFormatBuilder.defaultEncoding.|(com.eignex.kencode.IntPacking){}[0]
+ final var serializersModule // com.eignex.kencode/PackedFormatBuilder.serializersModule|{}serializersModule[0]
+ final fun (): kotlinx.serialization.modules/SerializersModule // com.eignex.kencode/PackedFormatBuilder.serializersModule.|(){}[0]
+ final fun (kotlinx.serialization.modules/SerializersModule) // com.eignex.kencode/PackedFormatBuilder.serializersModule.|(kotlinx.serialization.modules.SerializersModule){}[0]
+}
+
+final class com.eignex.kencode/UnicodeRangeAlphabet : com.eignex.kencode/Alphabet { // com.eignex.kencode/UnicodeRangeAlphabet|null[0]
+ constructor (kotlin/Int = ..., kotlin/Int = ...) // com.eignex.kencode/UnicodeRangeAlphabet.|(kotlin.Int;kotlin.Int){}[0]
+
+ final val size // com.eignex.kencode/UnicodeRangeAlphabet.size|{}size[0]
+ final fun (): kotlin/Int // com.eignex.kencode/UnicodeRangeAlphabet.size.|(){}[0]
+
+ final fun get(kotlin/Int): kotlin/Char // com.eignex.kencode/UnicodeRangeAlphabet.get|get(kotlin.Int){}[0]
+ final fun indexOf(kotlin/Char): kotlin/Int // com.eignex.kencode/UnicodeRangeAlphabet.indexOf|indexOf(kotlin.Char){}[0]
+}
+
+open class com.eignex.kencode/Base64 : com.eignex.kencode/ByteEncoding { // com.eignex.kencode/Base64|null[0]
+ constructor (kotlin/CharArray) // com.eignex.kencode/Base64.|(kotlin.CharArray){}[0]
+
+ final val alphabet // com.eignex.kencode/Base64.alphabet|{}alphabet[0]
+ final fun (): kotlin/CharArray // com.eignex.kencode/Base64.alphabet.|(){}[0]
+
+ open fun decode(kotlin/CharSequence): kotlin/ByteArray // com.eignex.kencode/Base64.decode|decode(kotlin.CharSequence){}[0]
+ open fun encode(kotlin/ByteArray, kotlin/Int, kotlin/Int): kotlin/String // com.eignex.kencode/Base64.encode|encode(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0]
+
+ final object Default : com.eignex.kencode/Base64 // com.eignex.kencode/Base64.Default|null[0]
+}
+
+open class com.eignex.kencode/Base85 : com.eignex.kencode/ByteEncoding { // com.eignex.kencode/Base85|null[0]
+ constructor (kotlin/CharArray) // com.eignex.kencode/Base85.|(kotlin.CharArray){}[0]
+
+ open fun decode(kotlin/CharSequence): kotlin/ByteArray // com.eignex.kencode/Base85.decode|decode(kotlin.CharSequence){}[0]
+ open fun encode(kotlin/ByteArray, kotlin/Int, kotlin/Int): kotlin/String // com.eignex.kencode/Base85.encode|encode(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0]
+
+ final object Default : com.eignex.kencode/Base85 // com.eignex.kencode/Base85.Default|null[0]
+}
+
+open class com.eignex.kencode/BaseRadix : com.eignex.kencode/ByteEncoding { // com.eignex.kencode/BaseRadix|null[0]
+ constructor (com.eignex.kencode/Alphabet, kotlin/Int = ...) // com.eignex.kencode/BaseRadix.|(com.eignex.kencode.Alphabet;kotlin.Int){}[0]
+ constructor (kotlin/String, kotlin/Int = ...) // com.eignex.kencode/BaseRadix.|(kotlin.String;kotlin.Int){}[0]
+
+ final val blockSize // com.eignex.kencode/BaseRadix.blockSize|{}blockSize[0]
+ final fun (): kotlin/Int // com.eignex.kencode/BaseRadix.blockSize.|(){}[0]
+
+ open fun decode(kotlin/CharSequence): kotlin/ByteArray // com.eignex.kencode/BaseRadix.decode|decode(kotlin.CharSequence){}[0]
+ open fun encode(kotlin/ByteArray, kotlin/Int, kotlin/Int): kotlin/String // com.eignex.kencode/BaseRadix.encode|encode(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0]
+}
+
+open class com.eignex.kencode/Crc16 : com.eignex.kencode/Crc { // com.eignex.kencode/Crc16|null[0]
+ constructor (kotlin/Int = ..., kotlin/Int = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Int = ...) // com.eignex.kencode/Crc16.|(kotlin.Int;kotlin.Int;kotlin.Boolean;kotlin.Boolean;kotlin.Int){}[0]
+
+ final object Default : com.eignex.kencode/Crc16 // com.eignex.kencode/Crc16.Default|null[0]
+}
+
+open class com.eignex.kencode/Crc32 : com.eignex.kencode/Crc { // com.eignex.kencode/Crc32|null[0]
+ constructor (kotlin/Int = ..., kotlin/Int = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Int = ...) // com.eignex.kencode/Crc32.|(kotlin.Int;kotlin.Int;kotlin.Boolean;kotlin.Boolean;kotlin.Int){}[0]
+
+ final object Default : com.eignex.kencode/Crc32 // com.eignex.kencode/Crc32.Default|null[0]
+}
+
+open class com.eignex.kencode/Crc8 : com.eignex.kencode/Crc { // com.eignex.kencode/Crc8|null[0]
+ constructor (kotlin/Int = ..., kotlin/Int = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Int = ...) // com.eignex.kencode/Crc8.|(kotlin.Int;kotlin.Int;kotlin.Boolean;kotlin.Boolean;kotlin.Int){}[0]
+
+ final object Default : com.eignex.kencode/Crc8 // com.eignex.kencode/Crc8.Default|null[0]
+}
+
+open class com.eignex.kencode/EncodedFormat : kotlinx.serialization/StringFormat { // com.eignex.kencode/EncodedFormat|null[0]
+ constructor (com.eignex.kencode/ByteEncoding = ..., com.eignex.kencode/PayloadTransform? = ..., kotlinx.serialization/BinaryFormat = ...) // com.eignex.kencode/EncodedFormat.|(com.eignex.kencode.ByteEncoding;com.eignex.kencode.PayloadTransform?;kotlinx.serialization.BinaryFormat){}[0]
+ constructor (com.eignex.kencode/EncodedConfiguration) // com.eignex.kencode/EncodedFormat.|(com.eignex.kencode.EncodedConfiguration){}[0]
+
+ final val configuration // com.eignex.kencode/EncodedFormat.configuration|{}configuration[0]
+ final fun (): com.eignex.kencode/EncodedConfiguration // com.eignex.kencode/EncodedFormat.configuration.|(){}[0]
+ open val serializersModule // com.eignex.kencode/EncodedFormat.serializersModule|{}serializersModule[0]
+ open fun (): kotlinx.serialization.modules/SerializersModule // com.eignex.kencode/EncodedFormat.serializersModule.|(){}[0]
+
+ open fun <#A1: kotlin/Any?> decodeFromString(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin/String): #A1 // com.eignex.kencode/EncodedFormat.decodeFromString|decodeFromString(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.String){0§}[0]
+ open fun <#A1: kotlin/Any?> encodeToString(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin/String // com.eignex.kencode/EncodedFormat.encodeToString|encodeToString(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§}[0]
+
+ final object Default : com.eignex.kencode/EncodedFormat // com.eignex.kencode/EncodedFormat.Default|null[0]
+}
+
+open class com.eignex.kencode/PackedFormat : kotlinx.serialization/BinaryFormat { // com.eignex.kencode/PackedFormat|null[0]
+ constructor (com.eignex.kencode/PackedConfiguration = ..., kotlinx.serialization.modules/SerializersModule = ...) // com.eignex.kencode/PackedFormat.|(com.eignex.kencode.PackedConfiguration;kotlinx.serialization.modules.SerializersModule){}[0]
+
+ final val configuration // com.eignex.kencode/PackedFormat.configuration|{}configuration[0]
+ final fun (): com.eignex.kencode/PackedConfiguration // com.eignex.kencode/PackedFormat.configuration.|(){}[0]
+ open val serializersModule // com.eignex.kencode/PackedFormat.serializersModule|{}serializersModule[0]
+ open fun (): kotlinx.serialization.modules/SerializersModule // com.eignex.kencode/PackedFormat.serializersModule.|(){}[0]
+
+ open fun <#A1: kotlin/Any?> decodeFromByteArray(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin/ByteArray): #A1 // com.eignex.kencode/PackedFormat.decodeFromByteArray|decodeFromByteArray(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.ByteArray){0§}[0]
+ open fun <#A1: kotlin/Any?> encodeToByteArray(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin/ByteArray // com.eignex.kencode/PackedFormat.encodeToByteArray|encodeToByteArray(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§}[0]
+
+ final object Default : com.eignex.kencode/PackedFormat // com.eignex.kencode/PackedFormat.Default|null[0]
+}
+
+final object com.eignex.kencode/Base36 : com.eignex.kencode/BaseRadix // com.eignex.kencode/Base36|null[0]
+
+final object com.eignex.kencode/Base62 : com.eignex.kencode/BaseRadix // com.eignex.kencode/Base62|null[0]
+
+final object com.eignex.kencode/Base64Url : com.eignex.kencode/Base64 // com.eignex.kencode/Base64Url|null[0]
+
+final object com.eignex.kencode/CompactZeros : com.eignex.kencode/PayloadTransform { // com.eignex.kencode/CompactZeros|null[0]
+ final fun decode(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/CompactZeros.decode|decode(kotlin.ByteArray){}[0]
+ final fun encode(kotlin/ByteArray): kotlin/ByteArray // com.eignex.kencode/CompactZeros.encode|encode(kotlin.ByteArray){}[0]
+}
+
+final const val com.eignex.kencode/ASCII85 // com.eignex.kencode/ASCII85|{}ASCII85[0]
+ final fun (): kotlin/String // com.eignex.kencode/ASCII85.|(){}[0]
+final const val com.eignex.kencode/BASE_62 // com.eignex.kencode/BASE_62|{}BASE_62[0]
+ final fun (): kotlin/String // com.eignex.kencode/BASE_62.|(){}[0]
+final const val com.eignex.kencode/BASE_64 // com.eignex.kencode/BASE_64|{}BASE_64[0]
+ final fun (): kotlin/String // com.eignex.kencode/BASE_64.|(){}[0]
+final const val com.eignex.kencode/BASE_64_URL // com.eignex.kencode/BASE_64_URL|{}BASE_64_URL[0]
+ final fun (): kotlin/String // com.eignex.kencode/BASE_64_URL.|(){}[0]
+
+final fun (com.eignex.kencode/Checksum).com.eignex.kencode/asTransform(): com.eignex.kencode/PayloadTransform // com.eignex.kencode/asTransform|asTransform@com.eignex.kencode.Checksum(){}[0]
+final fun (com.eignex.kencode/PayloadTransform).com.eignex.kencode/then(com.eignex.kencode/PayloadTransform): com.eignex.kencode/PayloadTransform // com.eignex.kencode/then|then@com.eignex.kencode.PayloadTransform(com.eignex.kencode.PayloadTransform){}[0]
+final fun com.eignex.kencode/EncodedFormat(com.eignex.kencode/EncodedFormat = ..., kotlin/Function1): com.eignex.kencode/EncodedFormat // com.eignex.kencode/EncodedFormat|EncodedFormat(com.eignex.kencode.EncodedFormat;kotlin.Function1){}[0]
+final fun com.eignex.kencode/PackedFormat(com.eignex.kencode/PackedFormat = ..., kotlin/Function1): com.eignex.kencode/PackedFormat // com.eignex.kencode/PackedFormat|PackedFormat(com.eignex.kencode.PackedFormat;kotlin.Function1){}[0]
diff --git a/api/kencode.klib.api b/api/kencode.klib.api
index da1e16c..280975b 100644
--- a/api/kencode.klib.api
+++ b/api/kencode.klib.api
@@ -1,5 +1,5 @@
// Klib ABI Dump
-// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, mingwX64, wasmJs, wasmWasi]
+// Targets: [linuxX64]
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: true
diff --git a/build.gradle.kts b/build.gradle.kts
index 55fac69..e31bad9 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,6 +2,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
plugins {
id("com.eignex.kmp")
+ id("com.eignex.publish.github")
kotlin("plugin.serialization")
}
@@ -19,15 +20,34 @@ eignexPublish {
githubRepo.set("Eignex/kencode")
}
-@OptIn(ExperimentalWasmDsl::class)
+// Run the real JVM artifact on both the supported floor and the current LTS.
+eignexBuild {
+ packagedTestJdks.set(listOf(17, 25))
+}
+
+tasks.withType().configureEach {
+ // Do not reuse results produced with the old aggregate-only failure policy.
+ inputs.property("kotlin.tests.individualTaskReports", providers.gradleProperty("kotlin.tests.individualTaskReports"))
+}
+
+// Routine checks cover JVM + Linux x64; release validation opts into every target.
+val fullTargets = providers.gradleProperty("targets.full").isPresent
+
+@OptIn(ExperimentalWasmDsl::class, org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation::class)
kotlin {
+ abiValidation {
+ referenceDumpDir.set(layout.projectDirectory.dir(if (fullTargets) "api-full" else "api"))
+ }
jvm()
- js { browser(); nodejs() }
- wasmJs { browser(); nodejs() }
- wasmWasi { nodejs() }
- linuxX64(); linuxArm64()
- macosArm64(); mingwX64()
- iosX64(); iosArm64(); iosSimulatorArm64()
+ linuxX64()
+ if (fullTargets) {
+ js { browser(); nodejs() }
+ wasmJs { browser(); nodejs() }
+ wasmWasi { nodejs() }
+ linuxArm64()
+ macosArm64(); mingwX64()
+ iosX64(); iosArm64(); iosSimulatorArm64()
+ }
sourceSets {
commonMain.dependencies {
@@ -58,3 +78,8 @@ dokka {
}
}
}
+
+// Select an installed simulator in CI rather than relying on a particular iPhone model.
+tasks.withType().configureEach {
+ providers.gradleProperty("iosSimulatorDevice").orNull?.let { device.set(it) }
+}
diff --git a/consumer/build.gradle.kts b/consumer/build.gradle.kts
new file mode 100644
index 0000000..61213ba
--- /dev/null
+++ b/consumer/build.gradle.kts
@@ -0,0 +1,21 @@
+plugins { application }
+
+val pomOnly = providers.gradleProperty("consumer.pomOnly").getOrElse("false").toBoolean()
+val library = providers.gradleProperty("consumer.library").get()
+val module = if (pomOnly) "$library-jvm" else library
+val version = providers.gradleProperty("consumer.version").get()
+
+dependencies {
+ // No KBuild plugin, platform, or Kotlin dependency declaration: published metadata must suffice.
+ implementation("com.eignex:$module:$version") {
+ // CI reuses a private staging version; always inspect its current local metadata/artifact.
+ isChanging = true
+ }
+}
+
+java { toolchain.languageVersion.set(JavaLanguageVersion.of(17)) }
+application { mainClass.set("Consumer") }
+
+configurations.configureEach {
+ resolutionStrategy.cacheChangingModulesFor(0, "seconds")
+}
diff --git a/consumer/gradle.properties b/consumer/gradle.properties
new file mode 100644
index 0000000..c0d290c
--- /dev/null
+++ b/consumer/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.configuration-cache=true
+org.gradle.caching=true
+org.gradle.warning.mode=fail
+consumer.library=kencode
diff --git a/consumer/settings.gradle.kts b/consumer/settings.gradle.kts
new file mode 100644
index 0000000..f0f5104
--- /dev/null
+++ b/consumer/settings.gradle.kts
@@ -0,0 +1,29 @@
+rootProject.name = "library-consumer"
+
+plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" }
+
+dependencyResolutionManagement {
+ repositories {
+ exclusiveContent {
+ forRepository {
+ val githubRepository = providers.gradleProperty("consumer.githubRepository")
+ if (githubRepository.isPresent) {
+ ivy {
+ url = uri(githubRepository.get())
+ patternLayout { artifact("[artifact]-[revision](-[classifier]).[ext]") }
+ metadataSources { gradleMetadata() }
+ }
+ } else {
+ maven {
+ url = uri(providers.gradleProperty("consumer.repository").get())
+ if (providers.gradleProperty("consumer.pomOnly").getOrElse("false").toBoolean()) {
+ metadataSources { mavenPom(); artifact(); ignoreGradleMetadataRedirection() }
+ }
+ }
+ }
+ }
+ filter { includeGroup("com.eignex") }
+ }
+ mavenCentral()
+ }
+}
diff --git a/consumer/src/main/java/Consumer.java b/consumer/src/main/java/Consumer.java
new file mode 100644
index 0000000..40f9961
--- /dev/null
+++ b/consumer/src/main/java/Consumer.java
@@ -0,0 +1,24 @@
+import com.eignex.kencode.Base62;
+import com.eignex.kencode.Base64;
+import com.eignex.kencode.Base64Url;
+import com.eignex.kencode.Base85;
+import com.eignex.kencode.ByteEncoding;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+public final class Consumer {
+ public static void main(String[] args) {
+ var bytes = "Published encoding 0123456789".getBytes(StandardCharsets.UTF_8);
+ ByteEncoding[] codecs = {Base64.Default, Base64Url.INSTANCE, Base85.Default, Base62.INSTANCE};
+ for (var codec : codecs) {
+ var encoded = codec.encode(bytes, 0, bytes.length);
+ if (!Arrays.equals(bytes, codec.decode(encoded))) {
+ throw new AssertionError("Published encoding failed to round trip");
+ }
+ }
+ if (!Base64.Default.encode(bytes, 0, bytes.length).equals(java.util.Base64.getEncoder().encodeToString(bytes))) {
+ throw new AssertionError("Published Base64 differs from JDK Base64");
+ }
+ System.out.println("Published Java consumer passed");
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index a675318..8990cef 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,3 +4,4 @@ kotlin.suppressGradlePluginWarnings=IncorrectCompileOnlyDependencyWarning
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g
org.gradle.workers.max=4
kotlin.native.ignoreDisabledTargets=true
+kotlin.tests.individualTaskReports=true
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 9824f50..1ea56e6 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -13,7 +13,7 @@ pluginManagement {
mavenCentral()
}
resolutionStrategy.eachPlugin {
- if (requested.id.id in setOf("com.eignex.kmp", "org.jetbrains.kotlin.plugin.serialization")) {
+ if (requested.id.id in setOf("com.eignex.kmp", "com.eignex.publish.github", "org.jetbrains.kotlin.plugin.serialization")) {
useModule("com.eignex:kbuild:$kbuildVersion")
}
}