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
57 changes: 56 additions & 1 deletion compose/api/compose.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ abstract fun interface app.tinyui.schema/Component { // app.tinyui.schema/Compon
abstract fun Render(app.tinyui.schema/NodeScope, androidx.compose.runtime/Composer?, kotlin/Int) // app.tinyui.schema/Component.Render|Render(app.tinyui.schema.NodeScope;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
}

abstract fun interface app.tinyui/BundleFiles { // app.tinyui/BundleFiles|null[0]
abstract suspend fun read(kotlin/String): kotlin/ByteArray? // app.tinyui/BundleFiles.read|read(kotlin.String){}[0]
}

abstract fun interface app.tinyui/Config { // app.tinyui/Config|null[0]
abstract fun get(kotlin/String): kotlin/String? // app.tinyui/Config.get|get(kotlin.String){}[0]

Expand Down Expand Up @@ -231,16 +235,32 @@ final class app.tinyui.schema/ComponentSchema { // app.tinyui.schema/ComponentSc
}

final class app.tinyui/BuildManifest { // app.tinyui/BuildManifest|null[0]
constructor <init>(kotlin.collections/List<kotlin/String>, kotlin.collections/List<kotlin/String>, kotlin.collections/Map<kotlin/String, kotlin/String>, kotlin.collections/Map<kotlin/String, kotlin/String>) // app.tinyui/BuildManifest.<init>|<init>(kotlin.collections.List<kotlin.String>;kotlin.collections.List<kotlin.String>;kotlin.collections.Map<kotlin.String,kotlin.String>;kotlin.collections.Map<kotlin.String,kotlin.String>){}[0]
constructor <init>(kotlin.collections/List<kotlin/String>, kotlin.collections/List<kotlin/String>, kotlin.collections/Map<kotlin/String, kotlin/String>, kotlin.collections/Map<kotlin/String, kotlin/String>, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/Int, kotlin.collections/Map<kotlin/String, kotlin/String>, kotlin/String? = ...) // app.tinyui/BuildManifest.<init>|<init>(kotlin.collections.List<kotlin.String>;kotlin.collections.List<kotlin.String>;kotlin.collections.Map<kotlin.String,kotlin.String>;kotlin.collections.Map<kotlin.String,kotlin.String>;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.Int;kotlin.collections.Map<kotlin.String,kotlin.String>;kotlin.String?){}[0]

final val buildIds // app.tinyui/BuildManifest.buildIds|{}buildIds[0]
final fun <get-buildIds>(): kotlin.collections/Map<kotlin/String, kotlin/String> // app.tinyui/BuildManifest.buildIds.<get-buildIds>|<get-buildIds>(){}[0]
final val createdAt // app.tinyui/BuildManifest.createdAt|{}createdAt[0]
final fun <get-createdAt>(): kotlin/String // app.tinyui/BuildManifest.createdAt.<get-createdAt>|<get-createdAt>(){}[0]
final val engine // app.tinyui/BuildManifest.engine|{}engine[0]
final fun <get-engine>(): kotlin/String // app.tinyui/BuildManifest.engine.<get-engine>|<get-engine>(){}[0]
final val files // app.tinyui/BuildManifest.files|{}files[0]
final fun <get-files>(): kotlin.collections/Map<kotlin/String, kotlin/String> // app.tinyui/BuildManifest.files.<get-files>|<get-files>(){}[0]
final val hashes // app.tinyui/BuildManifest.hashes|{}hashes[0]
final fun <get-hashes>(): kotlin.collections/Map<kotlin/String, kotlin/String> // app.tinyui/BuildManifest.hashes.<get-hashes>|<get-hashes>(){}[0]
final val name // app.tinyui/BuildManifest.name|{}name[0]
final fun <get-name>(): kotlin/String // app.tinyui/BuildManifest.name.<get-name>|<get-name>(){}[0]
final val pages // app.tinyui/BuildManifest.pages|{}pages[0]
final fun <get-pages>(): kotlin.collections/List<kotlin/String> // app.tinyui/BuildManifest.pages.<get-pages>|<get-pages>(){}[0]
final val protocol // app.tinyui/BuildManifest.protocol|{}protocol[0]
final fun <get-protocol>(): kotlin/Int // app.tinyui/BuildManifest.protocol.<get-protocol>|<get-protocol>(){}[0]
final val publicKey // app.tinyui/BuildManifest.publicKey|{}publicKey[0]
final fun <get-publicKey>(): kotlin/String // app.tinyui/BuildManifest.publicKey.<get-publicKey>|<get-publicKey>(){}[0]
final val runtime // app.tinyui/BuildManifest.runtime|{}runtime[0]
final fun <get-runtime>(): kotlin.collections/List<kotlin/String> // app.tinyui/BuildManifest.runtime.<get-runtime>|<get-runtime>(){}[0]
final val runtimeVersion // app.tinyui/BuildManifest.runtimeVersion|{}runtimeVersion[0]
final fun <get-runtimeVersion>(): kotlin/String? // app.tinyui/BuildManifest.runtimeVersion.<get-runtimeVersion>|<get-runtimeVersion>(){}[0]
final val version // app.tinyui/BuildManifest.version|{}version[0]
final fun <get-version>(): kotlin/String // app.tinyui/BuildManifest.version.<get-version>|<get-version>(){}[0]

final fun buildId(kotlin/String): kotlin/String // app.tinyui/BuildManifest.buildId|buildId(kotlin.String){}[0]
final fun file(kotlin/String): kotlin/String // app.tinyui/BuildManifest.file|file(kotlin.String){}[0]
Expand All @@ -250,6 +270,21 @@ final class app.tinyui/BuildManifest { // app.tinyui/BuildManifest|null[0]
}
}

final class app.tinyui/Bundle { // app.tinyui/Bundle|null[0]
constructor <init>(app.tinyui/BuildManifest, app.tinyui/BundleFiles) // app.tinyui/Bundle.<init>|<init>(app.tinyui.BuildManifest;app.tinyui.BundleFiles){}[0]

final val manifest // app.tinyui/Bundle.manifest|{}manifest[0]
final fun <get-manifest>(): app.tinyui/BuildManifest // app.tinyui/Bundle.manifest.<get-manifest>|<get-manifest>(){}[0]
final val name // app.tinyui/Bundle.name|{}name[0]
final fun <get-name>(): kotlin/String // app.tinyui/Bundle.name.<get-name>|<get-name>(){}[0]

final suspend fun page(kotlin/String): app.tinyui/LoadedPage // app.tinyui/Bundle.page|page(kotlin.String){}[0]

final object Companion { // app.tinyui/Bundle.Companion|null[0]
final suspend fun load(app.tinyui/BundleFiles): app.tinyui/Bundle // app.tinyui/Bundle.Companion.load|load(app.tinyui.BundleFiles){}[0]
}
}

final class app.tinyui/EventBus { // app.tinyui/EventBus|null[0]
constructor <init>() // app.tinyui/EventBus.<init>|<init>(){}[0]

Expand Down Expand Up @@ -322,6 +357,19 @@ final class app.tinyui/InMemoryStore : app.tinyui/Store { // app.tinyui/InMemory
final fun set(kotlin/String, kotlin/String) // app.tinyui/InMemoryStore.set|set(kotlin.String;kotlin.String){}[0]
}

final class app.tinyui/LoadedPage { // app.tinyui/LoadedPage|null[0]
constructor <init>(app.tinyui/RuntimeBundle, app.tinyui/PageModule, app.tinyui/SourceMaps, app.tinyui/Bundle) // app.tinyui/LoadedPage.<init>|<init>(app.tinyui.RuntimeBundle;app.tinyui.PageModule;app.tinyui.SourceMaps;app.tinyui.Bundle){}[0]

final val bundle // app.tinyui/LoadedPage.bundle|{}bundle[0]
final fun <get-bundle>(): app.tinyui/Bundle // app.tinyui/LoadedPage.bundle.<get-bundle>|<get-bundle>(){}[0]
final val module // app.tinyui/LoadedPage.module|{}module[0]
final fun <get-module>(): app.tinyui/PageModule // app.tinyui/LoadedPage.module.<get-module>|<get-module>(){}[0]
final val runtime // app.tinyui/LoadedPage.runtime|{}runtime[0]
final fun <get-runtime>(): app.tinyui/RuntimeBundle // app.tinyui/LoadedPage.runtime.<get-runtime>|<get-runtime>(){}[0]
final val sourceMaps // app.tinyui/LoadedPage.sourceMaps|{}sourceMaps[0]
final fun <get-sourceMaps>(): app.tinyui/SourceMaps // app.tinyui/LoadedPage.sourceMaps.<get-sourceMaps>|<get-sourceMaps>(){}[0]
}

final class app.tinyui/PageError { // app.tinyui/PageError|null[0]
constructor <init>(kotlin/String, kotlin/String, kotlin/String, kotlin/String, kotlin/String? = ..., kotlin/String? = ..., kotlin.collections/List<app.tinyui/StackFrame> = ..., kotlin/String? = ...) // app.tinyui/PageError.<init>|<init>(kotlin.String;kotlin.String;kotlin.String;kotlin.String;kotlin.String?;kotlin.String?;kotlin.collections.List<app.tinyui.StackFrame>;kotlin.String?){}[0]

Expand Down Expand Up @@ -406,6 +454,9 @@ final class app.tinyui/RuntimeBundle { // app.tinyui/RuntimeBundle|null[0]
final class app.tinyui/SourceMaps { // app.tinyui/SourceMaps|null[0]
constructor <init>(kotlin.collections/Map<kotlin/String, kotlin/String>) // app.tinyui/SourceMaps.<init>|<init>(kotlin.collections.Map<kotlin.String,kotlin.String>){}[0]

final val isEmpty // app.tinyui/SourceMaps.isEmpty|{}isEmpty[0]
final fun <get-isEmpty>(): kotlin/Boolean // app.tinyui/SourceMaps.isEmpty.<get-isEmpty>|<get-isEmpty>(){}[0]

final fun frames(kotlin/String?): kotlin.collections/List<app.tinyui/StackFrame> // app.tinyui/SourceMaps.frames|frames(kotlin.String?){}[0]

final object Companion { // app.tinyui/SourceMaps.Companion|null[0]
Expand Down Expand Up @@ -601,13 +652,15 @@ final val app.tinyui.schema/app_tinyui_schema_SizeValue_Fixed$stableprop // app.
final val app.tinyui.schema/app_tinyui_schema_SizeValue_Wrap$stableprop // app.tinyui.schema/app_tinyui_schema_SizeValue_Wrap$stableprop|#static{}app_tinyui_schema_SizeValue_Wrap$stableprop[0]
final val app.tinyui.schema/app_tinyui_schema_Theme$stableprop // app.tinyui.schema/app_tinyui_schema_Theme$stableprop|#static{}app_tinyui_schema_Theme$stableprop[0]
final val app.tinyui/app_tinyui_BuildManifest$stableprop // app.tinyui/app_tinyui_BuildManifest$stableprop|#static{}app_tinyui_BuildManifest$stableprop[0]
final val app.tinyui/app_tinyui_Bundle$stableprop // app.tinyui/app_tinyui_Bundle$stableprop|#static{}app_tinyui_Bundle$stableprop[0]
final val app.tinyui/app_tinyui_EventBus$stableprop // app.tinyui/app_tinyui_EventBus$stableprop|#static{}app_tinyui_EventBus$stableprop[0]
final val app.tinyui/app_tinyui_HostException$stableprop // app.tinyui/app_tinyui_HostException$stableprop|#static{}app_tinyui_HostException$stableprop[0]
final val app.tinyui/app_tinyui_HostServices$stableprop // app.tinyui/app_tinyui_HostServices$stableprop|#static{}app_tinyui_HostServices$stableprop[0]
final val app.tinyui/app_tinyui_HttpClient_None$stableprop // app.tinyui/app_tinyui_HttpClient_None$stableprop|#static{}app_tinyui_HttpClient_None$stableprop[0]
final val app.tinyui/app_tinyui_HttpRequest$stableprop // app.tinyui/app_tinyui_HttpRequest$stableprop|#static{}app_tinyui_HttpRequest$stableprop[0]
final val app.tinyui/app_tinyui_HttpResponse$stableprop // app.tinyui/app_tinyui_HttpResponse$stableprop|#static{}app_tinyui_HttpResponse$stableprop[0]
final val app.tinyui/app_tinyui_InMemoryStore$stableprop // app.tinyui/app_tinyui_InMemoryStore$stableprop|#static{}app_tinyui_InMemoryStore$stableprop[0]
final val app.tinyui/app_tinyui_LoadedPage$stableprop // app.tinyui/app_tinyui_LoadedPage$stableprop|#static{}app_tinyui_LoadedPage$stableprop[0]
final val app.tinyui/app_tinyui_Navigator_None$stableprop // app.tinyui/app_tinyui_Navigator_None$stableprop|#static{}app_tinyui_Navigator_None$stableprop[0]
final val app.tinyui/app_tinyui_PageError$stableprop // app.tinyui/app_tinyui_PageError$stableprop|#static{}app_tinyui_PageError$stableprop[0]
final val app.tinyui/app_tinyui_PageFailure$stableprop // app.tinyui/app_tinyui_PageFailure$stableprop|#static{}app_tinyui_PageFailure$stableprop[0]
Expand Down Expand Up @@ -648,13 +701,15 @@ final fun app.tinyui.schema/app_tinyui_schema_Theme$stableprop_getter(): kotlin/
final fun app.tinyui/PageFailureScreen(app.tinyui/PageFailure, androidx.compose.runtime/Composer?, kotlin/Int) // app.tinyui/PageFailureScreen|PageFailureScreen(app.tinyui.PageFailure;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun app.tinyui/TinyUIPage(app.tinyui/RuntimeBundle, app.tinyui/PageModule, app.tinyui.schema/ComponentRegistry, app.tinyui/PageSink, app.tinyui/HostServices?, kotlin/String?, androidx.compose.ui/Modifier?, app.tinyui/SourceMaps?, kotlin/Function3<app.tinyui/PageFailure, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, kotlin/Function1<app.tinyui/PageHost, kotlin/Unit>?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // app.tinyui/TinyUIPage|TinyUIPage(app.tinyui.RuntimeBundle;app.tinyui.PageModule;app.tinyui.schema.ComponentRegistry;app.tinyui.PageSink;app.tinyui.HostServices?;kotlin.String?;androidx.compose.ui.Modifier?;app.tinyui.SourceMaps?;kotlin.Function3<app.tinyui.PageFailure,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;kotlin.Function1<app.tinyui.PageHost,kotlin.Unit>?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun app.tinyui/app_tinyui_BuildManifest$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_BuildManifest$stableprop_getter|app_tinyui_BuildManifest$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_Bundle$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_Bundle$stableprop_getter|app_tinyui_Bundle$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_EventBus$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_EventBus$stableprop_getter|app_tinyui_EventBus$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_HostException$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_HostException$stableprop_getter|app_tinyui_HostException$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_HostServices$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_HostServices$stableprop_getter|app_tinyui_HostServices$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_HttpClient_None$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_HttpClient_None$stableprop_getter|app_tinyui_HttpClient_None$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_HttpRequest$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_HttpRequest$stableprop_getter|app_tinyui_HttpRequest$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_HttpResponse$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_HttpResponse$stableprop_getter|app_tinyui_HttpResponse$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_InMemoryStore$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_InMemoryStore$stableprop_getter|app_tinyui_InMemoryStore$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_LoadedPage$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_LoadedPage$stableprop_getter|app_tinyui_LoadedPage$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_Navigator_None$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_Navigator_None$stableprop_getter|app_tinyui_Navigator_None$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_PageError$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_PageError$stableprop_getter|app_tinyui_PageError$stableprop_getter(){}[0]
final fun app.tinyui/app_tinyui_PageFailure$stableprop_getter(): kotlin/Int // app.tinyui/app_tinyui_PageFailure$stableprop_getter|app_tinyui_PageFailure$stableprop_getter(){}[0]
Expand Down
10 changes: 4 additions & 6 deletions compose/src/androidHostTest/kotlin/app/tinyui/BundleSmokeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ class BundleSmokeTest {
@Test
fun stacksFromABuiltPageMapBackToTheSource() = runBlocking {
if (!out.isDirectory) return@runBlocking
val manifest = BuildManifest.parse(out.resolve("manifest.json").readText())
val maps = SourceMaps(mapOf("sample/todos" to out.resolve("pages/todos.js.map").readText(), "tinyui-core" to out.resolve("runtime/core.js.map").readText()))
val bundle = Bundle.load { path -> out.resolve(path).takeIf { it.isFile }?.readBytes() }
val loaded = bundle.page("sample/todos")
val errors = mutableListOf<PageError>()
val sink = object : PageSink {
override fun error(error: PageError) { errors += error }
Expand All @@ -49,9 +49,7 @@ class BundleSmokeTest {
if (request.url.endsWith("page=1")) HttpResponse(200, """{"items":[{"id":1,"title":"a","done":false}],"next":2}""")
else throw HostException("E_HTTP", "500")
}
val runtime = RuntimeBundle(out.resolve("runtime/core.bin").readBytes(), out.resolve("runtime/native.bin").readBytes())
val page = PageModule("sample/todos", out.resolve("pages/todos.bin").readBytes(), manifest.buildId("sample/todos"))
val host = PageHost(runtime, page, ComponentRegistry().registerBuiltins(), sink, HostServices(http = http), sourceMaps = maps)
val host = PageHost(loaded.runtime, loaded.module, ComponentRegistry().registerBuiltins(), sink, HostServices(http = http), sourceMaps = loaded.sourceMaps)
host.start()
try {
// the list only exists once the first page has loaded; node ids are dense, so scan them
Expand All @@ -62,7 +60,7 @@ class BundleSmokeTest {
while (errors.none { it.kind == "E7" }) delay(20)
} }
val e = errors.single { it.kind == "E7" }
assertEquals(manifest.buildId("sample/todos"), e.buildId)
assertEquals(bundle.manifest.buildId("sample/todos"), e.buildId)
val own = e.frames.firstOrNull { it.mapped && it.file == "src/pages/todos.tsx" } ?: error("no mapped frame in ${e.frames}; stack=${e.jsStack}")
val source = File("../sample/js/src/pages/todos.tsx").readLines()
assertTrue("await http.get" in source[own.line - 1], "frame $own points at the await; stack=${e.jsStack}")
Expand Down
34 changes: 32 additions & 2 deletions compose/src/commonMain/kotlin/app/tinyui/BuildManifest.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,58 @@
package app.tinyui

import kotlinx.serialization.json.Json
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive

/** `manifest.json` as `tinyui build` writes it: module names, the output path and the build id of each. */
/** `manifest.json` as `tinyui build` writes it (docs/updates.md §1.1): module names, output paths, package identity. */
class BuildManifest(
val runtime: List<String>,
val pages: List<String>,
val files: Map<String, String>,
val buildIds: Map<String, String>,
val name: String,
val publicKey: String,
val version: String,
val createdAt: String,
val engine: String,
val protocol: Int,
val hashes: Map<String, String>,
/** Present only in a manifest `tinyui bundle` wrote: the embedded package has none. */
val runtimeVersion: String? = null,
) {
fun buildId(module: String): String = buildIds[module] ?: ""

/** Output path of [module] without extension (`runtime/core`, `pages/home`): append `.bin` or `.js.map`. */
fun file(module: String): String = files[module] ?: module

companion object {
/** Throws [IllegalArgumentException] when the package identity (`name`, `publicKey`) is missing. */
fun parse(json: String): BuildManifest {
val root = Json.parseToJsonElement(json).jsonObject
val names = { key: String -> root[key]?.jsonArray?.map { it.jsonPrimitive.content } ?: emptyList() }
val strings = { key: String -> root[key]?.jsonObject?.mapValues { it.value.jsonPrimitive.content } ?: emptyMap() }
return BuildManifest(names("runtime"), names("pages"), strings("files"), strings("buildIds"))
// a JSON null is an absent value, not the string "null"
val string = { key: String -> root[key]?.jsonPrimitive?.contentOrNull ?: "" }
val name = string("name")
val publicKey = string("publicKey")
require(name.isNotEmpty() && publicKey.isNotEmpty()) { "manifest.json has no package identity (name / publicKey); rebuild with a current tinyui-cli" }
return BuildManifest(
runtime = names("runtime"),
pages = names("pages"),
files = strings("files"),
buildIds = strings("buildIds"),
name = name,
publicKey = publicKey,
version = string("version"),
createdAt = string("createdAt"),
engine = string("engine"),
protocol = root["protocol"]?.jsonPrimitive?.intOrNull ?: 0,
hashes = strings("hashes"),
runtimeVersion = root["runtimeVersion"]?.jsonPrimitive?.contentOrNull,
)
}
}
}
Loading
Loading