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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ aether/
├── aether-core/ # Core abstractions (Exchange, Pipeline, Dispatcher)
├── aether-db/ # ORM and database drivers
├── aether-web/ # Routing and controllers
├── aether-browser-client/ # Typed same-origin browser HTTP, navigation, and bootstrap APIs
├── aether-ui/ # UI rendering (SSR + CBOR)
├── aether-net/ # Network transport abstraction
├── aether-ksp/ # KSP-based migration generation
Expand Down
21 changes: 21 additions & 0 deletions aether-browser-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Aether Browser Client

`aether-browser-client` is Aether's small, browser-only HTTP and bootstrap layer for Kotlin/JS and
Kotlin/Wasm-JS applications. Browser interop stays inside Aether: applications use Kotlin APIs and
do not need handwritten JavaScript or direct DOM access.

```kotlin
val client = BrowserHttpClient(
BrowserHttpClientConfig(
csrfProvider = BrowserCsrfProvider.sessionStorage("portfolio.csrf")
)
)

val result: RunResponse = client.post("/api/run", RunRequest(source))
val state: InitialState = BrowserBootstrap.decode("portfolio-bootstrap")
BrowserHistory.replace("/playground#output")
```

Only same-origin relative request paths are accepted. Fetch always uses
`credentials = "same-origin"`; request and response limits, timeout, and redirect handling are
explicit configuration.
44 changes: 44 additions & 0 deletions aether-browser-client/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@file:OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)

plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.serialization)
}

kotlin {
js(IR) {
browser {
testTask {
enabled = false
}
}
nodejs()
}

wasmJs {
browser {
testTask {
enabled = false
}
}
nodejs()
}

sourceSets {
commonMain {
dependencies {
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines.core)
api(libs.kotlinx.serialization.core)
api(libs.kotlinx.serialization.json)
}
}

commonTest {
dependencies {
implementation(libs.kotlin.test)
implementation(libs.kotlinx.coroutines.test)
}
}
}
}
52 changes: 52 additions & 0 deletions aether-browser-client/gradle.lockfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
io.github.java-diff-utils:java-diff-utils:4.12=kotlinInternalAbiValidation
org.jetbrains.kotlin:abi-tools-api:2.3.21=kotlinInternalAbiValidation
org.jetbrains.kotlin:abi-tools:2.3.21=kotlinInternalAbiValidation
org.jetbrains.kotlin:kotlin-build-tools-api:2.3.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-compat:2.3.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-cri-impl:2.3.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-impl:2.3.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-compiler-embeddable:2.3.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-compiler-runner:2.3.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-daemon-client:2.3.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-daemon-embeddable:2.3.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-dom-api-compat:2.3.21=allSourceSetsCompileDependenciesMetadata,allTestSourceSetsCompileDependenciesMetadata,jsCompileClasspath,jsMainResolvableDependenciesMetadata,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestResolvableDependenciesMetadata,jsTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-klib-abi-reader:2.3.21=kotlinInternalAbiValidation
org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:2.3.21=kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-metadata-jvm:2.3.21=kotlinInternalAbiValidation
org.jetbrains.kotlin:kotlin-reflect:1.6.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-script-runtime:2.3.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathJsMain,kotlinCompilerPluginClasspathJsTest,kotlinCompilerPluginClasspathMetadataCommonMain,kotlinCompilerPluginClasspathMetadataMain,kotlinCompilerPluginClasspathMetadataWebMain,kotlinCompilerPluginClasspathWasmJsMain,kotlinCompilerPluginClasspathWasmJsTest,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-scripting-common:2.3.21=kotlinCompilerPluginClasspathJsMain,kotlinCompilerPluginClasspathJsTest,kotlinCompilerPluginClasspathMetadataCommonMain,kotlinCompilerPluginClasspathMetadataMain,kotlinCompilerPluginClasspathMetadataWebMain,kotlinCompilerPluginClasspathWasmJsMain,kotlinCompilerPluginClasspathWasmJsTest
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:2.3.21=kotlinCompilerPluginClasspathJsMain,kotlinCompilerPluginClasspathJsTest,kotlinCompilerPluginClasspathMetadataCommonMain,kotlinCompilerPluginClasspathMetadataMain,kotlinCompilerPluginClasspathMetadataWebMain,kotlinCompilerPluginClasspathWasmJsMain,kotlinCompilerPluginClasspathWasmJsTest
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:2.3.21=kotlinCompilerPluginClasspathJsMain,kotlinCompilerPluginClasspathJsTest,kotlinCompilerPluginClasspathMetadataCommonMain,kotlinCompilerPluginClasspathMetadataMain,kotlinCompilerPluginClasspathMetadataWebMain,kotlinCompilerPluginClasspathWasmJsMain,kotlinCompilerPluginClasspathWasmJsTest
org.jetbrains.kotlin:kotlin-scripting-jvm:2.3.21=kotlinCompilerPluginClasspathJsMain,kotlinCompilerPluginClasspathJsTest,kotlinCompilerPluginClasspathMetadataCommonMain,kotlinCompilerPluginClasspathMetadataMain,kotlinCompilerPluginClasspathMetadataWebMain,kotlinCompilerPluginClasspathWasmJsMain,kotlinCompilerPluginClasspathWasmJsTest
org.jetbrains.kotlin:kotlin-serialization-compiler-plugin-embeddable:2.3.21=kotlinCompilerPluginClasspathJsMain,kotlinCompilerPluginClasspathJsTest,kotlinCompilerPluginClasspathMetadataCommonMain,kotlinCompilerPluginClasspathMetadataMain,kotlinCompilerPluginClasspathMetadataWebMain,kotlinCompilerPluginClasspathWasmJsMain,kotlinCompilerPluginClasspathWasmJsTest
org.jetbrains.kotlin:kotlin-stdlib-common:2.3.21=allSourceSetsCompileDependenciesMetadata,allTestSourceSetsCompileDependenciesMetadata,commonMainResolvableDependenciesMetadata,commonTestResolvableDependenciesMetadata,jsMainResolvableDependenciesMetadata,jsTestResolvableDependenciesMetadata,metadataCommonMainCompileClasspath,metadataCompileClasspath,metadataWebMainCompileClasspath,wasmJsMainResolvableDependenciesMetadata,wasmJsTestResolvableDependenciesMetadata,webMainResolvableDependenciesMetadata,webTestResolvableDependenciesMetadata
org.jetbrains.kotlin:kotlin-stdlib-js:2.3.21=jsCompileClasspath,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib-wasm-js:2.3.21=wasmJsCompileClasspath,wasmJsNpmAggregated,wasmJsRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.3.21=allSourceSetsCompileDependenciesMetadata,allTestSourceSetsCompileDependenciesMetadata,commonMainResolvableDependenciesMetadata,commonTestResolvableDependenciesMetadata,jsCompileClasspath,jsMainResolvableDependenciesMetadata,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestResolvableDependenciesMetadata,jsTestRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathJsMain,kotlinCompilerPluginClasspathJsTest,kotlinCompilerPluginClasspathMetadataCommonMain,kotlinCompilerPluginClasspathMetadataMain,kotlinCompilerPluginClasspathMetadataWebMain,kotlinCompilerPluginClasspathWasmJsMain,kotlinCompilerPluginClasspathWasmJsTest,kotlinInternalAbiValidation,kotlinKlibCommonizerClasspath,metadataCommonMainCompileClasspath,metadataCompileClasspath,metadataWebMainCompileClasspath,wasmJsCompileClasspath,wasmJsMainResolvableDependenciesMetadata,wasmJsNpmAggregated,wasmJsRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestResolvableDependenciesMetadata,wasmJsTestRuntimeClasspath,webMainResolvableDependenciesMetadata,webTestResolvableDependenciesMetadata
org.jetbrains.kotlin:kotlin-test-js:2.3.21=jsTestCompileClasspath,jsTestNpmAggregated,jsTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-test-wasm-js:2.3.21=wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestRuntimeClasspath
org.jetbrains.kotlin:kotlin-test:2.3.21=allTestSourceSetsCompileDependenciesMetadata,commonTestResolvableDependenciesMetadata,jsTestCompileClasspath,jsTestNpmAggregated,jsTestResolvableDependenciesMetadata,jsTestRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestResolvableDependenciesMetadata,wasmJsTestRuntimeClasspath,webTestResolvableDependenciesMetadata
org.jetbrains.kotlin:kotlin-tooling-core:2.3.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlinx:atomicfu-js:0.26.1=jsCompileClasspath,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestRuntimeClasspath
org.jetbrains.kotlinx:atomicfu-wasm-js:0.26.1=wasmJsCompileClasspath,wasmJsNpmAggregated,wasmJsRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestRuntimeClasspath
org.jetbrains.kotlinx:atomicfu:0.23.1=allSourceSetsCompileDependenciesMetadata,allTestSourceSetsCompileDependenciesMetadata,commonMainResolvableDependenciesMetadata,commonTestResolvableDependenciesMetadata,jsMainResolvableDependenciesMetadata,jsTestResolvableDependenciesMetadata,metadataCommonMainCompileClasspath,metadataCompileClasspath,metadataWebMainCompileClasspath,wasmJsMainResolvableDependenciesMetadata,wasmJsTestResolvableDependenciesMetadata,webMainResolvableDependenciesMetadata,webTestResolvableDependenciesMetadata
org.jetbrains.kotlinx:atomicfu:0.26.1=jsCompileClasspath,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestRuntimeClasspath,wasmJsCompileClasspath,wasmJsNpmAggregated,wasmJsRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.10.2=jsCompileClasspath,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core-wasm-js:1.10.2=wasmJsCompileClasspath,wasmJsNpmAggregated,wasmJsRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2=allSourceSetsCompileDependenciesMetadata,allTestSourceSetsCompileDependenciesMetadata,commonMainResolvableDependenciesMetadata,commonTestResolvableDependenciesMetadata,jsCompileClasspath,jsMainResolvableDependenciesMetadata,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestResolvableDependenciesMetadata,jsTestRuntimeClasspath,metadataCommonMainCompileClasspath,metadataCompileClasspath,metadataWebMainCompileClasspath,wasmJsCompileClasspath,wasmJsMainResolvableDependenciesMetadata,wasmJsNpmAggregated,wasmJsRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestResolvableDependenciesMetadata,wasmJsTestRuntimeClasspath,webMainResolvableDependenciesMetadata,webTestResolvableDependenciesMetadata
org.jetbrains.kotlinx:kotlinx-coroutines-test-js:1.10.2=jsTestCompileClasspath,jsTestNpmAggregated,jsTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-test-wasm-js:1.10.2=wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2=allTestSourceSetsCompileDependenciesMetadata,commonTestResolvableDependenciesMetadata,jsTestCompileClasspath,jsTestNpmAggregated,jsTestResolvableDependenciesMetadata,jsTestRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestResolvableDependenciesMetadata,wasmJsTestRuntimeClasspath,webTestResolvableDependenciesMetadata
org.jetbrains.kotlinx:kotlinx-serialization-core-js:1.9.0=jsCompileClasspath,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-core-wasm-js:1.9.0=wasmJsCompileClasspath,wasmJsNpmAggregated,wasmJsRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-core:1.9.0=allSourceSetsCompileDependenciesMetadata,allTestSourceSetsCompileDependenciesMetadata,commonMainResolvableDependenciesMetadata,commonTestResolvableDependenciesMetadata,jsCompileClasspath,jsMainResolvableDependenciesMetadata,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestResolvableDependenciesMetadata,jsTestRuntimeClasspath,metadataCommonMainCompileClasspath,metadataCompileClasspath,metadataWebMainCompileClasspath,wasmJsCompileClasspath,wasmJsMainResolvableDependenciesMetadata,wasmJsNpmAggregated,wasmJsRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestResolvableDependenciesMetadata,wasmJsTestRuntimeClasspath,webMainResolvableDependenciesMetadata,webTestResolvableDependenciesMetadata
org.jetbrains.kotlinx:kotlinx-serialization-json-js:1.9.0=jsCompileClasspath,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-json-wasm-js:1.9.0=wasmJsCompileClasspath,wasmJsNpmAggregated,wasmJsRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0=allSourceSetsCompileDependenciesMetadata,allTestSourceSetsCompileDependenciesMetadata,commonMainResolvableDependenciesMetadata,commonTestResolvableDependenciesMetadata,jsCompileClasspath,jsMainResolvableDependenciesMetadata,jsNpmAggregated,jsRuntimeClasspath,jsTestCompileClasspath,jsTestNpmAggregated,jsTestResolvableDependenciesMetadata,jsTestRuntimeClasspath,metadataCommonMainCompileClasspath,metadataCompileClasspath,metadataWebMainCompileClasspath,wasmJsCompileClasspath,wasmJsMainResolvableDependenciesMetadata,wasmJsNpmAggregated,wasmJsRuntimeClasspath,wasmJsTestCompileClasspath,wasmJsTestNpmAggregated,wasmJsTestResolvableDependenciesMetadata,wasmJsTestRuntimeClasspath,webMainResolvableDependenciesMetadata,webTestResolvableDependenciesMetadata
org.jetbrains:annotations:13.0=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathJsMain,kotlinCompilerPluginClasspathJsTest,kotlinCompilerPluginClasspathMetadataCommonMain,kotlinCompilerPluginClasspathMetadataMain,kotlinCompilerPluginClasspathMetadataWebMain,kotlinCompilerPluginClasspathWasmJsMain,kotlinCompilerPluginClasspathWasmJsTest,kotlinInternalAbiValidation,kotlinKlibCommonizerClasspath
empty=commonMainImplementationDependenciesMetadata,commonTestImplementationDependenciesMetadata,jsMainImplementationDependenciesMetadata,jsTestImplementationDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,wasmJsMainImplementationDependenciesMetadata,wasmJsTestImplementationDependenciesMetadata,webMainImplementationDependenciesMetadata,webTestImplementationDependenciesMetadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package codes.yousef.aether.browser

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer

enum class BrowserBootstrapEncoding {
JSON,
BASE64_JSON
}

enum class BrowserBootstrapFailure {
MISSING,
EMPTY,
TOO_LARGE,
INVALID_ENCODING,
INVALID_JSON
}

class BrowserBootstrapException(
val failure: BrowserBootstrapFailure,
message: String,
cause: Throwable? = null
) : Exception(message, cause)

/** Reads bounded bootstrap data by element ID without exposing DOM APIs to application code. */
object BrowserBootstrap {
inline fun <reified T> decode(
elementId: String,
encoding: BrowserBootstrapEncoding = BrowserBootstrapEncoding.JSON,
maximumBytes: Int = DEFAULT_MAXIMUM_BROWSER_BOOTSTRAP_BYTES,
json: Json = Json { ignoreUnknownKeys = true }
): T = decode(elementId, serializer(), encoding, maximumBytes, json)

fun <T> decode(
elementId: String,
deserializer: DeserializationStrategy<T>,
encoding: BrowserBootstrapEncoding = BrowserBootstrapEncoding.JSON,
maximumBytes: Int = DEFAULT_MAXIMUM_BROWSER_BOOTSTRAP_BYTES,
json: Json = Json { ignoreUnknownKeys = true }
): T {
requireValidBootstrapId(elementId)
require(maximumBytes in 1..MAXIMUM_BROWSER_BOOTSTRAP_BYTES) {
"Browser bootstrap limit must be between 1 byte and $MAXIMUM_BROWSER_BOOTSTRAP_BYTES bytes"
}
val stored = readPlatformBootstrapText(elementId)
?: throw BrowserBootstrapException(
BrowserBootstrapFailure.MISSING,
"Browser bootstrap element was not found"
)
return decodeStoredBootstrap(stored, deserializer, encoding, maximumBytes, json)
}
}

internal fun <T> decodeStoredBootstrap(
stored: String,
deserializer: DeserializationStrategy<T>,
encoding: BrowserBootstrapEncoding,
maximumBytes: Int,
json: Json
): T {
require(maximumBytes in 1..MAXIMUM_BROWSER_BOOTSTRAP_BYTES) {
"Browser bootstrap limit must be between 1 byte and $MAXIMUM_BROWSER_BOOTSTRAP_BYTES bytes"
}
if (stored.isBlank()) {
throw BrowserBootstrapException(BrowserBootstrapFailure.EMPTY, "Browser bootstrap element was empty")
}
if (stored.encodeToByteArray().size > maximumBytes * 2) {
throw BrowserBootstrapException(
BrowserBootstrapFailure.TOO_LARGE,
"Browser bootstrap data exceeded its configured hard limit"
)
}
val decoded = when (encoding) {
BrowserBootstrapEncoding.JSON -> stored
BrowserBootstrapEncoding.BASE64_JSON -> try {
decodePlatformBase64Utf8(stored.trim())
} catch (error: Throwable) {
throw BrowserBootstrapException(
BrowserBootstrapFailure.INVALID_ENCODING,
"Browser bootstrap data was not valid base64-encoded UTF-8",
error
)
}
}
if (decoded.encodeToByteArray().size > maximumBytes) {
throw BrowserBootstrapException(
BrowserBootstrapFailure.TOO_LARGE,
"Browser bootstrap data exceeded its configured hard limit"
)
}
return try {
json.decodeFromString(deserializer, decoded)
} catch (error: SerializationException) {
throw BrowserBootstrapException(
BrowserBootstrapFailure.INVALID_JSON,
"Browser bootstrap data could not be decoded",
error
)
} catch (error: IllegalArgumentException) {
throw BrowserBootstrapException(
BrowserBootstrapFailure.INVALID_JSON,
"Browser bootstrap data could not be decoded",
error
)
}
}

const val DEFAULT_MAXIMUM_BROWSER_BOOTSTRAP_BYTES: Int = 1 * 1_024 * 1_024
const val MAXIMUM_BROWSER_BOOTSTRAP_BYTES: Int = 4 * 1_024 * 1_024

private fun requireValidBootstrapId(elementId: String) {
require(elementId.isNotBlank()) { "Browser bootstrap element ID must not be blank" }
require(elementId.length <= 256) { "Browser bootstrap element ID is too long" }
require(elementId.none(Char::isISOControl)) { "Browser bootstrap element ID contains control characters" }
}
Loading
Loading