From 9f69d5166dde24620f952beb9bb8623502ee621c Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 8 Jul 2024 20:16:26 +0200 Subject: [PATCH 01/32] feat: Update wasmer dependencies --- Cargo.lock | 1632 +++++++++++++++++++++++++---- Cargo.toml | 25 +- src/fs/directory.rs | 10 + src/lib.rs | 3 + src/package_loader.rs | 3 +- src/runtime.rs | 21 +- src/tasks/post_message_payload.rs | 13 +- src/tasks/scheduler.rs | 2 +- src/tasks/scheduler_message.rs | 8 +- src/tasks/task_wasm.rs | 21 +- src/wasmer.rs | 26 +- 11 files changed, 1507 insertions(+), 257 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c85cd7b8..533aa038 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -60,9 +66,21 @@ checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e" [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-trait" @@ -84,6 +102,12 @@ dependencies = [ "critical-section", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.1.0" @@ -111,6 +135,18 @@ version = "0.21.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + [[package]] name = "bincode" version = "1.3.3" @@ -144,6 +180,19 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake3" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -153,6 +202,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -196,6 +255,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" +dependencies = [ + "serde", +] + [[package]] name = "cc" version = "1.0.83" @@ -235,12 +303,28 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "cooked-waker" version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -260,6 +344,15 @@ dependencies = [ "windows-sys 0.33.0", ] +[[package]] +name = "counter" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d458e66999348f56fd3ffcfbb7f7951542075ca8359687c703de6500c1ddccd" +dependencies = [ + "num-traits", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -284,6 +377,25 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.11" @@ -309,6 +421,62 @@ dependencies = [ "typenum", ] +[[package]] +name = "cynic" +version = "3.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7335114540697c7b1c1a0131cbe0e983fdb1e646f881234afe9e2a66133ac99a" +dependencies = [ + "cynic-proc-macros", + "ref-cast", + "reqwest 0.11.27", + "serde", + "serde_json", + "static_assertions", + "thiserror", +] + +[[package]] +name = "cynic-codegen" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c0ec86f960a00ce087e96ff6f073f6ff28b6876d69ce8caa06c03fb4143981c" +dependencies = [ + "counter", + "cynic-parser", + "darling 0.20.3", + "once_cell", + "ouroboros", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.48", + "thiserror", +] + +[[package]] +name = "cynic-parser" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718f6cd8c54ae5249fd42b0c86639df0100b8a86eea2e5f1b915cde2e1481453" +dependencies = [ + "indexmap 2.2.6", + "lalrpop-util", + "logos", +] + +[[package]] +name = "cynic-proc-macros" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a69ecdf4aa110fed1c0c8de290bc8ccb2835388733cf2f418f0abdf6ff3899" +dependencies = [ + "cynic-codegen", + "darling 0.20.3", + "quote", + "syn 2.0.48", +] + [[package]] name = "darling" version = "0.14.4" @@ -353,6 +521,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "strsim", "syn 2.0.48", ] @@ -398,6 +567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -452,6 +622,45 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "document-features" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" +dependencies = [ + "litrs", +] + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "edge-schema" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0966f1fd49610cc67a835124e6fb4d00a36104e1aa34383c5ef5a265ca00ea2a" +dependencies = [ + "anyhow", + "bytesize", + "once_cell", + "parking_lot", + "rand_chacha", + "rand_core", + "schemars", + "serde", + "serde_json", + "serde_path_to_error", + "serde_yaml 0.8.26", + "sparx", + "time", + "url", + "uuid", + "wcgi-host", +] + [[package]] name = "educe" version = "0.4.23" @@ -464,6 +673,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "enum-iterator" version = "0.7.0" @@ -574,6 +798,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -690,9 +929,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -707,12 +946,69 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.11", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +[[package]] +name = "harsh" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6fce2283849822530a18d7d8eeb1719ac65a27cfb6649c0dc8dfd2d2cc5edfb" + [[package]] name = "hash32" version = "0.2.1" @@ -759,6 +1055,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hex" version = "0.4.3" @@ -777,77 +1079,257 @@ dependencies = [ ] [[package]] -name = "iana-time-zone" -version = "0.1.59" +name = "http" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", + "bytes", + "fnv", + "itoa", ] [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "http-body" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "cc", + "bytes", + "http 0.2.11", + "pin-project-lite", ] [[package]] -name = "id-arena" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" - -[[package]] -name = "ident_case" -version = "1.0.1" +name = "http-body" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] [[package]] -name = "idna" -version = "0.5.0" +name = "http-body-util" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "pin-project-lite", ] [[package]] -name = "indexmap" -version = "1.9.3" +name = "http-serde" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "6f560b665ad9f1572cfcaf034f7fb84338a7ce945216d64a90fd81f046a3caee" dependencies = [ - "autocfg", - "hashbrown 0.12.3", + "http 0.2.11", "serde", ] [[package]] -name = "indexmap" -version = "2.1.0" +name = "httparse" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown 0.14.3", - "serde", -] +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] -name = "instant" -version = "0.1.12" +name = "httpdate" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.11", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4fe55fb7a772d59a5ff1dfbff4fe0258d19b89fec4b233e75d35d5d2316badc" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.0", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.0", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.4.0", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.3", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", "js-sys", @@ -855,6 +1337,21 @@ dependencies = [ "web-sys", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -870,6 +1367,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata 0.4.3", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -893,9 +1399,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.152" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linked-hash-map" @@ -918,6 +1424,12 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "lock_api" version = "0.4.11" @@ -934,6 +1446,39 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "logos" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161971eb88a0da7ae0c333e1063467c5b5727e7fb6b710b8db4814eade3a42e8" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e31badd9de5131fdf4921f6473d457e3dd85b11b7f091ceb50e4df7c3eeb12a" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2", + "quote", + "regex-syntax 0.8.2", + "syn 2.0.48", +] + +[[package]] +name = "logos-derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c2a69b3eb68d5bd595107c9ee58d7e07fe2bb5e360cc85b0f084dedac80de0a" +dependencies = [ + "logos-codegen", +] + [[package]] name = "lz4_flex" version = "0.11.1" @@ -952,6 +1497,12 @@ dependencies = [ "libc", ] +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "matchers" version = "0.1.0" @@ -994,6 +1545,22 @@ dependencies = [ "autocfg", ] +[[package]] +name = "merge-streams" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f84f6452969abd246e7ac1fe4fe75906c76e8ec88d898df9aef37e0f3b6a7c2" +dependencies = [ + "futures-core", + "pin-project", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1003,12 +1570,40 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "more-asserts" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1085,12 +1680,91 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ouroboros" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" +dependencies = [ + "heck 0.4.1", + "itertools", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.48", +] + [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + [[package]] name = "parking_lot_core" version = "0.9.9" @@ -1123,7 +1797,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.1.0", + "indexmap 2.2.6", ] [[package]] @@ -1158,6 +1832,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "powerfmt" version = "0.2.0" @@ -1213,6 +1893,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "version_check", + "yansi", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -1298,6 +1991,26 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "regex" version = "1.10.2" @@ -1369,6 +2082,102 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690" +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.6", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.11", + "http-body 0.4.6", + "hyper 0.14.29", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.50.0", +] + +[[package]] +name = "reqwest" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.4.0", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg 0.52.0", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rkyv" version = "0.7.43" @@ -1416,9 +2225,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.4.1", "errno", @@ -1427,6 +2236,46 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + +[[package]] +name = "rustls-webpki" +version = "0.102.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.16" @@ -1442,6 +2291,41 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "schemars" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars_derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.48", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1460,6 +2344,29 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "self_cell" version = "1.0.3" @@ -1536,6 +2443,17 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "serde_json" version = "1.0.111" @@ -1547,15 +2465,37 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.8.26" @@ -1570,11 +2510,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.30" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -1628,9 +2568,39 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smoltcp" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee34c1e1bfc7e9206cc0fb8030a90129b4e319ab53856249bb27642cab914fb3" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "managed", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "sparx" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2257c28eacecfc38c658124ed239e7ecfc9b89082c0794b0672420b63b84c6" +dependencies = [ + "byteorder", +] [[package]] name = "spin" @@ -1659,6 +2629,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -1681,6 +2657,39 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -1706,25 +2715,24 @@ checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", "windows-sys 0.52.0", ] [[package]] -name = "term_size" -version = "0.3.2" +name = "terminal_size" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "libc", - "winapi", + "rustix", + "windows-sys 0.48.0", ] [[package]] @@ -1818,8 +2826,12 @@ checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", + "libc", + "mio", "pin-project-lite", + "socket2", "tokio-macros", + "windows-sys 0.48.0", ] [[package]] @@ -1833,6 +2845,27 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-serde" version = "0.8.0" @@ -1848,16 +2881,27 @@ dependencies = [ "serde", ] +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + [[package]] name = "tokio-util" -version = "0.6.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", - "log", "pin-project-lite", "tokio", ] @@ -1876,21 +2920,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.0", + "toml_edit 0.22.14", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] @@ -1901,26 +2945,53 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.33", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.13", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", ] +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" @@ -1983,6 +3054,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "twox-hash" version = "1.6.3" @@ -2049,9 +3126,15 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" @@ -2073,9 +3156,13 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "uuid" -version = "1.6.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" +dependencies = [ + "getrandom", + "serde", +] [[package]] name = "valuable" @@ -2083,6 +3170,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -2091,8 +3184,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "virtual-fs" -version = "0.11.0" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2be050a12999526f637afa40f010f3657827e63a804020bdb1be2e9fd5367a1" dependencies = [ "anyhow", "async-trait", @@ -2114,8 +3208,9 @@ dependencies = [ [[package]] name = "virtual-mio" -version = "0.3.0" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff8026c9d7575dc9afd8a0907357acb7aa55ec262097fbccae5da42f67773b3c" dependencies = [ "async-trait", "bytes", @@ -2128,12 +3223,13 @@ dependencies = [ [[package]] name = "virtual-net" -version = "0.6.2" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74aa69bbb19e531d274ba1aa730028f6fcd2117513ff6696d020af05188dfe92" dependencies = [ "anyhow", "async-trait", - "base64", + "base64 0.21.6", "bincode", "bytecheck", "bytes", @@ -2143,6 +3239,7 @@ dependencies = [ "pin-project-lite", "rkyv", "serde", + "smoltcp", "thiserror", "tokio", "tokio-serde", @@ -2167,7 +3264,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19bc05e8380515c4337c40ef03b2ff233e391315b178a320de8640703d522efe" dependencies = [ - "heck", + "heck 0.3.3", "wai-bindgen-gen-core", ] @@ -2177,18 +3274,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f35ce5e74086fac87f3a7bd50f643f00fe3559adb75c88521ecaa01c8a6199" dependencies = [ - "heck", - "wai-bindgen-gen-core", - "wai-bindgen-gen-rust", -] - -[[package]] -name = "wai-bindgen-gen-wasmer" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f61484185d8c520a86d5a7f7f8265f446617c2f9774b2e20a52de19b6e53432" -dependencies = [ - "heck", + "heck 0.3.3", "wai-bindgen-gen-core", "wai-bindgen-gen-rust", ] @@ -2215,32 +3301,6 @@ dependencies = [ "wai-bindgen-gen-rust-wasm", ] -[[package]] -name = "wai-bindgen-wasmer" -version = "0.18.0" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" -dependencies = [ - "anyhow", - "bitflags 1.3.2", - "once_cell", - "thiserror", - "tracing", - "wai-bindgen-wasmer-impl", - "wasmer", -] - -[[package]] -name = "wai-bindgen-wasmer-impl" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b3488ed88d4dd0e3bf85bad4e27dac6cb31aae5d122a5dda2424803c8dc863a" -dependencies = [ - "proc-macro2", - "syn 1.0.109", - "wai-bindgen-gen-core", - "wai-bindgen-gen-wasmer", -] - [[package]] name = "wai-parser" version = "0.2.3" @@ -2270,6 +3330,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2278,9 +3347,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2288,9 +3357,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -2337,9 +3406,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2347,9 +3416,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -2360,9 +3429,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-bindgen-test" @@ -2398,10 +3467,24 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmer" -version = "4.2.5" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1852ee143a2d8143265bfee017c43bf690702d6c2b45a763a2f13e669f5b7ec" dependencies = [ "bytes", "cfg-if", @@ -2421,16 +3504,40 @@ dependencies = [ "wasmer-derive", "wasmer-types", "wasmer-vm", - "wasmparser 0.83.0", - "wasmparser 0.95.0", + "wasmparser 0.121.2", "wat", "winapi", ] +[[package]] +name = "wasmer-api" +version = "0.0.30" +dependencies = [ + "anyhow", + "cynic", + "edge-schema", + "futures", + "getrandom", + "harsh", + "merge-streams", + "pin-project-lite", + "reqwest 0.11.27", + "serde", + "serde_json", + "serde_path_to_error", + "time", + "tokio", + "tracing", + "url", + "wasmer-config 0.4.0", + "webc", +] + [[package]] name = "wasmer-compiler" -version = "4.2.5" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4f157d715f3bb60c2c9d7b9e48299a30e9209f87f4484f79f9cd586b40b6ee" dependencies = [ "backtrace", "bytes", @@ -2452,12 +3559,56 @@ dependencies = [ "wasmer-types", "wasmer-vm", "winapi", + "xxhash-rust", +] + +[[package]] +name = "wasmer-config" +version = "0.4.0" +dependencies = [ + "anyhow", + "bytesize", + "derive_builder", + "hex", + "indexmap 2.2.6", + "schemars", + "semver", + "serde", + "serde_cbor", + "serde_json", + "serde_yaml 0.9.34+deprecated", + "thiserror", + "toml 0.8.14", + "url", +] + +[[package]] +name = "wasmer-config" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b4a632496950fde9ad821e195ef1a301440076f7c7d80de55239a140359bcbd" +dependencies = [ + "anyhow", + "bytesize", + "derive_builder", + "hex", + "indexmap 2.2.6", + "schemars", + "semver", + "serde", + "serde_cbor", + "serde_json", + "serde_yaml 0.9.34+deprecated", + "thiserror", + "toml 0.8.14", + "url", ] [[package]] name = "wasmer-derive" -version = "4.2.5" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cd5732ff64370e98986f9753cce13b91cc9d3c4b649e31b0d08d5db69164ea" dependencies = [ "proc-macro-error", "proc-macro2", @@ -2467,12 +3618,13 @@ dependencies = [ [[package]] name = "wasmer-journal" -version = "0.1.0" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4f51818afcb61d608414a1eee90e9d368753376ff2340c63deb548a6aa0c0c7" dependencies = [ "anyhow", "async-trait", - "base64", + "base64 0.21.6", "bincode", "bytecheck", "bytes", @@ -2484,6 +3636,7 @@ dependencies = [ "serde_json", "thiserror", "tracing", + "virtual-fs", "virtual-net", "wasmer", "wasmer-wasix-types", @@ -2500,13 +3653,16 @@ dependencies = [ "console_error_panic_hook", "derivative", "futures", - "http", + "http 0.2.11", "instant", "js-sys", "once_cell", + "reqwest 0.12.5", "serde", "serde-wasm-bindgen 0.6.3", + "tempfile", "tokio", + "toml 0.8.14", "tracing", "tracing-subscriber", "url", @@ -2517,50 +3673,42 @@ dependencies = [ "wasm-bindgen-futures", "wasm-bindgen-test", "wasmer", + "wasmer-api", + "wasmer-config 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-types", "wasmer-wasix", "web-sys", "webc", ] -[[package]] -name = "wasmer-toml" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d21472954ee9443235ca32522b17fc8f0fe58e2174556266a0d9766db055cc52" -dependencies = [ - "anyhow", - "derive_builder", - "indexmap 2.1.0", - "semver", - "serde", - "serde_cbor", - "serde_json", - "serde_yaml 0.9.30", - "thiserror", - "toml 0.8.8", -] - [[package]] name = "wasmer-types" -version = "4.2.5" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c890fd0dbda40df03977b899d1ad7113deba3c225f2cc7b88deb7633044d3e07" dependencies = [ "bytecheck", "enum-iterator", "enumset", + "getrandom", + "hex", "indexmap 1.9.3", "more-asserts", "rkyv", "serde", "serde_bytes", + "sha2", "target-lexicon", "thiserror", + "webc", + "xxhash-rust", ] [[package]] name = "wasmer-vm" -version = "4.2.5" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0dc60ab800cf0bd44e2d35d88422d256d2470b00c72778f91bfb826c42dbd0" dependencies = [ "backtrace", "cc", @@ -2587,13 +3735,15 @@ dependencies = [ [[package]] name = "wasmer-wasix" -version = "0.18.0" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24faf9bc4a34e7281db698c8cd0e6449f0dfabc4cf0e8c08f2599da70d4da7" dependencies = [ "anyhow", "async-trait", - "base64", + "base64 0.21.6", "bincode", + "blake3", "bytecheck", "bytes", "cfg-if", @@ -2605,7 +3755,7 @@ dependencies = [ "getrandom", "heapless", "hex", - "http", + "http 0.2.11", "js-sys", "lazy_static", "libc", @@ -2622,25 +3772,26 @@ dependencies = [ "serde_cbor", "serde_derive", "serde_json", - "serde_yaml 0.8.26", + "serde_yaml 0.9.34+deprecated", "sha2", "shared-buffer", "tempfile", - "term_size", + "terminal_size", "termios", "thiserror", "tokio", + "tokio-stream", "tracing", "url", "urlencoding", "virtual-fs", "virtual-mio", "virtual-net", - "wai-bindgen-wasmer", "waker-fn", "wasm-bindgen", "wasm-bindgen-futures", "wasmer", + "wasmer-config 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-journal", "wasmer-types", "wasmer-wasix-types", @@ -2653,8 +3804,9 @@ dependencies = [ [[package]] name = "wasmer-wasix-types" -version = "0.18.0" -source = "git+https://github.com/wasmerio/wasmer?branch=master#0cf0f5b26debffa1d4e615732ab1b55b991ef687" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e6f8e53945f532947482311efb95e7724197f0c4780eeea3b56e0bca79d0bfc" dependencies = [ "anyhow", "bitflags 1.3.2", @@ -2674,12 +3826,6 @@ dependencies = [ "wasmer-types", ] -[[package]] -name = "wasmparser" -version = "0.83.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" - [[package]] name = "wasmparser" version = "0.95.0" @@ -2690,6 +3836,17 @@ dependencies = [ "url", ] +[[package]] +name = "wasmparser" +version = "0.121.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" +dependencies = [ + "bitflags 2.4.1", + "indexmap 2.2.6", + "semver", +] + [[package]] name = "wast" version = "64.0.0" @@ -2711,6 +3868,33 @@ dependencies = [ "wast", ] +[[package]] +name = "wcgi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ca8f334eec3a8197bd25a612c74f415b8691d219ee11f1acd20f15a3e2bf77" +dependencies = [ + "http 0.2.11", + "http-serde", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "wcgi-host" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a762cf2b0ed389a2a2fb591d63a398c1a4c0f8bef938cfd040285a3c63b695cc" +dependencies = [ + "http 0.2.11", + "schemars", + "serde", + "tokio", + "wasmparser 0.95.0", + "wcgi", +] + [[package]] name = "web-sys" version = "0.3.66" @@ -2723,18 +3907,19 @@ dependencies = [ [[package]] name = "webc" -version = "5.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "973ca5a91b4fb3e4bb37cfebe03ef9364d0aff2765256abefdb7e79dc9188483" +version = "6.0.0-rc1" dependencies = [ "anyhow", - "base64", - "byteorder", + "base64 0.21.6", "bytes", + "cfg-if", + "document-features", "flate2", + "ignore", "indexmap 1.9.3", "leb128", "lexical-sort", + "libc", "once_cell", "path-clean", "rand", @@ -2749,8 +3934,7 @@ dependencies = [ "thiserror", "toml 0.7.8", "url", - "walkdir", - "wasmer-toml", + "wasmer-config 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2812,6 +3996,15 @@ dependencies = [ "windows_x86_64_msvc 0.33.0", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -2974,6 +4167,35 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wyz" version = "0.5.1" @@ -2996,9 +4218,9 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53be06678ed9e83edb1745eb72efc0bbcd7b5c3c35711a860906aed827a13d61" +checksum = "63658493314859b4dfdf3fb8c1defd61587839def09582db50b8a4e93afca6bb" [[package]] name = "yaml-rust" @@ -3008,3 +4230,15 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index b2dc7958..514a1436 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,21 +20,27 @@ http = "0.2" instant = { version = "0.1", features = ["wasm-bindgen"] } js-sys = "0.3" once_cell = "1" +reqwest = { version = "0.12.5", features = ["stream"] } serde = { version = "1", features = ["derive"] } serde-wasm-bindgen = "0.6" +tempfile = "3.10.1" tokio = { version = "1", features = ["sync"], default_features = false } +toml = "0.8.14" tracing = { version = "0.1", features = ["log", "release_max_level_debug"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } url = "2.4.0" -virtual-fs = { version = "0.11.0", default-features = false } -virtual-net = { version = "0.6.0", default-features = false, features = ["remote"] } +virtual-fs = { version = "0.13.0", default-features = false } +virtual-net = { version = "0.6.7", default-features = false, features = ["remote"] } wasm-bindgen = { version = "0.2" } wasm-bindgen-derive = "0.2.1" wasm-bindgen-futures = "0.4" wasm-bindgen-test = "0.3.37" -wasmer = { version = "4.2.5", default-features = false, features = ["js", "js-default", "tracing", "wasm-types-polyfill", "enable-serde"] } -wasmer-wasix = { version = "0.18", default-features = false, features = ["js", "js-default"] } -webc = "5.3.0" +wasmer = { version = "4.3.2", default-features = false, features = ["js", "js-default", "wasm-types-polyfill", "enable-serde"] } +wasmer-api = { version = "0.0.30", path = "../wasmer/lib/backend-api" } +wasmer-config = "0.4.0" +wasmer-types = "4.3.2" +wasmer-wasix = { version = "0.22.0", default-features = false, features = ["js", "js-default"] } +webc = "6.0.0-rc1" [dependencies.web-sys] version = "0.3" @@ -95,10 +101,11 @@ dwarf-debug-info = false wasm-opt = ["--enable-threads", "--enable-bulk-memory", "-Oz"] [patch.crates-io] -virtual-net = { git = "https://github.com/wasmerio/wasmer", branch = "master" } -virtual-fs = { git = "https://github.com/wasmerio/wasmer", branch = "master" } -wasmer-wasix = { git = "https://github.com/wasmerio/wasmer", branch = "master" } -wasmer = { git = "https://github.com/wasmerio/wasmer", branch = "master" } +webc = {path = "../pirita/crates/webc"} +#virtual-net = { git = "https://github.com/wasmerio/wasmer", branch = "master" } +#virtual-fs = { git = "https://github.com/wasmerio/wasmer", branch = "master" } +#wasmer-wasix = { git = "https://github.com/wasmerio/wasmer", branch = "master" } +#wasmer = { git = "https://github.com/wasmerio/wasmer", branch = "master" } # virtual-net = { path = "../wasmer/lib/virtual-net" } # virtual-fs = { path = "../wasmer/lib/virtual-fs" } # wasmer-wasix = { path = "../wasmer/lib/wasix" } diff --git a/src/fs/directory.rs b/src/fs/directory.rs index 12d27ed7..f0b0d2ef 100644 --- a/src/fs/directory.rs +++ b/src/fs/directory.rs @@ -201,6 +201,16 @@ impl FileSystem for Directory { fn new_open_options(&self) -> virtual_fs::OpenOptions { virtual_fs::OpenOptions::new(self) } + + #[tracing::instrument(level = "trace", skip(self))] + fn readlink(&self, path: &Path) -> virtual_fs::Result { + self.0.readlink(path) + } + + #[tracing::instrument(level = "trace", skip(self))] + fn symlink_metadata(&self, path: &Path) -> virtual_fs::Result { + self.0.symlink_metadata(path) + } } impl virtual_fs::FileOpener for Directory { diff --git a/src/lib.rs b/src/lib.rs index ab4e5951..5af7dbd7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(once_cell_try)] + #[cfg(test)] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); @@ -10,6 +12,7 @@ mod logging; mod net; mod options; mod package_loader; +pub mod registry; mod run; mod runtime; mod streams; diff --git a/src/package_loader.rs b/src/package_loader.rs index 5d50efc1..deedb4a3 100644 --- a/src/package_loader.rs +++ b/src/package_loader.rs @@ -94,8 +94,7 @@ impl wasmer_wasix::runtime::package_loader::PackageLoader for PackageLoader { #[tracing::instrument( skip_all, fields( - pkg.name=summary.pkg.name.as_str(), - pkg.version=%summary.pkg.version, + pkg=format!("{:?}", summary.pkg.id), pkg.url=summary.dist.webc.as_str(), ), )] diff --git a/src/runtime.rs b/src/runtime.rs index 22b75a18..4d1400ba 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -3,13 +3,14 @@ use std::sync::{atomic::AtomicBool, Arc, Mutex, Weak}; use http::HeaderValue; use once_cell::sync::Lazy; use virtual_net::VirtualNetworking; +use wasmer_config::package::PackageSource; use wasmer_wasix::{ http::{HttpClient, WebHttpClient}, os::{TtyBridge, TtyOptions}, runtime::{ module_cache::ThreadLocalCache, package_loader::PackageLoader, - resolver::{PackageSpecifier, PackageSummary, QueryError, Source, WapmSource}, + resolver::{PackageSummary, QueryError, Source, WapmSource}, }, VirtualTaskManager, WasiTtyState, }; @@ -167,19 +168,11 @@ impl wasmer_wasix::runtime::Runtime for Runtime { self.module_cache.clone() } - fn load_module_sync(&self, wasm: &[u8]) -> Result { + fn load_module_sync(&self, wasm: &[u8]) -> Result { let wasm = unsafe { js_sys::Uint8Array::view(wasm) }; - let module = js_sys::WebAssembly::Module::new(&wasm).map_err(crate::utils::js_error)?; - // Note: We need to use this From impl because it will use the - // wasm-types-polyfill to parse the *.wasm file's import section. - // - // The browser doesn't give you any way to inspect the imports at the - // moment, so without the polyfill we'll always assume the module wants - // a minimum of 1 page of memory. This causes modules that want more - // memory by default (e.g. sharrattj/bash) to fail with an instantiation - // error. - // - // https://github.com/wasmerio/wasmer/blob/8ec4f1d76062e2a612ac2f70f4a73eaf59f8fe9f/lib/api/src/js/module.rs#L323-L328 + let module = js_sys::WebAssembly::Module::new(&wasm) + .map_err(|x| wasmer_wasix::SpawnError::Other(crate::utils::js_error(x).into()))?; + Ok(wasmer::Module::from((module, wasm.to_vec()))) } @@ -236,7 +229,7 @@ struct UnsupportedSource; #[async_trait::async_trait] impl Source for UnsupportedSource { - async fn query(&self, _package: &PackageSpecifier) -> Result, QueryError> { + async fn query(&self, _package: &PackageSource) -> Result, QueryError> { Err(QueryError::Unsupported) } } diff --git a/src/tasks/post_message_payload.rs b/src/tasks/post_message_payload.rs index b9127ee3..1e3f0aa6 100644 --- a/src/tasks/post_message_payload.rs +++ b/src/tasks/post_message_payload.rs @@ -1,7 +1,7 @@ use derivative::Derivative; use js_sys::WebAssembly; use wasm_bindgen::JsValue; -use wasmer_wasix::runtime::module_cache::ModuleHash; +use wasmer_types::ModuleHash; use crate::tasks::{ interop::Serializer, task_wasm::SpawnWasm, AsyncTask, BlockingModuleTask, BlockingTask, @@ -126,7 +126,11 @@ impl PostMessagePayload { consts::TYPE_CACHE_MODULE => { let module = de.js(consts::MODULE)?; let hash = de.string(consts::MODULE_HASH)?; - let hash = ModuleHash::parse_hex(&hash)?; + let hash = if let Ok(hash) = ModuleHash::sha256_parse_hex(&hash) { + hash + } else { + ModuleHash::xxhash_parse_hex(&hash)? + }; Ok(PostMessagePayload::Notification( Notification::CacheModule { hash, module }, @@ -170,10 +174,7 @@ mod tests { use wasm_bindgen::JsCast; use wasm_bindgen_test::wasm_bindgen_test; use wasmer::AsJs; - use wasmer_wasix::{ - runtime::{module_cache::ModuleHash, task_manager::TaskWasm}, - WasiEnvBuilder, - }; + use wasmer_wasix::{runtime::task_manager::TaskWasm, WasiEnvBuilder}; use crate::{ runtime::Runtime, diff --git a/src/tasks/scheduler.rs b/src/tasks/scheduler.rs index 63924368..59718f11 100644 --- a/src/tasks/scheduler.rs +++ b/src/tasks/scheduler.rs @@ -10,7 +10,7 @@ use tokio::sync::mpsc::{self}; use tracing::Instrument; use wasm_bindgen::{JsCast, JsValue}; use wasmer::AsJs; -use wasmer_wasix::runtime::module_cache::ModuleHash; +use wasmer_types::ModuleHash; use crate::tasks::{ AsyncJob, BlockingJob, Notification, PostMessagePayload, SchedulerMessage, WorkerHandle, diff --git a/src/tasks/scheduler_message.rs b/src/tasks/scheduler_message.rs index ad96b949..6ccf1bd3 100644 --- a/src/tasks/scheduler_message.rs +++ b/src/tasks/scheduler_message.rs @@ -4,7 +4,7 @@ use derivative::Derivative; use js_sys::WebAssembly; use wasm_bindgen::JsValue; use wasmer::AsJs; -use wasmer_wasix::runtime::module_cache::ModuleHash; +use wasmer_types::ModuleHash; use crate::{ tasks::{ @@ -82,7 +82,11 @@ impl SchedulerMessage { } consts::TYPE_CACHE_MODULE => { let hash = de.string(consts::MODULE_HASH)?; - let hash = ModuleHash::parse_hex(&hash)?; + let hash = if let Ok(hash) = ModuleHash::sha256_parse_hex(&hash) { + hash + } else { + ModuleHash::xxhash_parse_hex(&hash)? + }; let module: WebAssembly::Module = de.js(consts::MODULE)?; Ok(SchedulerMessage::CacheModule { hash, diff --git a/src/tasks/task_wasm.rs b/src/tasks/task_wasm.rs index 344f50ee..a9f05b04 100644 --- a/src/tasks/task_wasm.rs +++ b/src/tasks/task_wasm.rs @@ -16,7 +16,7 @@ use wasmer_wasix::{ SpawnMemoryType, }, wasmer_wasix_types::wasi::ExitCode, - InstanceSnapshot, WasiEnv, WasiFunctionEnv, WasiThreadError, + StoreSnapshot, WasiEnv, WasiFunctionEnv, WasiThreadError, }; use crate::tasks::SchedulerMessage; @@ -28,15 +28,14 @@ pub(crate) fn to_scheduler_message( run, env, module, - snapshot, spawn_type, trigger, update_layout, recycle, + globals, } = task; let module_bytes = module.serialize().unwrap(); - let snapshot = snapshot.map(InstanceSnapshot::clone); let (memory_ty, memory, run_type) = match spawn_type { wasmer_wasix::runtime::SpawnMemoryType::CreateMemory => { @@ -84,6 +83,7 @@ pub(crate) fn to_scheduler_message( } }); + let store_snapshot = globals.cloned(); let spawn_wasm = SpawnWasm { trigger: trigger.map(|trigger| WasmRunTrigger { run: trigger, @@ -94,10 +94,10 @@ pub(crate) fn to_scheduler_message( run_type, env, module_bytes, - snapshot, update_layout, result: None, recycle, + store_snapshot, }; Ok(SchedulerMessage::SpawnWithModuleAndMemory { @@ -190,8 +190,8 @@ pub(crate) struct SpawnWasm { /// The raw bytes for the WebAssembly module being run. #[derivative(Debug(format_with = "crate::utils::hidden"))] module_bytes: Bytes, - /// A snapshot of the instance, if we are forking an existing instance. - snapshot: Option, + /// A snapshot of the instance store, used to fork from existing instances. + store_snapshot: Option, /// An asynchronous callback which is used to run asyncify methods. The /// returned value is used in [`wasmer_wasix::rewind()`] or instant /// responses. @@ -242,11 +242,11 @@ impl ReadySpawnWasm { run_type, env, module_bytes, - snapshot, update_layout, result, trigger: _, recycle, + store_snapshot, }) = self; // Invoke the callback which will run the web assembly module @@ -255,8 +255,8 @@ impl ReadySpawnWasm { wasm_memory, module_bytes, env, + store_snapshot, run_type, - snapshot, update_layout, ) .context("Unable to initialize the context and store")?; @@ -278,8 +278,8 @@ fn build_ctx_and_store( memory: JsValue, module_bytes: Bytes, env: WasiEnv, + store_snapshot: Option, run_type: WasmMemoryType, - snapshot: Option, update_layout: bool, ) -> Option<(WasiFunctionEnv, Store)> { // Compile the web assembly module @@ -302,9 +302,8 @@ fn build_ctx_and_store( } }; - let snapshot = snapshot.as_ref(); let (ctx, store) = - match WasiFunctionEnv::new_with_store(module, env, snapshot, spawn_type, update_layout) { + match WasiFunctionEnv::new_with_store(module, env, store_snapshot.as_ref(), spawn_type, update_layout) { Ok(a) => a, Err(err) => { tracing::error!( diff --git a/src/wasmer.rs b/src/wasmer.rs index be5adaf9..dca7d1be 100644 --- a/src/wasmer.rs +++ b/src/wasmer.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{str::FromStr, sync::Arc}; use bytes::BytesMut; use futures::{channel::oneshot, TryStreamExt}; @@ -6,11 +6,11 @@ use js_sys::{JsString, Reflect, Uint8Array}; use tracing::Instrument; use virtual_fs::{AsyncReadExt, Pipe}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue, UnwrapThrowExt}; +use wasmer_config::package::PackageSource; use wasmer_wasix::{ bin_factory::BinaryPackage, os::{Tty, TtyOptions}, runners::{wasi::WasiRunner, Runner}, - runtime::resolver::PackageSpecifier, Runtime as _, }; use web_sys::{ReadableStream, WritableStream}; @@ -78,7 +78,7 @@ impl Wasmer { specifier: &str, runtime: Option, ) -> Result { - let specifier = PackageSpecifier::parse(specifier)?; + let specifier = PackageSource::from_str(specifier)?; let runtime = runtime.unwrap_or_default().resolve()?.into_inner(); let pkg = BinaryPackage::from_registry(&specifier, &*runtime).await?; @@ -216,23 +216,23 @@ pub(crate) async fn configure_runner( Error, > { let args = options.parse_args()?; - runner.set_args(args); + runner.with_args(args); let env = options.parse_env()?; - runner.set_envs(env); + runner.with_envs(env); for (dest, dir) in options.mounted_directories()? { - runner.mount(dest, Arc::new(dir)); + runner.with_mount(dest, Arc::new(dir)); } if let Some(uses) = options.uses() { let uses = crate::utils::js_string_array(uses)?; let packages = load_injected_packages(uses, runtime).await?; - runner.add_injected_packages(packages); + runner.with_injected_packages(packages); } let (stderr_pipe, stderr_stream) = crate::streams::output_pipe(); - runner.set_stderr(Box::new(stderr_pipe)); + runner.with_stderr(Box::new(stderr_pipe)); let tty_options = runtime.tty_options().clone(); match setup_tty(options, tty_options) { @@ -243,16 +243,16 @@ pub(crate) async fn configure_runner( stdin_stream, } => { tracing::debug!("Setting up interactive TTY"); - runner.set_stdin(Box::new(stdin_pipe)); - runner.set_stdout(Box::new(stdout_pipe)); + runner.with_stdin(Box::new(stdin_pipe)); + runner.with_stdout(Box::new(stdout_pipe)); runtime.set_connected_to_tty(true); Ok((Some(stdin_stream), stdout_stream, stderr_stream)) } TerminalMode::NonInteractive { stdin } => { tracing::debug!("Setting up non-interactive TTY"); let (stdout_pipe, stdout_stream) = crate::streams::output_pipe(); - runner.set_stdin(Box::new(stdin)); - runner.set_stdout(Box::new(stdout_pipe)); + runner.with_stdin(Box::new(stdin)); + runner.with_stdout(Box::new(stdout_pipe)); // HACK: Make sure we don't report stdin as interactive. This // doesn't belong here because now it'll affect every other @@ -402,7 +402,7 @@ async fn load_injected_packages( #[tracing::instrument(level = "debug", skip(runtime))] async fn load_package(pkg: &str, runtime: &Runtime) -> Result { - let specifier: PackageSpecifier = pkg.parse()?; + let specifier: PackageSource = pkg.parse()?; let pkg = BinaryPackage::from_registry(&specifier, runtime).await?; Ok(pkg) From 12b00b70b88e292618eb972ce46cadd63cb63e1d Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 8 Jul 2024 20:16:43 +0200 Subject: [PATCH 02/32] feat: Add registry API and tests --- WasmerSDK.ts | 42 +++-- rollup.config.mjs | 2 +- src/registry/app.rs | 76 +++++++++ src/registry/mod.rs | 73 +++++++++ src/registry/package.rs | 344 ++++++++++++++++++++++++++++++++++++++++ tests/registry.test.ts | 192 ++++++++++++++++++++++ 6 files changed, 711 insertions(+), 18 deletions(-) create mode 100644 src/registry/app.rs create mode 100644 src/registry/mod.rs create mode 100644 src/registry/package.rs create mode 100644 tests/registry.test.ts diff --git a/WasmerSDK.ts b/WasmerSDK.ts index b5d9b908..dd462884 100644 --- a/WasmerSDK.ts +++ b/WasmerSDK.ts @@ -1,20 +1,28 @@ export * from "./pkg/wasmer_js"; // @ts-ignore -import load, { InitInput, InitOutput, ThreadPoolWorker, setWorkerUrl } from "./pkg/wasmer_js"; +import load, { InitInput, InitOutput, ThreadPoolWorker, setWorkerUrl, RegistryConfig } from "./pkg/wasmer_js"; + +type RegistryInput = { + registry_url: string, + token?: string +} /** * Initialize the underlying WebAssembly module. */ -export const init = async (module_or_path?: InitInput | Promise, maybe_memory?: WebAssembly.Memory): Promise => { - if (!module_or_path) { - // This will be replaced by the rollup bundler at the SDK build time - // to point to a valid http location of the SDK using unpkg.com. - let wasmUrl = (globalThis as any)["wasmUrl"]; - if (wasmUrl) { - module_or_path = new URL(wasmUrl); - } - } - return load(module_or_path, maybe_memory); +export const init = async (module_or_path?: InitInput | Promise, maybe_memory?: WebAssembly.Memory, maybe_registry?: RegistryInput): Promise => { + if (!module_or_path) { + // This will be replaced by the rollup bundler at the SDK build time + // to point to a valid http location of the SDK using unpkg.com. + let wasmUrl = (globalThis as any)["wasmUrl"]; + if (wasmUrl) { + module_or_path = new URL(wasmUrl); + } + } + + (globalThis as any)["__WASMER_REGISTRY__"] = maybe_registry; + + return load(module_or_path, maybe_memory); } /** @@ -22,10 +30,10 @@ export const init = async (module_or_path?: InitInput | Promise, mayb * an unpkg url that is set up at the SDK build time. */ export const setDefaultWorkerUrl = () => { - let workerUrl = (globalThis as any)["workerUrl"]; - if (workerUrl) { - setWorkerUrl(workerUrl) - } + let workerUrl = (globalThis as any)["workerUrl"]; + if (workerUrl) { + setWorkerUrl(workerUrl) + } } // HACK: We save these to the global scope because it's the most reliable way to @@ -35,5 +43,5 @@ export const setDefaultWorkerUrl = () => { // HACK: some bundlers such as webpack uses this on dev mode. // We add this functions to allow dev mode work in those bundlers. -(globalThis as any).$RefreshReg$ = (globalThis as any).$RefreshReg$ || function () {/**/ }; -(globalThis as any).$RefreshSig$ = (globalThis as any).$RefreshSig$ || function () { return function () { } }; +(globalThis as any).$RefreshReg$ = (globalThis as any).$RefreshReg$ || function() {/**/ }; +(globalThis as any).$RefreshSig$ = (globalThis as any).$RefreshSig$ || function() { return function() { } }; diff --git a/rollup.config.mjs b/rollup.config.mjs index 42870056..b3f45895 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -1,5 +1,5 @@ import terser from "@rollup/plugin-terser"; -import pkg from "./package.json" assert { type: "json" }; +import pkg from "./package.json" with { type: "json" }; import dts from "rollup-plugin-dts"; import typescript from "@rollup/plugin-typescript"; import replace from "@rollup/plugin-replace"; diff --git a/src/registry/app.rs b/src/registry/app.rs new file mode 100644 index 00000000..a2034772 --- /dev/null +++ b/src/registry/app.rs @@ -0,0 +1,76 @@ +use wasm_bindgen::prelude::wasm_bindgen; +use wasmer_api::types::{DeployAppVersion, PublishDeployAppVars}; +use wasmer_config::app::AppConfigV1; + +use crate::{ + utils::{self, Error}, + Wasmer, +}; + +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct DeployedApp { + pub id: String, + pub created_at: String, + pub version: String, + pub description: Option, + pub yaml_config: String, + pub user_yaml_config: String, + pub config: String, + pub json_config: String, + pub url: String, +} + +impl From for DeployedApp { + fn from(value: DeployAppVersion) -> Self { + Self { + id: value.id.inner().to_string(), + created_at: value.created_at.0, + version: value.version, + description: value.description, + yaml_config: value.yaml_config, + user_yaml_config: value.user_yaml_config, + config: value.config, + json_config: value.json_config, + url: value.url, + } + } +} + +#[wasm_bindgen] +impl Wasmer { + /// Deploy an app to the registry. + #[wasm_bindgen(js_name = "deployApp")] + #[allow(non_snake_case)] + pub async fn deploy_app(appConfig: wasm_bindgen::JsValue) -> Result { + let default = js_sys::Reflect::get(&appConfig, &(String::from("default").into())) + .map_err(utils::js_error)? + .as_bool(); + let app_config = serde_wasm_bindgen::from_value(appConfig) + .map_err(|e| anyhow::anyhow!(e.to_string()))?; + Wasmer::deploy_app_inner(app_config, default).await + } +} + +impl Wasmer { + async fn deploy_app_inner( + app_config: AppConfigV1, + make_default: Option, + ) -> Result { + let client = Wasmer::get_client()?; + let config = app_config.clone().to_yaml()?; + + wasmer_api::query::publish_deploy_app( + client, + PublishDeployAppVars { + config, + name: app_config.name.into(), + owner: app_config.owner.map(Into::into), + make_default, + }, + ) + .await + .map(|v| v.into()) + .map_err(utils::Error::Rust) + } +} diff --git a/src/registry/mod.rs b/src/registry/mod.rs new file mode 100644 index 00000000..21b30711 --- /dev/null +++ b/src/registry/mod.rs @@ -0,0 +1,73 @@ +pub mod app; +pub mod package; + +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use wasmer_api::WasmerClient; + +use crate::{utils::Error, Wasmer}; + +static WASMER_CLIENT: std::sync::OnceLock = std::sync::OnceLock::new(); + +#[wasm_bindgen(getter_with_clone)] +pub struct RegistryConfig { + pub registry_url: String, + pub token: Option, +} + +impl Default for RegistryConfig { + fn default() -> Self { + Self { + registry_url: String::from("https://registry.wasmer.io/graphql"), + token: Default::default(), + } + } +} + +impl Wasmer { + pub fn get_client() -> Result<&'static WasmerClient, Error> { + WASMER_CLIENT.get_or_try_init(|| { + let registry_input = if let Some(registry_info) = + web_sys::window().and_then(|w| w.get("__WASMER_REGISTRY__")) + { + if registry_info.is_undefined() { + RegistryConfig::default() + } else { + let registry_url = js_sys::Reflect::get( + ®istry_info, + &JsValue::from(String::from("registry_url")), + ) + .ok() + .and_then(|u| u.as_string()); + let token = + js_sys::Reflect::get(®istry_info, &JsValue::from(String::from("token"))) + .ok() + .and_then(|u| u.as_string()); + + if let Some(registry_url) = registry_url { + RegistryConfig { + registry_url, + token, + } + } else { + RegistryConfig { + token, + ..Default::default() + } + } + } + } else { + RegistryConfig::default() + }; + + let mut client = wasmer_api::WasmerClient::new( + url::Url::parse(®istry_input.registry_url)?, + "Wasmer JS SDK", + )?; + if let Some(token) = registry_input.token { + client = client.with_auth_token(token); + } + + Ok(client) + }) + } +} diff --git a/src/registry/package.rs b/src/registry/package.rs new file mode 100644 index 00000000..982e7e35 --- /dev/null +++ b/src/registry/package.rs @@ -0,0 +1,344 @@ +use crate::{ + utils::{self, Error}, + Wasmer, +}; +use anyhow::Context; +use js_sys::{JsString, Math::random}; +use std::collections::BTreeMap; +use tokio::io::AsyncBufReadExt as _; +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use wasmer_config::package::Manifest; +use webc::{bytes::Bytes, wasmer_package::Strictness}; +use webc::{ + wasmer_package::{MemoryDir, MemoryFile, MemoryNode, MemoryVolume, Package}, + Timestamps, +}; + +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct WasmerPackage { + pub manifest: js_sys::Object, + pub data: Vec, +} + +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct Volume {} + +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct Atom {} + +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct PublishPackageOutput { + pub manifest: wasm_bindgen::JsValue, + pub hash: String, +} + +#[wasm_bindgen] +impl Wasmer { + /// Create a `WasmerPackage`. + #[wasm_bindgen(js_name = "createPackage")] + #[allow(non_snake_case)] + pub async fn createPackage(manifest: js_sys::Object) -> Result { + let volumes: BTreeMap = if let Ok(volumes) = + js_sys::Reflect::get(&manifest, &JsValue::from(String::from("fs"))) + { + js_sys::Reflect::delete_property(&manifest, &JsValue::from(String::from("fs"))) + .map_err(|e| anyhow::anyhow!("while deleting fs property: {e:?}"))?; + + if let Some(volumes) = js_sys::Object::try_from(&volumes) { + let volumes = create_volumes(volumes)?; + let fs = js_sys::Object::new(); + for (key, _) in volumes.iter() { + js_sys::Reflect::set( + &fs, + &JsString::from(key.as_str()).into(), + &JsString::from(key.as_str()).into(), + ) + .map_err(|e| anyhow::anyhow!("while setting fs property: {e:?}"))?; + } + + js_sys::Reflect::set(&manifest, &JsValue::from(String::from("fs")), &fs) + .map_err(|e| anyhow::anyhow!("while deleting fs property: {e:?}"))?; + + volumes + } else { + BTreeMap::default() + } + } else { + BTreeMap::default() + }; + + // let atoms: BTreeMap = if manifest.is_object() { + // if let Ok(atoms) = + // js_sys::Reflect::get(&manifest, &JsValue::from(String::from("module"))) + // { + // js_sys::Reflect::delete_property(&manifest, &JsValue::from(String::from("module"))) + // .map_err(|e| anyhow::anyhow!("while deleting module property: {e:?}"))?; + // let atoms: BTreeMap = serde_wasm_bindgen::from_value(atoms) + // .map_err(|e| anyhow::anyhow!(e.to_string()))?; + // let mut ret = BTreeMap::default(); + // for (k, v) in atoms.into_iter() { + // ret.insert(k, webc::compat::SharedBytes::from_bytes(v)); + // } + + // ret + // } else { + // BTreeMap::default() + // } + // } else { + // BTreeMap::default() + // }; + + let wasmer_manifest: Manifest = serde_wasm_bindgen::from_value(manifest.clone().into()) + .map_err(|e| anyhow::anyhow!(e.to_string()))?; + wasmer_manifest.validate()?; + + let pkg: Package = webc::wasmer_package::Package::from_in_memory( + wasmer_manifest, + volumes, + BTreeMap::default(), + Strictness::default(), + ) + .context("While parsing the manifest")?; + let data = pkg + .serialize() + .context("While validating the package")? + .to_vec(); + + Ok(WasmerPackage { manifest, data }) + } + + /// Publish a package to the registry. + #[wasm_bindgen(js_name = "publishPackage")] + #[allow(non_snake_case)] + pub async fn publishPackage( + wasmerPackage: WasmerPackage, + ) -> Result { + let manifest: Manifest = serde_wasm_bindgen::from_value(wasmerPackage.manifest.into()) + .map_err(|e| anyhow::anyhow!(e.to_string()))?; + Wasmer::publish_package_inner(manifest, wasmerPackage.data.into()).await + } +} + +impl Wasmer { + async fn publish_package_inner( + manifest: Manifest, + bytes: Bytes, + ) -> Result { + let client = Wasmer::get_client()?; + + let signed_url = wasmer_api::query::get_signed_url_for_package_upload( + client, + Some(60 * 30), + Some(format!("test-{}", random()).replace('.', "-")).as_deref(), + None, + None, + ) + .await? + .ok_or_else(|| anyhow::anyhow!("No signed url!"))? + .url; + + upload(bytes, &signed_url).await?; + + let (namespace, name) = + if let Some(full_name) = manifest.package.as_ref().and_then(|p| p.name.clone()) { + let splits: Vec = full_name.split('/').map(|s| s.to_string()).collect(); + ( + splits + .first() + .ok_or_else(|| anyhow::anyhow!("No namespace provided!"))? + .clone(), + splits.get(1).cloned(), + ) + } else { + return Err(utils::Error::Rust(anyhow::anyhow!( + "No namespace provided!" + ))); + }; + + let out = wasmer_api::query::push_package_release( + client, + name.as_deref(), + &namespace, + &signed_url, + manifest.package.as_ref().map(|p| p.private), + ) + .await? + .ok_or_else(|| anyhow::anyhow!("Backend returned no data!"))?; + + Ok(PublishPackageOutput { + manifest: serde_wasm_bindgen::to_value(&manifest) + .map_err(|e| anyhow::anyhow!(e.to_string()))?, + hash: out + .package_webc + .and_then(|p| p.webc_v3) + .map(|c| c.webc_sha256) + .ok_or_else(|| anyhow::anyhow!("No package was published!"))?, + }) + } +} + +// Upload a package to a signed url. +pub(super) async fn upload(bytes: Bytes, signed_url: &str) -> anyhow::Result { + let client = reqwest::Client::builder() + .default_headers(reqwest::header::HeaderMap::default()) + .build() + .unwrap(); + + let res = client + .request(reqwest::Method::POST, signed_url) + .header(reqwest::header::CONTENT_LENGTH, "0") + .header(reqwest::header::CONTENT_TYPE, "application/octet-stream") + .header("x-goog-resumable", "start") + .header(reqwest::header::ACCEPT, "*/*") + .header(reqwest::header::ACCESS_CONTROL_ALLOW_HEADERS, "*") + .header(reqwest::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .header(reqwest::header::ACCESS_CONTROL_ALLOW_METHODS, "*") + .header(reqwest::header::ACCESS_CONTROL_MAX_AGE, "43200"); + + let result = res.send().await?; + + if result.status() != reqwest::StatusCode::from_u16(201).unwrap() { + return Err(anyhow::anyhow!( + "Uploading package failed: got HTTP {:?} when uploading", + result.status() + )); + } + + let headers = result + .headers() + .into_iter() + .filter_map(|(k, v)| { + let k = k.to_string(); + let v = v.to_str().ok()?.to_string(); + Some((k.to_lowercase(), v)) + }) + .collect::>(); + + let session_uri = headers + .get("location") + .ok_or_else(|| { + anyhow::anyhow!("The upload server did not provide the upload URL correctly") + })? + .clone(); + + //* XXX: If the package is large this line may result in + // * a surge in memory use. + // * + // * In the future, we might want a way to stream bytes + // * from the webc instead of a complete in-memory + // * representation. + // */ + let total_bytes = bytes.len(); + + let chunk_size = 2_097_152; // 2MB + + let mut reader = tokio::io::BufReader::with_capacity(chunk_size, &bytes[..]); + let mut cursor = 0; + + while let Some(chunk) = reader.fill_buf().await.ok().map(|s| s.to_vec()) { + let n = chunk.len(); + + if chunk.is_empty() { + break; + } + + let start = cursor; + let end = cursor + chunk.len().saturating_sub(1); + let content_range = format!("bytes {start}-{end}/{total_bytes}"); + + let res = client + .put(&session_uri) + .header(reqwest::header::CONTENT_TYPE, "application/octet-stream") + .header(reqwest::header::CONTENT_LENGTH, format!("{}", chunk.len())) + .header("Content-Range".to_string(), content_range) + .body(chunk.to_vec()); + + let res = res.send().await; + res.map(|response| response.error_for_status()) + .map_err(|e| { + anyhow::anyhow!( + "cannot send request to {session_uri} (chunk {}..{}): {e}", + cursor, + cursor + chunk_size + ) + })??; + + if n < chunk_size { + break; + } + + reader.consume(n); + cursor += n; + } + Ok(signed_url.to_string()) +} + +fn create_volumes(volumes: &js_sys::Object) -> Result, Error> { + let mut ret = BTreeMap::default(); + for (key, value) in utils::object_entries(volumes)?.into_iter() { + ret.insert( + format!( + "/{}", + key.as_string() + .ok_or_else(|| anyhow::anyhow!("Error making a string out of the key!"))? + ), + create_memory_volume(value)?, + ); + } + Ok(ret) +} + +fn create_memory_volume(value: JsValue) -> Result { + let node = create_memory_node(value)?; + + match node { + MemoryNode::File(f) => Err(Error::Rust(anyhow::anyhow!( + "Cannot create a volume from the single file {f:?}!" + ))), + MemoryNode::Dir(node) => Ok(MemoryVolume { node }), + } +} + +fn create_memory_node(value: JsValue) -> Result { + if value.is_array() { + let data = js_sys::Uint8Array::from(value).to_vec(); + + let file = MemoryNode::File(MemoryFile { + metadata: Some(webc::Metadata::File { + timestamps: Some(Timestamps::default()), + length: data.len(), + }), + data, + }); + + Ok(file) + } else if value.is_object() { + let obj = js_sys::Object::try_from(&value) + .ok_or_else(|| anyhow::anyhow!("Cannot create a directory out of non-object value!"))?; + let mut nodes = BTreeMap::default(); + for (key, value) in utils::object_entries(obj)?.into_iter() { + nodes.insert( + key.as_string() + .ok_or_else(|| anyhow::anyhow!("Error making a string out of the key!"))?, + create_memory_node(value)?, + ); + } + + let dir = MemoryNode::Dir(MemoryDir { + metadata: Some(webc::Metadata::Dir { + timestamps: Some(Timestamps::default()), + }), + nodes, + }); + + Ok(dir) + } else { + Err(Error::Rust(anyhow::anyhow!( + "Cannot create a memory node out of {value:#?}" + ))) + } +} diff --git a/tests/registry.test.ts b/tests/registry.test.ts new file mode 100644 index 00000000..a6b949ac --- /dev/null +++ b/tests/registry.test.ts @@ -0,0 +1,192 @@ +import { assert, expect } from "@esm-bundle/chai"; +import { init, initializeLogger, Wasmer } from ".."; + +const initialized = (async () => { + await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url), undefined, { + registry_url: "https://registry.wasmer.wtf/graphql", token: + "wap_8cd3923aa31a0094c916ec6d54e6cd1f410fdfebf9a007a505e87ed509dbc049" + }); + initializeLogger("error"); +})(); + +describe("Registry", function() { + this.timeout("60s").beforeAll(async () => await initialized); + + it("Has global context", async () => { + let v = (globalThis as any)["__WASMER_REGISTRY__"]; + expect(typeof v != "undefined") + expect(v.registry_url === "https://registry.wasmer.io/graphql") + expect(v.token === "wap_e7ab42cd40f6c7c2232eeb16a29aad3e267eb63be67747121e20942559b0aaf5") + }); + + it("can create a package", async () => { + + let manifest = { + "command": [ + { + "module": "wasmer/static-web-server:webserver", + "name": "script", + "runner": "https://webc.org/runner/wasi", + "annotations": { + "wasi": { + "main-args": [ + "-w", + "/settings/config.toml" + ] + } + } + } + ], + "fs": { + "public": { + "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], + "inner": { + "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], + } + } + }, + "dependencies": { + "wasmer/static-web-server": "^1" + }, + } + + let result = await Wasmer.createPackage(manifest); + + console.log("\n== Creating a package == "); + console.log(result.manifest) + }) + + it("can publish packages", async () => { + let manifest = { + "package": { + "name": "edoardo/my-package" + }, + + "command": [ + { + "module": "wasmer/static-web-server:webserver", + "name": "script", + "runner": "https://webc.org/runner/wasi", + "annotations": { + "wasi": { + "main-args": [ + "-w", + "/settings/config.toml" + ] + } + } + } + ], + "fs": { + "public": { + "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], + "inner": { + "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], + } + } + }, + "dependencies": { + "wasmer/static-web-server": "^1" + }, + } + + let wasmerPackage = await Wasmer.createPackage(manifest); + let result = await Wasmer.publishPackage(wasmerPackage); + + console.log("\n== Publishing a package == "); + console.log("Published package hash: ", result.hash); + console.log("Published package manifest: ", result.manifest); + }) + + it("can deploy apps", async () => { + let appConfig = + { + name: "my-awesome-app", + owner: "edoardo", + package: "sha256:e1f3be3d017a049aefa2be984f7352b2d901d73fd6daaba2d2c1643a4196cfaa", + env: [["test", "new_value"]], + default: true + + }; + + let result = await Wasmer.deployApp(appConfig); + + console.log("== Deploying an app == "); + console.log("Deployed app id: ", result.id); + console.log("Deployed app url: ", result.url); + }) + + + it("can run user-created packages", async () => { + let manifest = { + "command": [ + { + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-c", + "print(\"hello, js!\")" + ] + } + } + } + ], + "dependencies": { + "wasmer/python": "3.12.9+build.9" + } + }; + + let wasmerPackage = await Wasmer.createPackage(manifest); + let pkg = await Wasmer.fromFile(wasmerPackage.data); + let instance = await pkg.commands["hello"].run(); + + const output = await instance.wait(); + assert(output.stdout === "hello, js!\n") + }) + + + + it("can mount fs", async () => { + let manifest = + { + "command": [ + { + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')])" + ] + } + } + } + ], + "dependencies": { + "wasmer/python": "3.12.9+build.9" + }, + + "fs": { + "public": { + "index.html": Array.from("Hello, js!") + } + } + } + + + let wasmerPackage = await Wasmer.createPackage(manifest); + let pkg = await Wasmer.fromFile(wasmerPackage.data); + let instance = await pkg.commands["hello"].run(); + + const output = await instance.wait(); + console.log("\n== Mounting and running user-defined fs == "); + console.log(manifest); + console.log("Does this contain 'index.html'?", output.stdout) + assert(output.stdout.includes("index.html")) + }) +}); From 21c8ff8f81571ce3510b61b39aed7e897cc8e990 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Thu, 11 Jul 2024 15:09:37 +0200 Subject: [PATCH 03/32] feat: Use same `Wasmer` type as output of created packages, update tests --- Cargo.lock | 9 +- Cargo.toml | 3 +- src/registry/app.rs | 18 +++- src/registry/package.rs | 40 +++++---- src/wasmer.rs | 43 +++++++++- tests/registry.test.ts | 186 ++++++++++++++++++++++++---------------- 6 files changed, 204 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 533aa038..24f851c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3512,6 +3512,7 @@ dependencies = [ [[package]] name = "wasmer-api" version = "0.0.30" +source = "git+https://github.com/wasmerio/wasmer?branch=backend-api-wasm32#17b77586782c657610e2cc329b1c4b6ae0924bed" dependencies = [ "anyhow", "cynic", @@ -3529,7 +3530,7 @@ dependencies = [ "tokio", "tracing", "url", - "wasmer-config 0.4.0", + "wasmer-config 0.4.0 (git+https://github.com/wasmerio/wasmer?branch=backend-api-wasm32)", "webc", ] @@ -3565,6 +3566,8 @@ dependencies = [ [[package]] name = "wasmer-config" version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b4a632496950fde9ad821e195ef1a301440076f7c7d80de55239a140359bcbd" dependencies = [ "anyhow", "bytesize", @@ -3585,8 +3588,7 @@ dependencies = [ [[package]] name = "wasmer-config" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b4a632496950fde9ad821e195ef1a301440076f7c7d80de55239a140359bcbd" +source = "git+https://github.com/wasmerio/wasmer?branch=backend-api-wasm32#17b77586782c657610e2cc329b1c4b6ae0924bed" dependencies = [ "anyhow", "bytesize", @@ -3660,6 +3662,7 @@ dependencies = [ "reqwest 0.12.5", "serde", "serde-wasm-bindgen 0.6.3", + "sha2", "tempfile", "tokio", "toml 0.8.14", diff --git a/Cargo.toml b/Cargo.toml index 514a1436..61ca841c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ once_cell = "1" reqwest = { version = "0.12.5", features = ["stream"] } serde = { version = "1", features = ["derive"] } serde-wasm-bindgen = "0.6" +sha2 = "0.10.8" tempfile = "3.10.1" tokio = { version = "1", features = ["sync"], default_features = false } toml = "0.8.14" @@ -36,7 +37,7 @@ wasm-bindgen-derive = "0.2.1" wasm-bindgen-futures = "0.4" wasm-bindgen-test = "0.3.37" wasmer = { version = "4.3.2", default-features = false, features = ["js", "js-default", "wasm-types-polyfill", "enable-serde"] } -wasmer-api = { version = "0.0.30", path = "../wasmer/lib/backend-api" } +wasmer-api = { version = "0.0.30", git = "https://github.com/wasmerio/wasmer", branch = "backend-api-wasm32"} wasmer-config = "0.4.0" wasmer-types = "4.3.2" wasmer-wasix = { version = "0.22.0", default-features = false, features = ["js", "js-default"] } diff --git a/src/registry/app.rs b/src/registry/app.rs index a2034772..e209c907 100644 --- a/src/registry/app.rs +++ b/src/registry/app.rs @@ -1,4 +1,4 @@ -use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; use wasmer_api::types::{DeployAppVersion, PublishDeployAppVars}; use wasmer_config::app::AppConfigV1; @@ -42,7 +42,21 @@ impl Wasmer { /// Deploy an app to the registry. #[wasm_bindgen(js_name = "deployApp")] #[allow(non_snake_case)] - pub async fn deploy_app(appConfig: wasm_bindgen::JsValue) -> Result { + pub async fn deploy_app(appConfig: JsValue, pkg: Option) -> Result { + let maybe_app_pkg = js_sys::Reflect::get(&appConfig, &(String::from("package").into())); + let maybe_wasmer_pkg = pkg.and_then(|p| p.pkg); + + let pkg = match (maybe_app_pkg, maybe_wasmer_pkg) { + (Ok(pkg), None) => pkg.as_string().unwrap(), + (_, Some(p)) => p.hash.to_string(), + (Err(_), None) => Err(Error::Rust(anyhow::anyhow!("No package identified!")))?, + }; + + js_sys::Reflect::set(&appConfig, &(String::from("package").into()), &(pkg.into())) + .map_err(|e| { + anyhow::anyhow!("While setting the 'package' field in the app config: {e:?}") + })?; + let default = js_sys::Reflect::get(&appConfig, &(String::from("default").into())) .map_err(utils::js_error)? .as_bool(); diff --git a/src/registry/package.rs b/src/registry/package.rs index 982e7e35..40846083 100644 --- a/src/registry/package.rs +++ b/src/registry/package.rs @@ -1,5 +1,6 @@ use crate::{ utils::{self, Error}, + wasmer::OptionalRuntime, Wasmer, }; use anyhow::Context; @@ -41,7 +42,7 @@ impl Wasmer { /// Create a `WasmerPackage`. #[wasm_bindgen(js_name = "createPackage")] #[allow(non_snake_case)] - pub async fn createPackage(manifest: js_sys::Object) -> Result { + pub async fn createPackage(manifest: js_sys::Object) -> Result { let volumes: BTreeMap = if let Ok(volumes) = js_sys::Reflect::get(&manifest, &JsValue::from(String::from("fs"))) { @@ -97,36 +98,34 @@ impl Wasmer { wasmer_manifest.validate()?; let pkg: Package = webc::wasmer_package::Package::from_in_memory( - wasmer_manifest, + wasmer_manifest.clone(), volumes, BTreeMap::default(), Strictness::default(), ) .context("While parsing the manifest")?; - let data = pkg - .serialize() - .context("While validating the package")? - .to_vec(); - Ok(WasmerPackage { manifest, data }) + let runtime = OptionalRuntime::default().resolve()?.into_inner(); + Wasmer::from_user_package(pkg, wasmer_manifest, runtime).await } /// Publish a package to the registry. #[wasm_bindgen(js_name = "publishPackage")] #[allow(non_snake_case)] - pub async fn publishPackage( - wasmerPackage: WasmerPackage, - ) -> Result { - let manifest: Manifest = serde_wasm_bindgen::from_value(wasmerPackage.manifest.into()) - .map_err(|e| anyhow::anyhow!(e.to_string()))?; - Wasmer::publish_package_inner(manifest, wasmerPackage.data.into()).await + pub async fn publishPackage(wasmerPackage: Wasmer) -> Result { + match wasmerPackage.pkg { + Some(p) => Wasmer::publish_package_inner(p.manifest.clone(), p.data).await, + None => Err(Error::Rust(anyhow::anyhow!( + "The selected package has no container!" + ))), + } } } impl Wasmer { async fn publish_package_inner( manifest: Manifest, - bytes: Bytes, + bytes: bytes::Bytes, ) -> Result { let client = Wasmer::get_client()?; @@ -304,7 +303,18 @@ fn create_memory_volume(value: JsValue) -> Result { } fn create_memory_node(value: JsValue) -> Result { - if value.is_array() { + if value.is_string() { + let data = value.as_string().unwrap().as_bytes().to_vec(); + let file = MemoryNode::File(MemoryFile { + metadata: Some(webc::Metadata::File { + timestamps: Some(Timestamps::default()), + length: data.len(), + }), + data, + }); + + Ok(file) + } else if value.is_array() { let data = js_sys::Uint8Array::from(value).to_vec(); let file = MemoryNode::File(MemoryFile { diff --git a/src/wasmer.rs b/src/wasmer.rs index dca7d1be..ccc178db 100644 --- a/src/wasmer.rs +++ b/src/wasmer.rs @@ -1,8 +1,10 @@ use std::{str::FromStr, sync::Arc}; -use bytes::BytesMut; +use anyhow::Context; +use bytes::{Bytes, BytesMut}; use futures::{channel::oneshot, TryStreamExt}; use js_sys::{JsString, Reflect, Uint8Array}; +use sha2::Digest; use tracing::Instrument; use virtual_fs::{AsyncReadExt, Pipe}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue, UnwrapThrowExt}; @@ -14,6 +16,7 @@ use wasmer_wasix::{ Runtime as _, }; use web_sys::{ReadableStream, WritableStream}; +use webc::wasmer_package::Package; use crate::{ instance::ExitCondition, @@ -48,6 +51,16 @@ pub struct Wasmer { /// dependencies). #[wasm_bindgen(getter_with_clone)] pub commands: Commands, + + pub(crate) pkg: Option, +} + +#[derive(Debug, Clone)] +#[wasm_bindgen] +pub(crate) struct UserPackageDefinition { + pub(crate) manifest: wasmer_config::package::Manifest, + pub(crate) data: bytes::Bytes, + pub(crate) hash: String, } #[wasm_bindgen] @@ -117,8 +130,34 @@ impl Wasmer { Ok(Wasmer { entrypoint, commands, + pkg: None, }) } + + pub(crate) async fn from_user_package( + pkg: Package, + manifest: wasmer_config::package::Manifest, + runtime: Arc, + ) -> Result { + let data: Bytes = pkg + .serialize() + .context("While validating the package")? + .to_vec() + .into(); + + let hash = sha2::Sha256::digest(&data).into(); + let hash = wasmer_config::package::PackageHash::from_sha256_bytes(hash); + let hash = hash.to_string(); + let container = webc::Container::from_bytes(data.clone())?; + let bin_pkg = BinaryPackage::from_webc(&container, &*runtime).await?; + let mut ret = Wasmer::from_package(bin_pkg, runtime)?; + ret.pkg = Some(UserPackageDefinition { + manifest, + data, + hash, + }); + Ok(ret) + } } /// A runnable WASIX command. @@ -183,7 +222,7 @@ extern "C" { } impl OptionalRuntime { - fn resolve(&self) -> Result { + pub(crate) fn resolve(&self) -> Result { let js_value: &JsValue = self.as_ref(); if js_value.is_undefined() { diff --git a/tests/registry.test.ts b/tests/registry.test.ts index a6b949ac..e63b4f65 100644 --- a/tests/registry.test.ts +++ b/tests/registry.test.ts @@ -2,10 +2,7 @@ import { assert, expect } from "@esm-bundle/chai"; import { init, initializeLogger, Wasmer } from ".."; const initialized = (async () => { - await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url), undefined, { - registry_url: "https://registry.wasmer.wtf/graphql", token: - "wap_8cd3923aa31a0094c916ec6d54e6cd1f410fdfebf9a007a505e87ed509dbc049" - }); + await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url)); initializeLogger("error"); })(); @@ -15,8 +12,6 @@ describe("Registry", function() { it("Has global context", async () => { let v = (globalThis as any)["__WASMER_REGISTRY__"]; expect(typeof v != "undefined") - expect(v.registry_url === "https://registry.wasmer.io/graphql") - expect(v.token === "wap_e7ab42cd40f6c7c2232eeb16a29aad3e267eb63be67747121e20942559b0aaf5") }); it("can create a package", async () => { @@ -53,68 +48,117 @@ describe("Registry", function() { let result = await Wasmer.createPackage(manifest); console.log("\n== Creating a package == "); - console.log(result.manifest) + console.log(result.commands) }) - it("can publish packages", async () => { - let manifest = { - "package": { - "name": "edoardo/my-package" - }, - - "command": [ - { - "module": "wasmer/static-web-server:webserver", - "name": "script", - "runner": "https://webc.org/runner/wasi", - "annotations": { - "wasi": { - "main-args": [ - "-w", - "/settings/config.toml" - ] - } - } - } - ], - "fs": { - "public": { - "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], - "inner": { - "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], - } - } - }, - "dependencies": { - "wasmer/static-web-server": "^1" - }, - } - - let wasmerPackage = await Wasmer.createPackage(manifest); - let result = await Wasmer.publishPackage(wasmerPackage); - - console.log("\n== Publishing a package == "); - console.log("Published package hash: ", result.hash); - console.log("Published package manifest: ", result.manifest); - }) - - it("can deploy apps", async () => { - let appConfig = - { - name: "my-awesome-app", - owner: "edoardo", - package: "sha256:e1f3be3d017a049aefa2be984f7352b2d901d73fd6daaba2d2c1643a4196cfaa", - env: [["test", "new_value"]], - default: true - - }; - - let result = await Wasmer.deployApp(appConfig); - - console.log("== Deploying an app == "); - console.log("Deployed app id: ", result.id); - console.log("Deployed app url: ", result.url); - }) + //it("can publish packages", async () => { + // let manifest = { + // "package": { + // "name": "/my-package" + // }, + // "command": [ + // { + // "module": "wasmer/static-web-server:webserver", + // "name": "script", + // "runner": "https://webc.org/runner/wasi", + // "annotations": { + // "wasi": { + // "main-args": [ + // "-w", + // "/settings/config.toml" + // ] + // } + // } + // } + // ], + // "fs": { + // "public": { + // "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], + // "inner": { + // "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], + // } + // } + // }, + // "dependencies": { + // "wasmer/static-web-server": "^1" + // }, + // } + // let wasmerPackage = await Wasmer.createPackage(manifest); + // let result = await Wasmer.publishPackage(wasmerPackage); + // console.log("\n== Publishing a package == "); + // console.log("Published package hash: ", result.hash); + // console.log("Published package manifest: ", result.manifest); + //}) + + ////it("can deploy apps", async () => { + // let appConfig = + // { + // name: "my-awesome-app", + // owner: "", + // package: "sha256:e1f3be3d017a049aefa2be984f7352b2d901d73fd6daaba2d2c1643a4196cfaa", + // env: [["test", "new_value"]], + // default: true + + // }; + + // let result = await Wasmer.deployApp(appConfig); + + // console.log("\n== Deploying an app == "); + // console.log("Deployed app id: ", result.id); + // console.log("Deployed app url: ", result.url); + //}) + + //it("can deploy apps with user-created packages", async () => { + // let manifest = { + // "package": { + // "name": "/my-package" + // }, + + // "command": [ + // { + // "module": "wasmer/static-web-server:webserver", + // "name": "script", + // "runner": "https://webc.org/runner/wasi", + // "annotations": { + // "wasi": { + // "main-args": [ + // "-w", + // "/settings/config.toml" + // ] + // } + // } + // } + // ], + // "fs": { + // "public": { + // "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], + // "inner": { + // "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], + // } + // } + // }, + // "dependencies": { + // "wasmer/static-web-server": "^1" + // }, + // } + + // let wasmerPackage = await Wasmer.createPackage(manifest); + + // let appConfig = + // { + // name: "my-awesome-app", + // owner: "", + // env: [["test", "new_value"]], + // default: true + + // }; + + // let result = await Wasmer.deployApp(appConfig, wasmerPackage); + + // console.log("\n== Deploying user-created app =="); + // console.log("Deployed app id: ", result.id); + // console.log("Deployed app url: ", result.url); + //}) it("can run user-created packages", async () => { @@ -139,8 +183,7 @@ describe("Registry", function() { } }; - let wasmerPackage = await Wasmer.createPackage(manifest); - let pkg = await Wasmer.fromFile(wasmerPackage.data); + let pkg = await Wasmer.createPackage(manifest); let instance = await pkg.commands["hello"].run(); const output = await instance.wait(); @@ -161,7 +204,7 @@ describe("Registry", function() { "wasi": { "main-args": [ "-c", - "import os; print([f for f in os.walk('/public')])" + "import os; print([f for f in os.walk('/public')]); " ] } } @@ -173,14 +216,13 @@ describe("Registry", function() { "fs": { "public": { - "index.html": Array.from("Hello, js!") + "index.html": "Hello, js!" } } } - let wasmerPackage = await Wasmer.createPackage(manifest); - let pkg = await Wasmer.fromFile(wasmerPackage.data); + let pkg = await Wasmer.createPackage(manifest); let instance = await pkg.commands["hello"].run(); const output = await instance.wait(); From 5d937852d4ab385d9336d9d2b371e76b02569698 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Thu, 11 Jul 2024 15:09:59 +0200 Subject: [PATCH 04/32] chore: Formatting --- src/tasks/task_wasm.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/tasks/task_wasm.rs b/src/tasks/task_wasm.rs index a9f05b04..5dfd050c 100644 --- a/src/tasks/task_wasm.rs +++ b/src/tasks/task_wasm.rs @@ -302,16 +302,21 @@ fn build_ctx_and_store( } }; - let (ctx, store) = - match WasiFunctionEnv::new_with_store(module, env, store_snapshot.as_ref(), spawn_type, update_layout) { - Ok(a) => a, - Err(err) => { - tracing::error!( - error = &err as &dyn std::error::Error, - "Failed to crate wasi context", - ); - return None; - } - }; + let (ctx, store) = match WasiFunctionEnv::new_with_store( + module, + env, + store_snapshot.as_ref(), + spawn_type, + update_layout, + ) { + Ok(a) => a, + Err(err) => { + tracing::error!( + error = &err as &dyn std::error::Error, + "Failed to crate wasi context", + ); + return None; + } + }; Some((ctx, store)) } From a16acfac5e19a412d7e365e721b4148753f06d0d Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Tue, 23 Jul 2024 18:54:09 +0200 Subject: [PATCH 05/32] feat: Allow users to specify metadata in various formats --- src/registry/app.rs | 57 ++- src/registry/package.rs | 354 ------------------ src/registry/package/mod.rs | 140 +++++++ .../package/package_utils/mem_volumes.rs | 215 +++++++++++ src/registry/package/package_utils/mod.rs | 5 + src/registry/package/package_utils/upload.rs | 99 +++++ src/wasmer.rs | 8 +- tests/registry.test.ts | 300 ++++++++------- 8 files changed, 682 insertions(+), 496 deletions(-) delete mode 100644 src/registry/package.rs create mode 100644 src/registry/package/mod.rs create mode 100644 src/registry/package/package_utils/mem_volumes.rs create mode 100644 src/registry/package/package_utils/mod.rs create mode 100644 src/registry/package/package_utils/upload.rs diff --git a/src/registry/app.rs b/src/registry/app.rs index e209c907..54d754c6 100644 --- a/src/registry/app.rs +++ b/src/registry/app.rs @@ -37,31 +37,60 @@ impl From for DeployedApp { } } +fn resolve_pkg(app_config: &JsValue) -> anyhow::Result<()> { + // The app config must have a 'package' field. + // + // The package field can either be a raw string or a [`Wasmer`]. + + let package_key = JsValue::from_str("package"); + let package = js_sys::Reflect::get(app_config, &package_key).map_err(|e| { + anyhow::anyhow!("While trying to get the package field from the app config: {e:?}") + })?; + + if package.is_undefined() { + anyhow::bail!( + "While trying to get the package field from the app config: undefined field name" + ) + } + + tracing::error!("{package:?}"); + + let pkg_key = JsValue::from_str("pkg"); + + if package.is_string() { + // Do nothing + Ok(()) + } else if js_sys::Reflect::has(&package, &pkg_key).map_err(|e| anyhow::anyhow!("{e:?}"))? { + let pkg = js_sys::Reflect::get(&package, &pkg_key).unwrap(); + let hash_key = JsValue::from_str("hash"); + if js_sys::Reflect::has(&pkg, &hash_key).map_err(|e| anyhow::anyhow!("{e:?}"))? { + let hash = js_sys::Reflect::get(&pkg, &hash_key).unwrap(); + js_sys::Reflect::set(app_config, &package_key, &hash) + .map_err(|e| anyhow::anyhow!("{e:?}"))?; + + Ok(()) + } else { + anyhow::bail!("No package information provided! Set the 'package'") + } + } else { + anyhow::bail!("No package information provided! Set the 'package'") + } +} + #[wasm_bindgen] impl Wasmer { /// Deploy an app to the registry. #[wasm_bindgen(js_name = "deployApp")] #[allow(non_snake_case)] - pub async fn deploy_app(appConfig: JsValue, pkg: Option) -> Result { - let maybe_app_pkg = js_sys::Reflect::get(&appConfig, &(String::from("package").into())); - let maybe_wasmer_pkg = pkg.and_then(|p| p.pkg); - - let pkg = match (maybe_app_pkg, maybe_wasmer_pkg) { - (Ok(pkg), None) => pkg.as_string().unwrap(), - (_, Some(p)) => p.hash.to_string(), - (Err(_), None) => Err(Error::Rust(anyhow::anyhow!("No package identified!")))?, - }; - - js_sys::Reflect::set(&appConfig, &(String::from("package").into()), &(pkg.into())) - .map_err(|e| { - anyhow::anyhow!("While setting the 'package' field in the app config: {e:?}") - })?; + pub async fn deploy_app(appConfig: JsValue) -> Result { + resolve_pkg(&appConfig)?; let default = js_sys::Reflect::get(&appConfig, &(String::from("default").into())) .map_err(utils::js_error)? .as_bool(); let app_config = serde_wasm_bindgen::from_value(appConfig) .map_err(|e| anyhow::anyhow!(e.to_string()))?; + Wasmer::deploy_app_inner(app_config, default).await } } diff --git a/src/registry/package.rs b/src/registry/package.rs deleted file mode 100644 index 40846083..00000000 --- a/src/registry/package.rs +++ /dev/null @@ -1,354 +0,0 @@ -use crate::{ - utils::{self, Error}, - wasmer::OptionalRuntime, - Wasmer, -}; -use anyhow::Context; -use js_sys::{JsString, Math::random}; -use std::collections::BTreeMap; -use tokio::io::AsyncBufReadExt as _; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; -use wasmer_config::package::Manifest; -use webc::{bytes::Bytes, wasmer_package::Strictness}; -use webc::{ - wasmer_package::{MemoryDir, MemoryFile, MemoryNode, MemoryVolume, Package}, - Timestamps, -}; - -#[wasm_bindgen(getter_with_clone)] -#[derive(Debug, Clone)] -pub struct WasmerPackage { - pub manifest: js_sys::Object, - pub data: Vec, -} - -#[wasm_bindgen(getter_with_clone)] -#[derive(Debug, Clone)] -pub struct Volume {} - -#[wasm_bindgen(getter_with_clone)] -#[derive(Debug, Clone)] -pub struct Atom {} - -#[wasm_bindgen(getter_with_clone)] -#[derive(Debug, Clone)] -pub struct PublishPackageOutput { - pub manifest: wasm_bindgen::JsValue, - pub hash: String, -} - -#[wasm_bindgen] -impl Wasmer { - /// Create a `WasmerPackage`. - #[wasm_bindgen(js_name = "createPackage")] - #[allow(non_snake_case)] - pub async fn createPackage(manifest: js_sys::Object) -> Result { - let volumes: BTreeMap = if let Ok(volumes) = - js_sys::Reflect::get(&manifest, &JsValue::from(String::from("fs"))) - { - js_sys::Reflect::delete_property(&manifest, &JsValue::from(String::from("fs"))) - .map_err(|e| anyhow::anyhow!("while deleting fs property: {e:?}"))?; - - if let Some(volumes) = js_sys::Object::try_from(&volumes) { - let volumes = create_volumes(volumes)?; - let fs = js_sys::Object::new(); - for (key, _) in volumes.iter() { - js_sys::Reflect::set( - &fs, - &JsString::from(key.as_str()).into(), - &JsString::from(key.as_str()).into(), - ) - .map_err(|e| anyhow::anyhow!("while setting fs property: {e:?}"))?; - } - - js_sys::Reflect::set(&manifest, &JsValue::from(String::from("fs")), &fs) - .map_err(|e| anyhow::anyhow!("while deleting fs property: {e:?}"))?; - - volumes - } else { - BTreeMap::default() - } - } else { - BTreeMap::default() - }; - - // let atoms: BTreeMap = if manifest.is_object() { - // if let Ok(atoms) = - // js_sys::Reflect::get(&manifest, &JsValue::from(String::from("module"))) - // { - // js_sys::Reflect::delete_property(&manifest, &JsValue::from(String::from("module"))) - // .map_err(|e| anyhow::anyhow!("while deleting module property: {e:?}"))?; - // let atoms: BTreeMap = serde_wasm_bindgen::from_value(atoms) - // .map_err(|e| anyhow::anyhow!(e.to_string()))?; - // let mut ret = BTreeMap::default(); - // for (k, v) in atoms.into_iter() { - // ret.insert(k, webc::compat::SharedBytes::from_bytes(v)); - // } - - // ret - // } else { - // BTreeMap::default() - // } - // } else { - // BTreeMap::default() - // }; - - let wasmer_manifest: Manifest = serde_wasm_bindgen::from_value(manifest.clone().into()) - .map_err(|e| anyhow::anyhow!(e.to_string()))?; - wasmer_manifest.validate()?; - - let pkg: Package = webc::wasmer_package::Package::from_in_memory( - wasmer_manifest.clone(), - volumes, - BTreeMap::default(), - Strictness::default(), - ) - .context("While parsing the manifest")?; - - let runtime = OptionalRuntime::default().resolve()?.into_inner(); - Wasmer::from_user_package(pkg, wasmer_manifest, runtime).await - } - - /// Publish a package to the registry. - #[wasm_bindgen(js_name = "publishPackage")] - #[allow(non_snake_case)] - pub async fn publishPackage(wasmerPackage: Wasmer) -> Result { - match wasmerPackage.pkg { - Some(p) => Wasmer::publish_package_inner(p.manifest.clone(), p.data).await, - None => Err(Error::Rust(anyhow::anyhow!( - "The selected package has no container!" - ))), - } - } -} - -impl Wasmer { - async fn publish_package_inner( - manifest: Manifest, - bytes: bytes::Bytes, - ) -> Result { - let client = Wasmer::get_client()?; - - let signed_url = wasmer_api::query::get_signed_url_for_package_upload( - client, - Some(60 * 30), - Some(format!("test-{}", random()).replace('.', "-")).as_deref(), - None, - None, - ) - .await? - .ok_or_else(|| anyhow::anyhow!("No signed url!"))? - .url; - - upload(bytes, &signed_url).await?; - - let (namespace, name) = - if let Some(full_name) = manifest.package.as_ref().and_then(|p| p.name.clone()) { - let splits: Vec = full_name.split('/').map(|s| s.to_string()).collect(); - ( - splits - .first() - .ok_or_else(|| anyhow::anyhow!("No namespace provided!"))? - .clone(), - splits.get(1).cloned(), - ) - } else { - return Err(utils::Error::Rust(anyhow::anyhow!( - "No namespace provided!" - ))); - }; - - let out = wasmer_api::query::push_package_release( - client, - name.as_deref(), - &namespace, - &signed_url, - manifest.package.as_ref().map(|p| p.private), - ) - .await? - .ok_or_else(|| anyhow::anyhow!("Backend returned no data!"))?; - - Ok(PublishPackageOutput { - manifest: serde_wasm_bindgen::to_value(&manifest) - .map_err(|e| anyhow::anyhow!(e.to_string()))?, - hash: out - .package_webc - .and_then(|p| p.webc_v3) - .map(|c| c.webc_sha256) - .ok_or_else(|| anyhow::anyhow!("No package was published!"))?, - }) - } -} - -// Upload a package to a signed url. -pub(super) async fn upload(bytes: Bytes, signed_url: &str) -> anyhow::Result { - let client = reqwest::Client::builder() - .default_headers(reqwest::header::HeaderMap::default()) - .build() - .unwrap(); - - let res = client - .request(reqwest::Method::POST, signed_url) - .header(reqwest::header::CONTENT_LENGTH, "0") - .header(reqwest::header::CONTENT_TYPE, "application/octet-stream") - .header("x-goog-resumable", "start") - .header(reqwest::header::ACCEPT, "*/*") - .header(reqwest::header::ACCESS_CONTROL_ALLOW_HEADERS, "*") - .header(reqwest::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*") - .header(reqwest::header::ACCESS_CONTROL_ALLOW_METHODS, "*") - .header(reqwest::header::ACCESS_CONTROL_MAX_AGE, "43200"); - - let result = res.send().await?; - - if result.status() != reqwest::StatusCode::from_u16(201).unwrap() { - return Err(anyhow::anyhow!( - "Uploading package failed: got HTTP {:?} when uploading", - result.status() - )); - } - - let headers = result - .headers() - .into_iter() - .filter_map(|(k, v)| { - let k = k.to_string(); - let v = v.to_str().ok()?.to_string(); - Some((k.to_lowercase(), v)) - }) - .collect::>(); - - let session_uri = headers - .get("location") - .ok_or_else(|| { - anyhow::anyhow!("The upload server did not provide the upload URL correctly") - })? - .clone(); - - //* XXX: If the package is large this line may result in - // * a surge in memory use. - // * - // * In the future, we might want a way to stream bytes - // * from the webc instead of a complete in-memory - // * representation. - // */ - let total_bytes = bytes.len(); - - let chunk_size = 2_097_152; // 2MB - - let mut reader = tokio::io::BufReader::with_capacity(chunk_size, &bytes[..]); - let mut cursor = 0; - - while let Some(chunk) = reader.fill_buf().await.ok().map(|s| s.to_vec()) { - let n = chunk.len(); - - if chunk.is_empty() { - break; - } - - let start = cursor; - let end = cursor + chunk.len().saturating_sub(1); - let content_range = format!("bytes {start}-{end}/{total_bytes}"); - - let res = client - .put(&session_uri) - .header(reqwest::header::CONTENT_TYPE, "application/octet-stream") - .header(reqwest::header::CONTENT_LENGTH, format!("{}", chunk.len())) - .header("Content-Range".to_string(), content_range) - .body(chunk.to_vec()); - - let res = res.send().await; - res.map(|response| response.error_for_status()) - .map_err(|e| { - anyhow::anyhow!( - "cannot send request to {session_uri} (chunk {}..{}): {e}", - cursor, - cursor + chunk_size - ) - })??; - - if n < chunk_size { - break; - } - - reader.consume(n); - cursor += n; - } - Ok(signed_url.to_string()) -} - -fn create_volumes(volumes: &js_sys::Object) -> Result, Error> { - let mut ret = BTreeMap::default(); - for (key, value) in utils::object_entries(volumes)?.into_iter() { - ret.insert( - format!( - "/{}", - key.as_string() - .ok_or_else(|| anyhow::anyhow!("Error making a string out of the key!"))? - ), - create_memory_volume(value)?, - ); - } - Ok(ret) -} - -fn create_memory_volume(value: JsValue) -> Result { - let node = create_memory_node(value)?; - - match node { - MemoryNode::File(f) => Err(Error::Rust(anyhow::anyhow!( - "Cannot create a volume from the single file {f:?}!" - ))), - MemoryNode::Dir(node) => Ok(MemoryVolume { node }), - } -} - -fn create_memory_node(value: JsValue) -> Result { - if value.is_string() { - let data = value.as_string().unwrap().as_bytes().to_vec(); - let file = MemoryNode::File(MemoryFile { - metadata: Some(webc::Metadata::File { - timestamps: Some(Timestamps::default()), - length: data.len(), - }), - data, - }); - - Ok(file) - } else if value.is_array() { - let data = js_sys::Uint8Array::from(value).to_vec(); - - let file = MemoryNode::File(MemoryFile { - metadata: Some(webc::Metadata::File { - timestamps: Some(Timestamps::default()), - length: data.len(), - }), - data, - }); - - Ok(file) - } else if value.is_object() { - let obj = js_sys::Object::try_from(&value) - .ok_or_else(|| anyhow::anyhow!("Cannot create a directory out of non-object value!"))?; - let mut nodes = BTreeMap::default(); - for (key, value) in utils::object_entries(obj)?.into_iter() { - nodes.insert( - key.as_string() - .ok_or_else(|| anyhow::anyhow!("Error making a string out of the key!"))?, - create_memory_node(value)?, - ); - } - - let dir = MemoryNode::Dir(MemoryDir { - metadata: Some(webc::Metadata::Dir { - timestamps: Some(Timestamps::default()), - }), - nodes, - }); - - Ok(dir) - } else { - Err(Error::Rust(anyhow::anyhow!( - "Cannot create a memory node out of {value:#?}" - ))) - } -} diff --git a/src/registry/package/mod.rs b/src/registry/package/mod.rs new file mode 100644 index 00000000..d18cf74a --- /dev/null +++ b/src/registry/package/mod.rs @@ -0,0 +1,140 @@ +mod package_utils; + +use package_utils::*; + +use crate::{ + utils::{self, Error}, + wasmer::OptionalRuntime, + Wasmer, +}; +use anyhow::Context; +use js_sys::Math::random; +use std::path::PathBuf; +use wasm_bindgen::prelude::wasm_bindgen; +use wasmer_config::package::Manifest; +use webc::wasmer_package::Package; +use webc::wasmer_package::Strictness; + +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct WasmerPackage { + pub manifest: js_sys::Object, + pub data: Vec, +} + +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct Volume {} + +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct Atom {} + +#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Clone)] +pub struct PublishPackageOutput { + pub manifest: wasm_bindgen::JsValue, + pub hash: String, +} + +#[wasm_bindgen] +impl Wasmer { + /// Create a `WasmerPackage`. + #[wasm_bindgen(js_name = "createPackage")] + #[allow(non_snake_case)] + pub async fn createPackage(manifest: js_sys::Object) -> Result { + let base_dir = PathBuf::from("/"); + let volumes = package_utils::create_volumes(&manifest, &base_dir)?; + + let metadata = package_utils::create_metadata(&manifest, &base_dir)?; + + let atoms = package_utils::create_atoms(&manifest)?; + + let wasmer_manifest: Manifest = serde_wasm_bindgen::from_value(manifest.clone().into()) + .map_err(|e| anyhow::anyhow!(e.to_string()))?; + wasmer_manifest.validate()?; + + let pkg: Package = webc::wasmer_package::Package::from_in_memory( + wasmer_manifest.clone(), + volumes, + atoms, + metadata, + Strictness::default(), + &base_dir, + ) + .context("While parsing the manifest")?; + + let runtime = OptionalRuntime::default().resolve()?.into_inner(); + Wasmer::from_user_package(pkg, wasmer_manifest, runtime).await + } + + /// Publish a package to the registry. + #[wasm_bindgen(js_name = "publishPackage")] + #[allow(non_snake_case)] + pub async fn publishPackage(wasmerPackage: &Wasmer) -> Result { + match &wasmerPackage.pkg { + Some(p) => Wasmer::publish_package_inner(p.manifest.clone(), p.data.clone()).await, + None => Err(Error::Rust(anyhow::anyhow!( + "The selected package has no container!" + ))), + } + } +} + +impl Wasmer { + async fn publish_package_inner( + manifest: Manifest, + bytes: bytes::Bytes, + ) -> Result { + let client = Wasmer::get_client()?; + + let signed_url = wasmer_api::query::get_signed_url_for_package_upload( + client, + Some(60 * 30), + Some(format!("test-{}", random()).replace('.', "-")).as_deref(), + None, + None, + ) + .await? + .ok_or_else(|| anyhow::anyhow!("No signed url!"))? + .url; + + upload(bytes, &signed_url).await?; + + let (namespace, name) = + if let Some(full_name) = manifest.package.as_ref().and_then(|p| p.name.clone()) { + let splits: Vec = full_name.split('/').map(|s| s.to_string()).collect(); + ( + splits + .first() + .ok_or_else(|| anyhow::anyhow!("No namespace provided!"))? + .clone(), + splits.get(1).cloned(), + ) + } else { + return Err(utils::Error::Rust(anyhow::anyhow!( + "No namespace provided!" + ))); + }; + + let out = wasmer_api::query::push_package_release( + client, + name.as_deref(), + &namespace, + &signed_url, + manifest.package.as_ref().map(|p| p.private), + ) + .await? + .ok_or_else(|| anyhow::anyhow!("Backend returned no data!"))?; + + Ok(PublishPackageOutput { + manifest: serde_wasm_bindgen::to_value(&manifest) + .map_err(|e| anyhow::anyhow!(e.to_string()))?, + hash: out + .package_webc + .and_then(|p| p.webc_v3) + .map(|c| c.webc_sha256) + .ok_or_else(|| anyhow::anyhow!("No package was published!"))?, + }) + } +} diff --git a/src/registry/package/package_utils/mem_volumes.rs b/src/registry/package/package_utils/mem_volumes.rs new file mode 100644 index 00000000..8532e116 --- /dev/null +++ b/src/registry/package/package_utils/mem_volumes.rs @@ -0,0 +1,215 @@ +use instant::Duration; +use js_sys::{ + JsString, Object, + Reflect::{delete_property, get, set}, +}; +use std::{collections::BTreeMap, path::Path, time::SystemTime}; +use wasm_bindgen::{JsCast, JsValue}; +use webc::wasmer_package::{MemoryDir, MemoryFile, MemoryNode, MemoryVolume}; + +use crate::utils; + +fn to_systime(modified: &JsValue) -> anyhow::Result { + let millis = if let Some(date) = modified.dyn_ref::() { + date.get_time() + } else if let Some(millis) = modified.as_f64() { + millis + } else { + anyhow::bail!("'modified' is neither a 'Date' or a number!") + }; + + SystemTime::UNIX_EPOCH + .checked_add(Duration::from_secs_f64(millis)) + .ok_or_else(|| anyhow::anyhow!("Error creating the timestamp")) +} + +fn create_node(value: JsValue) -> anyhow::Result { + // We don't know if the value represents a node or a dir, and the two can share a common + // pre-structure ({data: , meta: -}) + + let data_key = JsString::from("data"); + let modified_key = JsString::from("modified"); + + let (data, modified) = + if let (Ok(data), Ok(modified)) = (get(&value, &data_key), get(&value, &modified_key)) { + if !data.is_undefined() && !modified.is_undefined() { + (data, to_systime(&modified)?) + } else { + (value, SystemTime::UNIX_EPOCH) + } + } else { + (value, SystemTime::UNIX_EPOCH) + }; + + Ok( + if data.is_string() || data.is_instance_of::() { + MemoryNode::File(create_file(data, modified)?) + } else { + MemoryNode::Dir(create_dir(data, modified)?) + }, + ) +} + +fn create_file(data: JsValue, modified: SystemTime) -> anyhow::Result { + let data = if data.is_string() { + data.as_string().unwrap().as_bytes().to_vec() + } else if data.is_instance_of::() { + js_sys::Uint8Array::from(data.clone()).to_vec() + } else { + anyhow::bail!("The embedded file is not a string or a 'Uint8Array") + }; + + Ok(MemoryFile { modified, data }) +} + +fn create_dir(data: JsValue, modified: SystemTime) -> anyhow::Result { + // The assumption is that 'data' is in the form + // := { + // 'ident1' : '', + // 'ident2' : '', + // ... + // } + + let mut nodes = BTreeMap::new(); + + let entries = utils::object_entries(&data.dyn_into::().unwrap()) + .map_err(|e| anyhow::anyhow!("{e}"))? + .into_iter(); + + for (key, value) in entries { + nodes.insert(key.as_string().unwrap(), create_node(value)?); + } + + Ok(MemoryDir { modified, nodes }) +} + +fn create_volume(value: JsValue) -> anyhow::Result { + // Each volume is specified on the user-side as an embedded directory. + // + // Users can specify directories in two forms: + // + // 1. Explicitly setting metadata + // { + // data: '', + // modified: '' + // } + // + // 2. Using default metadata + // '' + // + // where is an object mapping node names to node contents: + // + // := { + // 'ident1' : '', + // 'ident2' : '', + // ... + // } + + let data_key = JsString::from("data"); + let modified_key = JsString::from("modified"); + + if let (Ok(data), Ok(modified)) = (get(&value, &data_key), get(&value, &modified_key)) { + if !data.is_undefined() && !modified.is_undefined() { + return Ok(MemoryVolume { + node: create_dir(data, to_systime(&modified)?)?, + }); + } + } + // Assume it's directly an ''. + Ok(MemoryVolume { + node: create_dir(value, SystemTime::UNIX_EPOCH)?, + }) +} + +pub fn create_volumes( + manifest: &js_sys::Object, + base_dir: &Path, +) -> anyhow::Result> { + let fs_key = JsString::from("fs"); + + if let Ok(fs) = get(manifest, &fs_key) { + if !fs.is_undefined() && fs.is_object() { + // Remove the original 'fs' property on the manifest object. + delete_property(manifest, &fs_key) + .map_err(|e| anyhow::anyhow!("while deleting fs property: {e:?}"))?; + + // The 'fs' property's value must be a map in the form + // + // { + // '' : '' + // } + + let fs = fs + .dyn_into::() + .map_err(|e| anyhow::anyhow!("{e:?}"))?; + + let mut volumes = BTreeMap::new(); + let new_fs = js_sys::Object::new(); + + let entries = utils::object_entries(&fs) + .map_err(|e| anyhow::anyhow!("{e}"))? + .into_iter(); + + for (key, value) in entries { + let key_str = key.as_string().unwrap(); + volumes.insert( + base_dir.join(key_str).display().to_string(), + create_volume(value)?, + ); + set(&new_fs, &key, &key) + .map_err(|e| anyhow::anyhow!("while setting fs property: {e:?}"))?; + } + + set(manifest, &fs_key, &new_fs) + .map_err(|e| anyhow::anyhow!("while deleting fs property: {e:?}"))?; + + return Ok(volumes); + } + } + + Ok(BTreeMap::new()) +} + +pub fn create_atoms( + _manifest: &js_sys::Object, +) -> anyhow::Result> { + // @xdoardo: [todo] + + Ok(BTreeMap::new()) +} + +pub fn create_metadata(manifest: &js_sys::Object, base_dir: &Path) -> anyhow::Result { + let pkg_key = JsString::from("package"); + let readme_key = JsString::from("readme"); + let license_key = JsString::from("license"); + + let guest_readme_key = JsString::from(base_dir.join("README.md").display().to_string()); + let guest_license_key = JsString::from(base_dir.join("License").display().to_string()); + + let meta_obj = js_sys::Object::new(); + + let dir = if let Ok(pkg) = get(manifest, &pkg_key) { + if let Ok(readme_file) = get(&pkg, &readme_key) { + if !readme_file.is_undefined() { + set(&meta_obj, &guest_readme_key, &readme_file) + .map_err(|e| anyhow::anyhow!("{e:?}"))?; + } + } + if let Ok(license_file) = get(&pkg, &license_key) { + if !license_file.is_undefined() { + set(&meta_obj, &guest_license_key, &license_file) + .map_err(|e| anyhow::anyhow!("{e:?}"))?; + } + } + + tracing::error!("{meta_obj:?}"); + create_dir(meta_obj.into(), SystemTime::UNIX_EPOCH)? + } else { + MemoryDir { + modified: SystemTime::UNIX_EPOCH, + nodes: BTreeMap::default(), + } + }; + + Ok(MemoryVolume { node: dir }) +} diff --git a/src/registry/package/package_utils/mod.rs b/src/registry/package/package_utils/mod.rs new file mode 100644 index 00000000..831b8540 --- /dev/null +++ b/src/registry/package/package_utils/mod.rs @@ -0,0 +1,5 @@ +mod mem_volumes; +mod upload; + +pub use mem_volumes::*; +pub use upload::*; diff --git a/src/registry/package/package_utils/upload.rs b/src/registry/package/package_utils/upload.rs new file mode 100644 index 00000000..8a96a18b --- /dev/null +++ b/src/registry/package/package_utils/upload.rs @@ -0,0 +1,99 @@ +use bytes::Bytes; +use std::collections::BTreeMap; +use tokio::io::AsyncBufReadExt as _; + +// Upload a package to a signed url. +pub async fn upload(bytes: Bytes, signed_url: &str) -> anyhow::Result { + let client = reqwest::Client::builder() + .default_headers(reqwest::header::HeaderMap::default()) + .build() + .unwrap(); + + let res = client + .request(reqwest::Method::POST, signed_url) + .header(reqwest::header::CONTENT_LENGTH, "0") + .header(reqwest::header::CONTENT_TYPE, "application/octet-stream") + .header("x-goog-resumable", "start") + .header(reqwest::header::ACCEPT, "*/*") + .header(reqwest::header::ACCESS_CONTROL_ALLOW_HEADERS, "*") + .header(reqwest::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .header(reqwest::header::ACCESS_CONTROL_ALLOW_METHODS, "*") + .header(reqwest::header::ACCESS_CONTROL_MAX_AGE, "43200"); + + let result = res.send().await?; + + if result.status() != reqwest::StatusCode::from_u16(201).unwrap() { + return Err(anyhow::anyhow!( + "Uploading package failed: got HTTP {:?} when uploading", + result.status() + )); + } + + let headers = result + .headers() + .into_iter() + .filter_map(|(k, v)| { + let k = k.to_string(); + let v = v.to_str().ok()?.to_string(); + Some((k.to_lowercase(), v)) + }) + .collect::>(); + + let session_uri = headers + .get("location") + .ok_or_else(|| { + anyhow::anyhow!("The upload server did not provide the upload URL correctly") + })? + .clone(); + + //* XXX: If the package is large this line may result in + // * a surge in memory use. + // * + // * In the future, we might want a way to stream bytes + // * from the webc instead of a complete in-memory + // * representation. + // */ + let total_bytes = bytes.len(); + + let chunk_size = 2_097_152; // 2MB + + let mut reader = tokio::io::BufReader::with_capacity(chunk_size, &bytes[..]); + let mut cursor = 0; + + while let Some(chunk) = reader.fill_buf().await.ok().map(|s| s.to_vec()) { + let n = chunk.len(); + + if chunk.is_empty() { + break; + } + + let start = cursor; + let end = cursor + chunk.len().saturating_sub(1); + let content_range = format!("bytes {start}-{end}/{total_bytes}"); + + let res = client + .put(&session_uri) + .header(reqwest::header::CONTENT_TYPE, "application/octet-stream") + .header(reqwest::header::CONTENT_LENGTH, format!("{}", chunk.len())) + .header("Content-Range".to_string(), content_range) + .body(chunk.to_vec()); + + let res = res.send().await; + res.map(|response| response.error_for_status()) + .map_err(|e| { + anyhow::anyhow!( + "cannot send request to {session_uri} (chunk {}..{}): {e}", + cursor, + cursor + chunk_size + ) + })??; + + if n < chunk_size { + break; + } + + reader.consume(n); + cursor += n; + } + Ok(signed_url.to_string()) +} diff --git a/src/wasmer.rs b/src/wasmer.rs index ccc178db..7599aa1f 100644 --- a/src/wasmer.rs +++ b/src/wasmer.rs @@ -52,15 +52,17 @@ pub struct Wasmer { #[wasm_bindgen(getter_with_clone)] pub commands: Commands, - pub(crate) pkg: Option, + #[wasm_bindgen(getter_with_clone)] + pub pkg: Option, } #[derive(Debug, Clone)] #[wasm_bindgen] -pub(crate) struct UserPackageDefinition { +pub struct UserPackageDefinition { pub(crate) manifest: wasmer_config::package::Manifest, pub(crate) data: bytes::Bytes, - pub(crate) hash: String, + #[wasm_bindgen(getter_with_clone)] + pub hash: String, } #[wasm_bindgen] diff --git a/tests/registry.test.ts b/tests/registry.test.ts index e63b4f65..6c145eb0 100644 --- a/tests/registry.test.ts +++ b/tests/registry.test.ts @@ -3,9 +3,15 @@ import { init, initializeLogger, Wasmer } from ".."; const initialized = (async () => { await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url)); + //, undefined, { registry_url: "https://registry.wasmer.wtf/graphql", token: "" }); initializeLogger("error"); })(); + +const owner = "ciuser" +const pkg_name = "test-js-sdk" +const app_name = "test-js-sdk" + describe("Registry", function() { this.timeout("60s").beforeAll(async () => await initialized); @@ -15,151 +21,150 @@ describe("Registry", function() { }); it("can create a package", async () => { - - let manifest = { + let manifest = + { "command": [ { - "module": "wasmer/static-web-server:webserver", - "name": "script", - "runner": "https://webc.org/runner/wasi", + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", "annotations": { "wasi": { "main-args": [ - "-w", - "/settings/config.toml" + "-c", + "import os; print([f for f in os.walk('/public')]); " ] } } } ], + "dependencies": { + "wasmer/python": "3.12.5+build.7" + }, + "fs": { "public": { - "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], - "inner": { - "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], - } + "index.html": "Hello, js!", + "index_timestamp.html": { data: "Hello, js!", modified: 987656789 }, + "index_date.html": { data: "Hello, js!", modified: new Date() } } + } + } + let result = await Wasmer.createPackage(manifest); + console.log("\n== Creating a package == "); + console.log(result.commands) + }) + + + + it("can publish packages", async () => { + let manifest = + { + "package": { + "name": owner + "/" + pkg_name }, + "command": [ + { + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); " + ] + } + } + } + ], "dependencies": { - "wasmer/static-web-server": "^1" + "wasmer/python": "3.12.5+build.7" }, + + "fs": { + "public": { + "index.html": "Hello, js!" + } + } } - let result = await Wasmer.createPackage(manifest); + let wasmerPackage = await Wasmer.createPackage(manifest); + let result = await Wasmer.publishPackage(wasmerPackage); + console.log("\n== Publishing a package == "); + console.log("Published package hash: ", result.hash); + console.log("Published package manifest: ", result.manifest); + }) - console.log("\n== Creating a package == "); - console.log(result.commands) + it("can deploy apps", async () => { + let appConfig = { + name: app_name, + owner: owner, + package: "sha256:34a3b5f5a9108c2b258eb51e9d0978b6778a3696b9c7e713adab33293fb5e4f1", + env: [["test", "new_value"]], + default: true + + }; + + let result = await Wasmer.deployApp(appConfig); + + console.log("\n== Deploying an app == "); + console.log("Deployed app id: ", result.id); + console.log("Deployed app url: ", result.url); }) - //it("can publish packages", async () => { - // let manifest = { - // "package": { - // "name": "/my-package" - // }, - // "command": [ - // { - // "module": "wasmer/static-web-server:webserver", - // "name": "script", - // "runner": "https://webc.org/runner/wasi", - // "annotations": { - // "wasi": { - // "main-args": [ - // "-w", - // "/settings/config.toml" - // ] - // } - // } - // } - // ], - // "fs": { - // "public": { - // "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], - // "inner": { - // "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], - // } - // } - // }, - // "dependencies": { - // "wasmer/static-web-server": "^1" - // }, - // } - // let wasmerPackage = await Wasmer.createPackage(manifest); - // let result = await Wasmer.publishPackage(wasmerPackage); - // console.log("\n== Publishing a package == "); - // console.log("Published package hash: ", result.hash); - // console.log("Published package manifest: ", result.manifest); - //}) - - ////it("can deploy apps", async () => { - // let appConfig = - // { - // name: "my-awesome-app", - // owner: "", - // package: "sha256:e1f3be3d017a049aefa2be984f7352b2d901d73fd6daaba2d2c1643a4196cfaa", - // env: [["test", "new_value"]], - // default: true - - // }; - - // let result = await Wasmer.deployApp(appConfig); - - // console.log("\n== Deploying an app == "); - // console.log("Deployed app id: ", result.id); - // console.log("Deployed app url: ", result.url); - //}) - - //it("can deploy apps with user-created packages", async () => { - // let manifest = { - // "package": { - // "name": "/my-package" - // }, - - // "command": [ - // { - // "module": "wasmer/static-web-server:webserver", - // "name": "script", - // "runner": "https://webc.org/runner/wasi", - // "annotations": { - // "wasi": { - // "main-args": [ - // "-w", - // "/settings/config.toml" - // ] - // } - // } - // } - // ], - // "fs": { - // "public": { - // "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], - // "inner": { - // "index.html": [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a], - // } - // } - // }, - // "dependencies": { - // "wasmer/static-web-server": "^1" - // }, - // } - - // let wasmerPackage = await Wasmer.createPackage(manifest); - - // let appConfig = - // { - // name: "my-awesome-app", - // owner: "", - // env: [["test", "new_value"]], - // default: true - - // }; - - // let result = await Wasmer.deployApp(appConfig, wasmerPackage); - - // console.log("\n== Deploying user-created app =="); - // console.log("Deployed app id: ", result.id); - // console.log("Deployed app url: ", result.url); - //}) + it("can deploy apps with user-created packages", async () => { + + let manifest = + { + "package": { + "name": owner + "/" + pkg_name + }, + "command": [ + { + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); " + ] + } + } + } + ], + "dependencies": { + "wasmer/python": "3.12.5+build.7" + }, + + "fs": { + "public": { + "index.html": "Hello, js!" + } + } + } + + let wasmerPackage = await Wasmer.createPackage(manifest); + await Wasmer.publishPackage(wasmerPackage); + + let appConfig = + { + name: app_name, + owner: owner, + package: wasmerPackage, + env: [["test", "new_value"]], + default: true + }; + + let result = await Wasmer.deployApp(appConfig); + + console.log("\n== Deploying user-created app =="); + console.log("Deployed app id: ", result.id); + console.log("Deployed app url: ", result.url); + }) it("can run user-created packages", async () => { let manifest = { @@ -179,7 +184,7 @@ describe("Registry", function() { } ], "dependencies": { - "wasmer/python": "3.12.9+build.9" + "wasmer/python": "3.12.5+build.7" } }; @@ -211,7 +216,7 @@ describe("Registry", function() { } ], "dependencies": { - "wasmer/python": "3.12.9+build.9" + "wasmer/python": "3.12.5+build.7" }, "fs": { @@ -231,4 +236,49 @@ describe("Registry", function() { console.log("Does this contain 'index.html'?", output.stdout) assert(output.stdout.includes("index.html")) }) + + it("can read metadata", async () => { + let manifest = + { + "package": { + "readme": "This is my readme!", + "manifest": { data: "This is my manifest!", modified: new Date() } + }, + "command": [ + { + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); " + ] + } + } + } + ], + "dependencies": { + "wasmer/python": "3.12.5+build.7" + }, + + "fs": { + "public": { + "index.html": "Hello, js!" + } + } + } + + + let pkg = await Wasmer.createPackage(manifest); + let instance = await pkg.commands["hello"].run(); + + const output = await instance.wait(); + console.log("\n== Mounting and running user-defined fs == "); + console.log(manifest); + console.log("Does this contain 'index.html'?", output.stdout) + assert(output.stdout.includes("index.html")) + }) + }); From 9b256b2dd817fb5f733c8d7756a3119cc98bbb09 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Tue, 23 Jul 2024 18:57:26 +0200 Subject: [PATCH 06/32] Fix: use git repository to patch `webc` --- Cargo.lock | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 24f851c5..c0a5ddfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3911,6 +3911,7 @@ dependencies = [ [[package]] name = "webc" version = "6.0.0-rc1" +source = "git+https://github.com/xdoardo/pirita?branch=in-memory-manifest#5193aaae510f263c33ac8635f2c9ff0a10bd2101" dependencies = [ "anyhow", "base64 0.21.6", diff --git a/Cargo.toml b/Cargo.toml index 61ca841c..5594b463 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,7 +102,7 @@ dwarf-debug-info = false wasm-opt = ["--enable-threads", "--enable-bulk-memory", "-Oz"] [patch.crates-io] -webc = {path = "../pirita/crates/webc"} +webc = {git = "https://github.com/xdoardo/pirita", branch = "in-memory-manifest"} #virtual-net = { git = "https://github.com/wasmerio/wasmer", branch = "master" } #virtual-fs = { git = "https://github.com/wasmerio/wasmer", branch = "master" } #wasmer-wasix = { git = "https://github.com/wasmerio/wasmer", branch = "master" } From ff5db26e490175e59daa4b1aff234077b729031c Mon Sep 17 00:00:00 2001 From: edoardo <48774736+xdoardo@users.noreply.github.com> Date: Wed, 24 Jul 2024 11:34:22 +0200 Subject: [PATCH 07/32] fix: Use spaces instead of tabs in the js part of the code --- WasmerSDK.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/WasmerSDK.ts b/WasmerSDK.ts index dd462884..be4f1739 100644 --- a/WasmerSDK.ts +++ b/WasmerSDK.ts @@ -11,18 +11,18 @@ type RegistryInput = { * Initialize the underlying WebAssembly module. */ export const init = async (module_or_path?: InitInput | Promise, maybe_memory?: WebAssembly.Memory, maybe_registry?: RegistryInput): Promise => { - if (!module_or_path) { - // This will be replaced by the rollup bundler at the SDK build time - // to point to a valid http location of the SDK using unpkg.com. - let wasmUrl = (globalThis as any)["wasmUrl"]; - if (wasmUrl) { - module_or_path = new URL(wasmUrl); - } - } + if (!module_or_path) { + // This will be replaced by the rollup bundler at the SDK build time + // to point to a valid http location of the SDK using unpkg.com. + let wasmUrl = (globalThis as any)["wasmUrl"]; + if (wasmUrl) { + module_or_path = new URL(wasmUrl); + } + } - (globalThis as any)["__WASMER_REGISTRY__"] = maybe_registry; + (globalThis as any)["__WASMER_REGISTRY__"] = maybe_registry; - return load(module_or_path, maybe_memory); + return load(module_or_path, maybe_memory); } /** @@ -30,10 +30,10 @@ export const init = async (module_or_path?: InitInput | Promise, mayb * an unpkg url that is set up at the SDK build time. */ export const setDefaultWorkerUrl = () => { - let workerUrl = (globalThis as any)["workerUrl"]; - if (workerUrl) { - setWorkerUrl(workerUrl) - } + let workerUrl = (globalThis as any)["workerUrl"]; + if (workerUrl) { + setWorkerUrl(workerUrl) + } } // HACK: We save these to the global scope because it's the most reliable way to From 29e26f4fb0a63f57566459fa654227e7654eb8e6 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Fri, 26 Jul 2024 10:57:17 +0200 Subject: [PATCH 08/32] feat: Add atoms to manifests and use latest revision of webc crate --- Cargo.lock | 24 ++----- Cargo.toml | 2 +- src/registry/app.rs | 4 +- src/registry/package/mod.rs | 14 ++-- .../package/package_utils/mem_volumes.rs | 64 +++++++++++++++++-- tests/registry.test.ts | 56 +++++++++++++++- 6 files changed, 126 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0a5ddfa..24aed177 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2906,18 +2906,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.14" @@ -2946,8 +2934,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.2.6", - "serde", - "serde_spanned", "toml_datetime", "winnow 0.5.33", ] @@ -3581,7 +3567,7 @@ dependencies = [ "serde_json", "serde_yaml 0.9.34+deprecated", "thiserror", - "toml 0.8.14", + "toml", "url", ] @@ -3602,7 +3588,7 @@ dependencies = [ "serde_json", "serde_yaml 0.9.34+deprecated", "thiserror", - "toml 0.8.14", + "toml", "url", ] @@ -3665,7 +3651,7 @@ dependencies = [ "sha2", "tempfile", "tokio", - "toml 0.8.14", + "toml", "tracing", "tracing-subscriber", "url", @@ -3911,7 +3897,7 @@ dependencies = [ [[package]] name = "webc" version = "6.0.0-rc1" -source = "git+https://github.com/xdoardo/pirita?branch=in-memory-manifest#5193aaae510f263c33ac8635f2c9ff0a10bd2101" +source = "git+https://github.com/wasmerio/pirita?branch=in-memory-manifest#51d0f017992424bfc8185fa5486a83dbc2de029a" dependencies = [ "anyhow", "base64 0.21.6", @@ -3936,7 +3922,7 @@ dependencies = [ "tar", "tempfile", "thiserror", - "toml 0.7.8", + "toml", "url", "wasmer-config 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index 5594b463..e1388382 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,7 +102,7 @@ dwarf-debug-info = false wasm-opt = ["--enable-threads", "--enable-bulk-memory", "-Oz"] [patch.crates-io] -webc = {git = "https://github.com/xdoardo/pirita", branch = "in-memory-manifest"} +webc = {git = "https://github.com/wasmerio/pirita", branch = "in-memory-manifest"} #virtual-net = { git = "https://github.com/wasmerio/wasmer", branch = "master" } #virtual-fs = { git = "https://github.com/wasmerio/wasmer", branch = "master" } #wasmer-wasix = { git = "https://github.com/wasmerio/wasmer", branch = "master" } diff --git a/src/registry/app.rs b/src/registry/app.rs index 54d754c6..c04e031b 100644 --- a/src/registry/app.rs +++ b/src/registry/app.rs @@ -89,7 +89,7 @@ impl Wasmer { .map_err(utils::js_error)? .as_bool(); let app_config = serde_wasm_bindgen::from_value(appConfig) - .map_err(|e| anyhow::anyhow!(e.to_string()))?; + .map_err(|e| anyhow::anyhow!("{e:?}"))?; Wasmer::deploy_app_inner(app_config, default).await } @@ -114,6 +114,6 @@ impl Wasmer { ) .await .map(|v| v.into()) - .map_err(utils::Error::Rust) + .map_err(|e| utils::Error::Rust(anyhow::anyhow!("{e:?}"))) } } diff --git a/src/registry/package/mod.rs b/src/registry/package/mod.rs index d18cf74a..93fd6c66 100644 --- a/src/registry/package/mod.rs +++ b/src/registry/package/mod.rs @@ -1,14 +1,12 @@ mod package_utils; -use package_utils::*; - use crate::{ utils::{self, Error}, wasmer::OptionalRuntime, Wasmer, }; -use anyhow::Context; use js_sys::Math::random; +use package_utils::*; use std::path::PathBuf; use wasm_bindgen::prelude::wasm_bindgen; use wasmer_config::package::Manifest; @@ -51,7 +49,7 @@ impl Wasmer { let atoms = package_utils::create_atoms(&manifest)?; let wasmer_manifest: Manifest = serde_wasm_bindgen::from_value(manifest.clone().into()) - .map_err(|e| anyhow::anyhow!(e.to_string()))?; + .map_err(|e| anyhow::anyhow!("While parsing the manifest: {e}"))?; wasmer_manifest.validate()?; let pkg: Package = webc::wasmer_package::Package::from_in_memory( @@ -60,9 +58,8 @@ impl Wasmer { atoms, metadata, Strictness::default(), - &base_dir, ) - .context("While parsing the manifest")?; + .map_err(|e| anyhow::anyhow!("{e:?}"))?; let runtime = OptionalRuntime::default().resolve()?.into_inner(); Wasmer::from_user_package(pkg, wasmer_manifest, runtime).await @@ -124,12 +121,13 @@ impl Wasmer { &signed_url, manifest.package.as_ref().map(|p| p.private), ) - .await? + .await + .map_err(|e| anyhow::anyhow!("{e:?}"))? .ok_or_else(|| anyhow::anyhow!("Backend returned no data!"))?; Ok(PublishPackageOutput { manifest: serde_wasm_bindgen::to_value(&manifest) - .map_err(|e| anyhow::anyhow!(e.to_string()))?, + .map_err(|e| anyhow::anyhow!("{e:?}"))?, hash: out .package_webc .and_then(|p| p.webc_v3) diff --git a/src/registry/package/package_utils/mem_volumes.rs b/src/registry/package/package_utils/mem_volumes.rs index 8532e116..bc094355 100644 --- a/src/registry/package/package_utils/mem_volumes.rs +++ b/src/registry/package/package_utils/mem_volumes.rs @@ -171,11 +171,66 @@ pub fn create_volumes( } pub fn create_atoms( - _manifest: &js_sys::Object, -) -> anyhow::Result> { - // @xdoardo: [todo] + manifest: &js_sys::Object, +) -> anyhow::Result, webc::compat::SharedBytes)>> { + // [todo]: + // [module] + // name = .sdasd + // source = [] + // + // -> + // + // [module] + // name = .sdasda + // source = .sdasda + let module_key = JsString::from("module"); + let mut atoms_data = BTreeMap::new(); + + if let Ok(modules) = get(manifest, &module_key) { + if !modules.is_undefined() { + if !modules.is_array() { + anyhow::bail!("'module' must be an array!") + } else { + let modules = js_sys::Array::from(&modules); + let name_key = JsString::from("name"); + let source_key = JsString::from("source"); + for o in modules.to_vec().into_iter() { + let (name, source) = ( + get(&o, &name_key).map_err(|e| anyhow::anyhow!("{e:?}"))?, + get(&o, &source_key).map_err(|e| anyhow::anyhow!("{e:?}"))?, + ); + + if name.is_undefined() + || name.is_null() + || source.is_undefined() + || source.is_null() + { + anyhow::bail!("'name', or 'source' undefined") + } + + if !name.is_string() { + anyhow::bail!("'name' must be a string") + } + + let name = name.as_string().unwrap(); + + let MemoryFile { data, .. } = match create_node(source)? { + MemoryNode::File(f) => f, + MemoryNode::Dir(_) => anyhow::bail!("The atom must be a file!"), + }; + + atoms_data.insert( + name.clone(), + (None, webc::compat::SharedBytes::from_bytes(data)), + ); + set(&o, &source_key, &JsValue::from_str(&name)) + .map_err(|e| anyhow::anyhow!("While setting object values: {e:?}"))?; + } + } + } + } - Ok(BTreeMap::new()) + Ok(atoms_data) } pub fn create_metadata(manifest: &js_sys::Object, base_dir: &Path) -> anyhow::Result { @@ -202,7 +257,6 @@ pub fn create_metadata(manifest: &js_sys::Object, base_dir: &Path) -> anyhow::Re } } - tracing::error!("{meta_obj:?}"); create_dir(meta_obj.into(), SystemTime::UNIX_EPOCH)? } else { MemoryDir { diff --git a/tests/registry.test.ts b/tests/registry.test.ts index 6c145eb0..beed9055 100644 --- a/tests/registry.test.ts +++ b/tests/registry.test.ts @@ -2,13 +2,15 @@ import { assert, expect } from "@esm-bundle/chai"; import { init, initializeLogger, Wasmer } from ".."; const initialized = (async () => { - await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url)); - //, undefined, { registry_url: "https://registry.wasmer.wtf/graphql", token: "" }); + await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url), undefined, { + registry_url: "https://registry.wasmer.wtf/graphql", token: + "" + }); initializeLogger("error"); })(); -const owner = "ciuser" +const owner = "" const pkg_name = "test-js-sdk" const app_name = "test-js-sdk" @@ -20,6 +22,54 @@ describe("Registry", function() { expect(typeof v != "undefined") }); + + it("can create a package with atoms", async () => { + let manifest = + { + "module": [ + { + "name": "test", + "abi": "wasi", + "source": new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]) + }, + { + "name": "other-test", + "source": new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]) + } + ], + "command": [ + { + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); " + ] + } + } + } + ], + "dependencies": { + "wasmer/python": "3.12.5+build.7" + }, + + "fs": { + "public": { + "index.html": "Hello, js!", + "index_timestamp.html": { data: "Hello, js!", modified: 987656789 }, + "index_date.html": { data: "Hello, js!", modified: new Date() } + } + } + } + let result = await Wasmer.createPackage(manifest); + console.log("\n== Creating a package == "); + console.log(result.commands) + }) + + it("can create a package", async () => { let manifest = { From 2b4211cdd8945e9637fcffa4b713fcc3d7ce37b6 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Wed, 31 Jul 2024 16:49:56 +0200 Subject: [PATCH 09/32] feat: Allow users to specify just their token while initializing the library + minor fixes --- WasmerSDK.ts | 44 ++++++++++++------- .../package/package_utils/mem_volumes.rs | 4 ++ tests/registry.test.ts | 11 +++-- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/WasmerSDK.ts b/WasmerSDK.ts index be4f1739..ec569f81 100644 --- a/WasmerSDK.ts +++ b/WasmerSDK.ts @@ -10,19 +10,29 @@ type RegistryInput = { /** * Initialize the underlying WebAssembly module. */ -export const init = async (module_or_path?: InitInput | Promise, maybe_memory?: WebAssembly.Memory, maybe_registry?: RegistryInput): Promise => { - if (!module_or_path) { - // This will be replaced by the rollup bundler at the SDK build time - // to point to a valid http location of the SDK using unpkg.com. - let wasmUrl = (globalThis as any)["wasmUrl"]; - if (wasmUrl) { - module_or_path = new URL(wasmUrl); - } - } - - (globalThis as any)["__WASMER_REGISTRY__"] = maybe_registry; - - return load(module_or_path, maybe_memory); +export const init = async (module_or_path?: InitInput | Promise, maybe_memory?: WebAssembly.Memory, maybe_registry?: string | RegistryInput): Promise => { + if (!module_or_path) { + // This will be replaced by the rollup bundler at the SDK build time + // to point to a valid http location of the SDK using unpkg.com. + let wasmUrl = (globalThis as any)["wasmUrl"]; + if (wasmUrl) { + module_or_path = new URL(wasmUrl); + } + } + + const DEFAULT_REGISTRY = "https://registry.wasmer.io/graphql"; + + if (typeof maybe_registry === 'string' || maybe_registry instanceof String) { + (globalThis as any)["__WASMER_REGISTRY__"] = { + "registry": DEFAULT_REGISTRY, + "token": maybe_registry + }; + } else { + (globalThis as any)["__WASMER_REGISTRY__"] = maybe_registry; + } + + + return load(module_or_path, maybe_memory); } /** @@ -30,10 +40,10 @@ export const init = async (module_or_path?: InitInput | Promise, mayb * an unpkg url that is set up at the SDK build time. */ export const setDefaultWorkerUrl = () => { - let workerUrl = (globalThis as any)["workerUrl"]; - if (workerUrl) { - setWorkerUrl(workerUrl) - } + let workerUrl = (globalThis as any)["workerUrl"]; + if (workerUrl) { + setWorkerUrl(workerUrl) + } } // HACK: We save these to the global scope because it's the most reliable way to diff --git a/src/registry/package/package_utils/mem_volumes.rs b/src/registry/package/package_utils/mem_volumes.rs index bc094355..9558357f 100644 --- a/src/registry/package/package_utils/mem_volumes.rs +++ b/src/registry/package/package_utils/mem_volumes.rs @@ -248,12 +248,16 @@ pub fn create_metadata(manifest: &js_sys::Object, base_dir: &Path) -> anyhow::Re if !readme_file.is_undefined() { set(&meta_obj, &guest_readme_key, &readme_file) .map_err(|e| anyhow::anyhow!("{e:?}"))?; + set(&pkg, &readme_key, &guest_readme_key) + .map_err(|e| anyhow::anyhow!("While setting object values: {e:?}"))?; } } if let Ok(license_file) = get(&pkg, &license_key) { if !license_file.is_undefined() { set(&meta_obj, &guest_license_key, &license_file) .map_err(|e| anyhow::anyhow!("{e:?}"))?; + set(&pkg, &license_key, &guest_license_key) + .map_err(|e| anyhow::anyhow!("While setting object values: {e:?}"))?; } } diff --git a/tests/registry.test.ts b/tests/registry.test.ts index beed9055..d4f45f49 100644 --- a/tests/registry.test.ts +++ b/tests/registry.test.ts @@ -2,15 +2,14 @@ import { assert, expect } from "@esm-bundle/chai"; import { init, initializeLogger, Wasmer } from ".."; const initialized = (async () => { - await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url), undefined, { - registry_url: "https://registry.wasmer.wtf/graphql", token: - "" - }); + await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url), undefined, + { registry_url: "https://registry.wasmer.wtf/graphql", token: "" } + ); initializeLogger("error"); })(); -const owner = "" +const owner = "YOUR_NAME" const pkg_name = "test-js-sdk" const app_name = "test-js-sdk" @@ -292,7 +291,7 @@ describe("Registry", function() { { "package": { "readme": "This is my readme!", - "manifest": { data: "This is my manifest!", modified: new Date() } + "license": { data: "This is my license!", modified: new Date() } }, "command": [ { From fc39403b3a6471d4e32ec518201cce61836bc01e Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Wed, 31 Jul 2024 16:51:03 +0200 Subject: [PATCH 10/32] chore: Use spaces --- WasmerSDK.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/WasmerSDK.ts b/WasmerSDK.ts index ec569f81..3e43e4fc 100644 --- a/WasmerSDK.ts +++ b/WasmerSDK.ts @@ -20,17 +20,16 @@ export const init = async (module_or_path?: InitInput | Promise, mayb } } - const DEFAULT_REGISTRY = "https://registry.wasmer.io/graphql"; - - if (typeof maybe_registry === 'string' || maybe_registry instanceof String) { - (globalThis as any)["__WASMER_REGISTRY__"] = { - "registry": DEFAULT_REGISTRY, - "token": maybe_registry - }; - } else { - (globalThis as any)["__WASMER_REGISTRY__"] = maybe_registry; - } - + const DEFAULT_REGISTRY = "https://registry.wasmer.io/graphql"; + + if (typeof maybe_registry === 'string' || maybe_registry instanceof String) { + (globalThis as any)["__WASMER_REGISTRY__"] = { + "registry": DEFAULT_REGISTRY, + "token": maybe_registry + }; + } else { + (globalThis as any)["__WASMER_REGISTRY__"] = maybe_registry; + } return load(module_or_path, maybe_memory); } From c3081da7c8ab3e6f8ef8d0c5596d79e4d65a0fb6 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Thu, 1 Aug 2024 15:01:02 +0200 Subject: [PATCH 11/32] fix: Add more tests and remove spurious tracing messages --- src/registry/app.rs | 2 - tests/registry.test.ts | 168 ++++++++++++++++++++++++++++++++++------- 2 files changed, 140 insertions(+), 30 deletions(-) diff --git a/src/registry/app.rs b/src/registry/app.rs index c04e031b..599ad958 100644 --- a/src/registry/app.rs +++ b/src/registry/app.rs @@ -53,8 +53,6 @@ fn resolve_pkg(app_config: &JsValue) -> anyhow::Result<()> { ) } - tracing::error!("{package:?}"); - let pkg_key = JsValue::from_str("pkg"); if package.is_string() { diff --git a/tests/registry.test.ts b/tests/registry.test.ts index d4f45f49..520c6250 100644 --- a/tests/registry.test.ts +++ b/tests/registry.test.ts @@ -9,7 +9,7 @@ const initialized = (async () => { })(); -const owner = "YOUR_NAME" +const owner = "" const pkg_name = "test-js-sdk" const app_name = "test-js-sdk" @@ -63,13 +63,11 @@ describe("Registry", function() { } } } - let result = await Wasmer.createPackage(manifest); - console.log("\n== Creating a package == "); - console.log(result.commands) + await Wasmer.createPackage(manifest); }) - it("can create a package", async () => { + it("can create an unnamed package", async () => { let manifest = { "command": [ @@ -99,14 +97,48 @@ describe("Registry", function() { } } } - let result = await Wasmer.createPackage(manifest); - console.log("\n== Creating a package == "); - console.log(result.commands) + await Wasmer.createPackage(manifest); }) - it("can publish packages", async () => { + it("can publish unnamed packages", async () => { + let manifest = + { + "package": { + "name": owner + "/" + }, + "command": [ + { + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); " + ] + } + } + } + ], + "dependencies": { + "wasmer/python": "3.12.5+build.7" + }, + + "fs": { + "public": { + "index.html": "Hello, js!" + } + } + } + + let wasmerPackage = await Wasmer.createPackage(manifest); + await Wasmer.publishPackage(wasmerPackage); + }) + + it("can publish named packages", async () => { let manifest = { "package": { @@ -139,12 +171,10 @@ describe("Registry", function() { } let wasmerPackage = await Wasmer.createPackage(manifest); - let result = await Wasmer.publishPackage(wasmerPackage); - console.log("\n== Publishing a package == "); - console.log("Published package hash: ", result.hash); - console.log("Published package manifest: ", result.manifest); + await Wasmer.publishPackage(wasmerPackage); }) + it("can deploy apps", async () => { let appConfig = { name: app_name, @@ -155,13 +185,62 @@ describe("Registry", function() { }; - let result = await Wasmer.deployApp(appConfig); + await Wasmer.deployApp(appConfig); + }) + + it("fails deploying apps with unpublished packages", async () => { - console.log("\n== Deploying an app == "); - console.log("Deployed app id: ", result.id); - console.log("Deployed app url: ", result.url); + let manifest = + { + "package": { + "name": owner + "/" + pkg_name + }, + "command": [ + { + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); " + ] + } + } + } + ], + "dependencies": { + "wasmer/python": "3.12.5+build.7" + }, + + "fs": { + "public": { + "index.html": "A totally new file!" + } + } + } + + let wasmerPackage = await Wasmer.createPackage(manifest); + + let appConfig = { + name: app_name, + owner: owner, + package: wasmerPackage, + env: [["test", "new_value"]], + default: true + + }; + + try { + await Wasmer.deployApp(appConfig); + assert.fail("deploys the app", "should not deploy the app") + } catch { + return + } }) + it("can deploy apps with user-created packages", async () => { let manifest = @@ -208,11 +287,7 @@ describe("Registry", function() { }; - let result = await Wasmer.deployApp(appConfig); - - console.log("\n== Deploying user-created app =="); - console.log("Deployed app id: ", result.id); - console.log("Deployed app url: ", result.url); + await Wasmer.deployApp(appConfig); }) it("can run user-created packages", async () => { @@ -280,9 +355,6 @@ describe("Registry", function() { let instance = await pkg.commands["hello"].run(); const output = await instance.wait(); - console.log("\n== Mounting and running user-defined fs == "); - console.log(manifest); - console.log("Does this contain 'index.html'?", output.stdout) assert(output.stdout.includes("index.html")) }) @@ -324,10 +396,50 @@ describe("Registry", function() { let instance = await pkg.commands["hello"].run(); const output = await instance.wait(); - console.log("\n== Mounting and running user-defined fs == "); - console.log(manifest); - console.log("Does this contain 'index.html'?", output.stdout) assert(output.stdout.includes("index.html")) }) + it("can deploy a php app", async () => { + let manifest = { + "package": { "name": owner + "/" }, + "command": [ + { + "module": "php/php:php", + "name": "run", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-t", + "/app", + "-S", + "localhost:8080" + ] + } + } + } + ], + "dependencies": { + "php/php": "=8.3.4" + }, + "fs": { + "/app": { + "index.php": " Date: Fri, 2 Aug 2024 10:35:05 +0200 Subject: [PATCH 12/32] fix: use env variables in tests --- tests/registry.test.ts | 45 ++++++++++++++++++++++----------------- web-dev-server.config.mjs | 33 +++++++++++++++++++++------- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/tests/registry.test.ts b/tests/registry.test.ts index 520c6250..7e20d922 100644 --- a/tests/registry.test.ts +++ b/tests/registry.test.ts @@ -1,22 +1,21 @@ import { assert, expect } from "@esm-bundle/chai"; import { init, initializeLogger, Wasmer } from ".."; -const initialized = (async () => { - await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url), undefined, - { registry_url: "https://registry.wasmer.wtf/graphql", token: "" } - ); - initializeLogger("error"); -})(); - - -const owner = "" -const pkg_name = "test-js-sdk" -const app_name = "test-js-sdk" +const pkg_name = "test-js-sdk-pkg" +const app_name = "test-js-sdk-app" describe("Registry", function() { - this.timeout("60s").beforeAll(async () => await initialized); + this.timeout("60s"); + it("can be initialized", async () => { + await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url), undefined, + { registry_url: "https://registry.wasmer.wtf/graphql", token: process.env.WASMER_TOKEN } + ); + initializeLogger("error"); + + (globalThis as any)["__WASMER_TEST_OWNER__"] = process.env.WASMER_TEST_OWNER; + }) - it("Has global context", async () => { + it("has global context", async () => { let v = (globalThis as any)["__WASMER_REGISTRY__"]; expect(typeof v != "undefined") }); @@ -102,12 +101,9 @@ describe("Registry", function() { - it("can publish unnamed packages", async () => { + it("can't publish unnamed packages", async () => { let manifest = { - "package": { - "name": owner + "/" - }, "command": [ { "module": "wasmer/python:python", @@ -135,10 +131,16 @@ describe("Registry", function() { } let wasmerPackage = await Wasmer.createPackage(manifest); - await Wasmer.publishPackage(wasmerPackage); + try { + await Wasmer.publishPackage(wasmerPackage); + assert.fail("publishes the package", "should not publish the package") + } catch { + return + } }) it("can publish named packages", async () => { + let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let manifest = { "package": { @@ -176,6 +178,7 @@ describe("Registry", function() { it("can deploy apps", async () => { + let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let appConfig = { name: app_name, owner: owner, @@ -190,6 +193,7 @@ describe("Registry", function() { it("fails deploying apps with unpublished packages", async () => { + let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let manifest = { "package": { @@ -236,12 +240,13 @@ describe("Registry", function() { await Wasmer.deployApp(appConfig); assert.fail("deploys the app", "should not deploy the app") } catch { - return + return } }) it("can deploy apps with user-created packages", async () => { + let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let manifest = { @@ -400,6 +405,8 @@ describe("Registry", function() { }) it("can deploy a php app", async () => { + + let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let manifest = { "package": { "name": owner + "/" }, "command": [ diff --git a/web-dev-server.config.mjs b/web-dev-server.config.mjs index 7c59abbd..9e5ffce8 100644 --- a/web-dev-server.config.mjs +++ b/web-dev-server.config.mjs @@ -1,16 +1,33 @@ import { chromeLauncher } from "@web/test-runner"; import { esbuildPlugin } from "@web/dev-server-esbuild"; +import { fromRollup } from '@web/dev-server-rollup'; +import rollupReplace from '@rollup/plugin-replace'; async function add_headers(ctx, next) { - ctx.set("Cross-Origin-Opener-Policy", "same-origin"); - ctx.set("Cross-Origin-Embedder-Policy", "require-corp"); - await next(ctx); + ctx.set("Cross-Origin-Opener-Policy", "same-origin"); + ctx.set("Cross-Origin-Embedder-Policy", "require-corp"); + await next(ctx); } + +const envPlugin = fromRollup(rollupReplace)({ + preventAssignment: true, + values: { + 'process.env.WASMER_TOKEN': JSON.stringify(process.env.WASMER_TOKEN), + 'process.env.WASMER_TEST_OWNER': JSON.stringify(process.env.WASMER_TEST_OWNER), + }, +}); + export default { - files: ["tests/**/*.test.ts"], - plugins: [esbuildPlugin({ ts: true })], - middlewares: [add_headers], - browsers: [chromeLauncher({ launchOptions: { devtools: true } })], - testsFinishTimeout: 10 * 60 * 1000, + files: ["tests/**/*.test.ts"], + plugins: [esbuildPlugin({ ts: true }), envPlugin], + middlewares: [add_headers], + browsers: [chromeLauncher({ launchOptions: { devtools: true } })], + testsFinishTimeout: 10 * 60 * 1000, + environmentVariables: { + API_URL: process.env.HELLO, + }, }; + + + From ec087ebae7cccb5de45628f6d1375e924846b4d8 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Fri, 2 Aug 2024 15:05:39 +0200 Subject: [PATCH 13/32] fix: address comments --- WasmerSDK.ts | 19 +++---- package.json | 8 ++- src/lib.rs | 1 + src/registry/app.rs | 75 +++++++++++++++++--------- src/registry/mod.rs | 84 ++++++++++++++++------------- src/registry/package/mod.rs | 16 +++++- src/wasmer.rs | 4 +- tests/registry.test.ts | 102 ++++++++++++++++++++++++++++++++++++ web-dev-server.config.mjs | 7 +++ 9 files changed, 236 insertions(+), 80 deletions(-) diff --git a/WasmerSDK.ts b/WasmerSDK.ts index 3e43e4fc..7f1c8d03 100644 --- a/WasmerSDK.ts +++ b/WasmerSDK.ts @@ -3,10 +3,9 @@ export * from "./pkg/wasmer_js"; import load, { InitInput, InitOutput, ThreadPoolWorker, setWorkerUrl, RegistryConfig } from "./pkg/wasmer_js"; type RegistryInput = { - registry_url: string, + registry_url?: string, token?: string } - /** * Initialize the underlying WebAssembly module. */ @@ -20,16 +19,12 @@ export const init = async (module_or_path?: InitInput | Promise, mayb } } - const DEFAULT_REGISTRY = "https://registry.wasmer.io/graphql"; - - if (typeof maybe_registry === 'string' || maybe_registry instanceof String) { - (globalThis as any)["__WASMER_REGISTRY__"] = { - "registry": DEFAULT_REGISTRY, - "token": maybe_registry - }; - } else { - (globalThis as any)["__WASMER_REGISTRY__"] = maybe_registry; - } + if (typeof maybe_registry === 'string' || maybe_registry instanceof String) { + let token = String(maybe_registry); + (globalThis as any)["__WASMER_REGISTRY__"] = { token: token }; + } else { + (globalThis as any)["__WASMER_REGISTRY__"] = maybe_registry; + } return load(module_or_path, maybe_memory); } diff --git a/package.json b/package.json index 686ed961..e8b8734d 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "build:dev": "wasm-pack build --dev --target=web --weak-refs --no-pack && rollup -c --environment BUILD:development", "dev": "rollup -c -w", "test": "web-test-runner --node-resolve --esbuild-target auto --config ./web-dev-server.config.mjs", + "test-reg": "web-test-runner --node-resolve --esbuild-target auto --config ./web-dev-server.config.mjs --group reg", "docs": "typedoc --options docs/typedoc.json", "doc:watch": "typedoc --watch", "fmt": "prettier . --write", @@ -49,16 +50,19 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.5", "@rollup/plugin-wasm": "^6.2.2", + "@types/mocha": "^10.0.7", "@web/dev-server-esbuild": "^1.0.1", "@web/test-runner": "^0.18.0", "prettier": "^3.1.0", "rimraf": "^5.0.5", + "rollup": "^4.8.0", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-dts": "^6.1.0", - "rollup": "^4.8.0", "typedoc": "^0.25.4", "typescript": "^5.3.3" }, "browserslist": "> 0.5%, last 2 versions, Firefox ESR, not dead", - "dependencies": {} + "dependencies": { + "dotenv": "^16.4.5" + } } diff --git a/src/lib.rs b/src/lib.rs index 5af7dbd7..91b6b53b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ pub use crate::{ run::run_wasix, utils::StringOrBytes, wasmer::Wasmer, + registry::RegistryConfig, }; use once_cell::sync::Lazy; diff --git a/src/registry/app.rs b/src/registry/app.rs index 599ad958..14625565 100644 --- a/src/registry/app.rs +++ b/src/registry/app.rs @@ -1,9 +1,12 @@ -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use anyhow::{anyhow, bail}; +use js_sys::Reflect::{get, has, set}; +use wasm_bindgen::{convert::TryFromJsValue, prelude::wasm_bindgen, JsValue}; use wasmer_api::types::{DeployAppVersion, PublishDeployAppVars}; -use wasmer_config::app::AppConfigV1; +use wasmer_config::{app::AppConfigV1, package::PackageBuilder}; use crate::{ utils::{self, Error}, + wasmer::UserPackageDefinition, Wasmer, }; @@ -37,20 +40,17 @@ impl From for DeployedApp { } } -fn resolve_pkg(app_config: &JsValue) -> anyhow::Result<()> { +async fn resolve_pkg(app_config: &JsValue) -> anyhow::Result<()> { // The app config must have a 'package' field. // // The package field can either be a raw string or a [`Wasmer`]. let package_key = JsValue::from_str("package"); - let package = js_sys::Reflect::get(app_config, &package_key).map_err(|e| { - anyhow::anyhow!("While trying to get the package field from the app config: {e:?}") - })?; + let package = get(app_config, &package_key) + .map_err(|e| anyhow!("While trying to get the package field from the app config: {e:?}"))?; if package.is_undefined() { - anyhow::bail!( - "While trying to get the package field from the app config: undefined field name" - ) + bail!("While trying to get the package field from the app config: undefined field name") } let pkg_key = JsValue::from_str("pkg"); @@ -58,20 +58,43 @@ fn resolve_pkg(app_config: &JsValue) -> anyhow::Result<()> { if package.is_string() { // Do nothing Ok(()) - } else if js_sys::Reflect::has(&package, &pkg_key).map_err(|e| anyhow::anyhow!("{e:?}"))? { - let pkg = js_sys::Reflect::get(&package, &pkg_key).unwrap(); - let hash_key = JsValue::from_str("hash"); - if js_sys::Reflect::has(&pkg, &hash_key).map_err(|e| anyhow::anyhow!("{e:?}"))? { - let hash = js_sys::Reflect::get(&pkg, &hash_key).unwrap(); - js_sys::Reflect::set(app_config, &package_key, &hash) - .map_err(|e| anyhow::anyhow!("{e:?}"))?; - - Ok(()) - } else { - anyhow::bail!("No package information provided! Set the 'package'") + } else if has(&package, &pkg_key).map_err(|e| anyhow!("{e:?}"))? { + let pkg = get(&package, &pkg_key).unwrap(); + let mut u = UserPackageDefinition::try_from_js_value(pkg).map_err(|e| { + anyhow!("Error while casting 'pkg' field back to inner rust type: {e:?}") + })?; + + // Set the app to use the package hash to deploy the app. + set(app_config, &package_key, &JsValue::from(u.hash.clone())) + .map_err(|e| anyhow!("{e:?}"))?; + + if u.manifest.package.is_none() || u.manifest.package.and_then(|v| v.name).is_none() { + // The package was left unnamed - we need to publish it! + let owner_key = JsValue::from_str("owner"); + if has(app_config, &owner_key).map_err(|e| anyhow!("{e:?}"))? { + let owner = get(app_config, &owner_key).map_err(|e| anyhow!("{e:?}"))?; + if !owner.is_string() { + anyhow::bail!("'owner' in the provided app config is not a string!") + } + + let owner = owner.as_string().unwrap(); + u.manifest.package = Some( + PackageBuilder::default() + .name(format!("{owner}/")) + .build()?, + ); + + Wasmer::publish_package_inner(&u.hash, u.manifest.clone(), u.data.clone()) + .await + .map_err(|e| anyhow!("{e:?}"))?; + } else { + anyhow::bail!("app config has no owner specified! Specify one with 'owner'") + } } + + Ok(()) } else { - anyhow::bail!("No package information provided! Set the 'package'") + bail!("no package information provided! Set the 'package' field!") } } @@ -81,14 +104,14 @@ impl Wasmer { #[wasm_bindgen(js_name = "deployApp")] #[allow(non_snake_case)] pub async fn deploy_app(appConfig: JsValue) -> Result { - resolve_pkg(&appConfig)?; + resolve_pkg(&appConfig).await?; - let default = js_sys::Reflect::get(&appConfig, &(String::from("default").into())) + let default = get(&appConfig, &(String::from("default").into())) .map_err(utils::js_error)? .as_bool(); - let app_config = serde_wasm_bindgen::from_value(appConfig) - .map_err(|e| anyhow::anyhow!("{e:?}"))?; + let app_config = serde_wasm_bindgen::from_value(appConfig) + .map_err(|e| anyhow!("while deserializing the app config: {e:?}"))?; Wasmer::deploy_app_inner(app_config, default).await } } @@ -112,6 +135,6 @@ impl Wasmer { ) .await .map(|v| v.into()) - .map_err(|e| utils::Error::Rust(anyhow::anyhow!("{e:?}"))) + .map_err(|e| utils::Error::Rust(anyhow!("while deploying the app: {e:?}"))) } } diff --git a/src/registry/mod.rs b/src/registry/mod.rs index 21b30711..ece8710a 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -1,25 +1,57 @@ pub mod app; pub mod package; -use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use anyhow::anyhow; +use js_sys::Reflect::{get, has}; +use wasm_bindgen::{convert::TryFromJsValue, JsValue}; use wasmer_api::WasmerClient; use crate::{utils::Error, Wasmer}; static WASMER_CLIENT: std::sync::OnceLock = std::sync::OnceLock::new(); -#[wasm_bindgen(getter_with_clone)] +#[derive(Debug, Default, Clone)] pub struct RegistryConfig { - pub registry_url: String, + pub registry_url: Option, pub token: Option, } -impl Default for RegistryConfig { - fn default() -> Self { - Self { - registry_url: String::from("https://registry.wasmer.io/graphql"), - token: Default::default(), - } +impl TryFromJsValue for RegistryConfig { + type Error = JsValue; + + fn try_from_js_value(value: wasm_bindgen::prelude::JsValue) -> Result { + let token_key = JsValue::from_str("token"); + let registry_url_key = JsValue::from_str("registry_url"); + let token = if has(&value, &token_key)? { + let token = get(&value, &token_key)?; + if let Some(token) = token.as_string() { + Some(token) + } else { + return Err(JsValue::from_str( + "Cannot create token from non-string object!", + )); + } + } else { + None + }; + + let registry_url = if has(&value, ®istry_url_key)? { + let registry_url = get(&value, ®istry_url_key)?; + if let Some(registry_url) = registry_url.as_string() { + Some(registry_url) + } else { + return Err(JsValue::from_str( + "Cannot create registry url from non-string object!", + )); + } + } else { + None + }; + + Ok(Self { + registry_url, + token, + }) } } @@ -29,38 +61,18 @@ impl Wasmer { let registry_input = if let Some(registry_info) = web_sys::window().and_then(|w| w.get("__WASMER_REGISTRY__")) { - if registry_info.is_undefined() { - RegistryConfig::default() - } else { - let registry_url = js_sys::Reflect::get( - ®istry_info, - &JsValue::from(String::from("registry_url")), - ) - .ok() - .and_then(|u| u.as_string()); - let token = - js_sys::Reflect::get(®istry_info, &JsValue::from(String::from("token"))) - .ok() - .and_then(|u| u.as_string()); - - if let Some(registry_url) = registry_url { - RegistryConfig { - registry_url, - token, - } - } else { - RegistryConfig { - token, - ..Default::default() - } - } - } + RegistryConfig::try_from_js_value(registry_info.into()) + .map_err(|e| anyhow!("while reading registry configuration: {e:?}"))? } else { RegistryConfig::default() }; let mut client = wasmer_api::WasmerClient::new( - url::Url::parse(®istry_input.registry_url)?, + url::Url::parse( + ®istry_input + .registry_url + .unwrap_or(crate::DEFAULT_REGISTRY.into()), + )?, "Wasmer JS SDK", )?; if let Some(token) = registry_input.token { diff --git a/src/registry/package/mod.rs b/src/registry/package/mod.rs index 93fd6c66..5168d5f1 100644 --- a/src/registry/package/mod.rs +++ b/src/registry/package/mod.rs @@ -70,7 +70,9 @@ impl Wasmer { #[allow(non_snake_case)] pub async fn publishPackage(wasmerPackage: &Wasmer) -> Result { match &wasmerPackage.pkg { - Some(p) => Wasmer::publish_package_inner(p.manifest.clone(), p.data.clone()).await, + Some(p) => { + Wasmer::publish_package_inner(&p.hash, p.manifest.clone(), p.data.clone()).await + } None => Err(Error::Rust(anyhow::anyhow!( "The selected package has no container!" ))), @@ -79,12 +81,22 @@ impl Wasmer { } impl Wasmer { - async fn publish_package_inner( + pub(super) async fn publish_package_inner( + hash: &str, manifest: Manifest, bytes: bytes::Bytes, ) -> Result { let client = Wasmer::get_client()?; + if wasmer_api::query::get_package_release(client, &hash).await?.is_some() { + // The package was already published. + return Ok(PublishPackageOutput { + manifest: serde_wasm_bindgen::to_value(&manifest) + .map_err(|e| anyhow::anyhow!("{e:?}"))?, + hash: hash.to_string(), + }); + } + let signed_url = wasmer_api::query::get_signed_url_for_package_upload( client, Some(60 * 30), diff --git a/src/wasmer.rs b/src/wasmer.rs index 7599aa1f..8f5bcfa8 100644 --- a/src/wasmer.rs +++ b/src/wasmer.rs @@ -41,7 +41,7 @@ use crate::{ /// throw new Error(`Python exited with ${code}: ${stderr}`); /// } /// ``` -#[derive(Debug, Clone)] +#[derive(Debug, Clone, wasm_bindgen_derive::TryFromJsValue)] #[wasm_bindgen] pub struct Wasmer { /// The package's entrypoint. @@ -56,7 +56,7 @@ pub struct Wasmer { pub pkg: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, wasm_bindgen_derive::TryFromJsValue)] #[wasm_bindgen] pub struct UserPackageDefinition { pub(crate) manifest: wasmer_config::package::Manifest, diff --git a/tests/registry.test.ts b/tests/registry.test.ts index 7e20d922..12b66484 100644 --- a/tests/registry.test.ts +++ b/tests/registry.test.ts @@ -6,6 +6,7 @@ const app_name = "test-js-sdk-app" describe("Registry", function() { this.timeout("60s"); + it("can be initialized", async () => { await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url), undefined, { registry_url: "https://registry.wasmer.wtf/graphql", token: process.env.WASMER_TOKEN } @@ -15,6 +16,107 @@ describe("Registry", function() { (globalThis as any)["__WASMER_TEST_OWNER__"] = process.env.WASMER_TEST_OWNER; }) + it("can use unnamed packages twice", async () => { + let manifest = + { + "command": [ + { + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); " + ] + } + } + } + ], + "dependencies": { + "wasmer/python": "3.12.5+build.7" + }, + + "fs": { + "public": { + "index.html": "Hello, js!" + } + } + } + + let wasmerPackage = await Wasmer.createPackage(manifest); + + let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; + let appConfig = { + name: app_name, + owner: owner, + package: wasmerPackage, + env: [["test", "new_value"]], + default: true + + }; + + await Wasmer.deployApp(appConfig); + + let appConfig2 = { + name: app_name + "2", + owner: owner, + package: wasmerPackage, + env: [["test", "new_value"]], + default: true + + }; + + await Wasmer.deployApp(appConfig2); + }) + + it("can deploy unnamed packages", async () => { + let manifest = + { + "command": [ + { + "module": "wasmer/python:python", + "name": "hello", + "runner": "wasi", + "annotations": { + "wasi": { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); " + ] + } + } + } + ], + "dependencies": { + "wasmer/python": "3.12.5+build.7" + }, + + "fs": { + "public": { + "index.html": "Hello, js!" + } + } + } + + let wasmerPackage = await Wasmer.createPackage(manifest); + + let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; + let appConfig = { + name: app_name, + owner: owner, + package: wasmerPackage, + env: [["test", "new_value"]], + default: true + + }; + + await Wasmer.deployApp(appConfig); + + }) + + it("has global context", async () => { let v = (globalThis as any)["__WASMER_REGISTRY__"]; expect(typeof v != "undefined") diff --git a/web-dev-server.config.mjs b/web-dev-server.config.mjs index 9e5ffce8..be740187 100644 --- a/web-dev-server.config.mjs +++ b/web-dev-server.config.mjs @@ -2,6 +2,9 @@ import { chromeLauncher } from "@web/test-runner"; import { esbuildPlugin } from "@web/dev-server-esbuild"; import { fromRollup } from '@web/dev-server-rollup'; import rollupReplace from '@rollup/plugin-replace'; +import dotenv from 'dotenv'; +dotenv.config(); + async function add_headers(ctx, next) { ctx.set("Cross-Origin-Opener-Policy", "same-origin"); @@ -27,6 +30,10 @@ export default { environmentVariables: { API_URL: process.env.HELLO, }, + groups: [{ + "name": "reg", + "files": ["tests/registry.test.ts"] + }] }; From acaf046cb52473144e780e18f22121b74d890ac0 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Fri, 2 Aug 2024 15:52:51 +0200 Subject: [PATCH 14/32] chore: Update dependencies --- package-lock.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/package-lock.json b/package-lock.json index 178dd6a2..0e5102ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@wasmer/sdk", "version": "0.6.0", "license": "MIT", + "dependencies": { + "dotenv": "^16.4.5" + }, "devDependencies": { "@babel/preset-env": "^7.23.3", "@esm-bundle/chai": "^4.3.4-fix.0", @@ -16,6 +19,7 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.5", "@rollup/plugin-wasm": "^6.2.2", + "@types/mocha": "^10.0.7", "@web/dev-server-esbuild": "^1.0.1", "@web/test-runner": "^0.18.0", "prettier": "^3.1.0", @@ -2808,6 +2812,12 @@ "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true }, + "node_modules/@types/mocha": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", + "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", + "dev": true + }, "node_modules/@types/node": { "version": "20.10.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", @@ -4180,6 +4190,18 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", From d634399c219814ebd11c5128f1f676ca06f916bf Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Fri, 2 Aug 2024 15:54:13 +0200 Subject: [PATCH 15/32] chore: Make linter happy --- src/lib.rs | 2 +- src/registry/package/mod.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 91b6b53b..52681af9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,10 +29,10 @@ pub use crate::{ js_runtime::{JsRuntime, RuntimeOptions}, logging::initialize_logger, options::{RunOptions, SpawnOptions}, + registry::RegistryConfig, run::run_wasix, utils::StringOrBytes, wasmer::Wasmer, - registry::RegistryConfig, }; use once_cell::sync::Lazy; diff --git a/src/registry/package/mod.rs b/src/registry/package/mod.rs index 5168d5f1..5ee9cae5 100644 --- a/src/registry/package/mod.rs +++ b/src/registry/package/mod.rs @@ -88,7 +88,10 @@ impl Wasmer { ) -> Result { let client = Wasmer::get_client()?; - if wasmer_api::query::get_package_release(client, &hash).await?.is_some() { + if wasmer_api::query::get_package_release(client, hash) + .await? + .is_some() + { // The package was already published. return Ok(PublishPackageOutput { manifest: serde_wasm_bindgen::to_value(&manifest) From 4711513912ebc997e9e8a46af0eb6b50658b8677 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Fri, 2 Aug 2024 18:55:58 +0200 Subject: [PATCH 16/32] chore: Use newly-released version of pirita --- Cargo.lock | 7 ++++--- Cargo.toml | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24aed177..9761e444 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3896,11 +3896,12 @@ dependencies = [ [[package]] name = "webc" -version = "6.0.0-rc1" -source = "git+https://github.com/wasmerio/pirita?branch=in-memory-manifest#51d0f017992424bfc8185fa5486a83dbc2de029a" +version = "6.0.0-rc2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3e2ccb43d303c5bd48f31db7a129481a9aaa5343d623f92951751df190df81" dependencies = [ "anyhow", - "base64 0.21.6", + "base64 0.22.1", "bytes", "cfg-if", "document-features", diff --git a/Cargo.toml b/Cargo.toml index e1388382..e9cbac03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ wasmer-api = { version = "0.0.30", git = "https://github.com/wasmerio/wasmer", b wasmer-config = "0.4.0" wasmer-types = "4.3.2" wasmer-wasix = { version = "0.22.0", default-features = false, features = ["js", "js-default"] } -webc = "6.0.0-rc1" +webc = "6.0.0-rc2" [dependencies.web-sys] version = "0.3" @@ -102,7 +102,7 @@ dwarf-debug-info = false wasm-opt = ["--enable-threads", "--enable-bulk-memory", "-Oz"] [patch.crates-io] -webc = {git = "https://github.com/wasmerio/pirita", branch = "in-memory-manifest"} +#webc = {git = "https://github.com/wasmerio/pirita", branch = "in-memory-manifest"} #virtual-net = { git = "https://github.com/wasmerio/wasmer", branch = "master" } #virtual-fs = { git = "https://github.com/wasmerio/wasmer", branch = "master" } #wasmer-wasix = { git = "https://github.com/wasmerio/wasmer", branch = "master" } From e8a22103b9997bd9d5ebf4520e910f17dd38bdb2 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Fri, 2 Aug 2024 19:02:29 +0200 Subject: [PATCH 17/32] chore: Move const initialisation out of test --- tests/registry.test.ts | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/tests/registry.test.ts b/tests/registry.test.ts index 12b66484..d8e21963 100644 --- a/tests/registry.test.ts +++ b/tests/registry.test.ts @@ -6,14 +6,13 @@ const app_name = "test-js-sdk-app" describe("Registry", function() { this.timeout("60s"); + const WASMER_TEST_OWNER = process.env.WASMER_TEST_OWNER; it("can be initialized", async () => { await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url), undefined, { registry_url: "https://registry.wasmer.wtf/graphql", token: process.env.WASMER_TOKEN } ); initializeLogger("error"); - - (globalThis as any)["__WASMER_TEST_OWNER__"] = process.env.WASMER_TEST_OWNER; }) it("can use unnamed packages twice", async () => { @@ -47,10 +46,9 @@ describe("Registry", function() { let wasmerPackage = await Wasmer.createPackage(manifest); - let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let appConfig = { name: app_name, - owner: owner, + owner: WASMER_TEST_OWNER, package: wasmerPackage, env: [["test", "new_value"]], default: true @@ -61,7 +59,7 @@ describe("Registry", function() { let appConfig2 = { name: app_name + "2", - owner: owner, + owner: WASMER_TEST_OWNER, package: wasmerPackage, env: [["test", "new_value"]], default: true @@ -102,10 +100,9 @@ describe("Registry", function() { let wasmerPackage = await Wasmer.createPackage(manifest); - let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let appConfig = { name: app_name, - owner: owner, + owner: WASMER_TEST_OWNER, package: wasmerPackage, env: [["test", "new_value"]], default: true @@ -242,11 +239,10 @@ describe("Registry", function() { }) it("can publish named packages", async () => { - let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let manifest = { "package": { - "name": owner + "/" + pkg_name + "name": WASMER_TEST_OWNER + "/" + pkg_name }, "command": [ { @@ -280,10 +276,9 @@ describe("Registry", function() { it("can deploy apps", async () => { - let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let appConfig = { name: app_name, - owner: owner, + owner: WASMER_TEST_OWNER, package: "sha256:34a3b5f5a9108c2b258eb51e9d0978b6778a3696b9c7e713adab33293fb5e4f1", env: [["test", "new_value"]], default: true @@ -295,11 +290,10 @@ describe("Registry", function() { it("fails deploying apps with unpublished packages", async () => { - let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let manifest = { "package": { - "name": owner + "/" + pkg_name + "name": WASMER_TEST_OWNER + "/" + pkg_name }, "command": [ { @@ -331,7 +325,7 @@ describe("Registry", function() { let appConfig = { name: app_name, - owner: owner, + owner: WASMER_TEST_OWNER, package: wasmerPackage, env: [["test", "new_value"]], default: true @@ -348,12 +342,11 @@ describe("Registry", function() { it("can deploy apps with user-created packages", async () => { - let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let manifest = { "package": { - "name": owner + "/" + pkg_name + "name": WASMER_TEST_OWNER + "/" + pkg_name }, "command": [ { @@ -387,7 +380,7 @@ describe("Registry", function() { let appConfig = { name: app_name, - owner: owner, + owner: WASMER_TEST_OWNER, package: wasmerPackage, env: [["test", "new_value"]], default: true @@ -508,9 +501,8 @@ describe("Registry", function() { it("can deploy a php app", async () => { - let owner = (globalThis as any)["__WASMER_TEST_OWNER__"]; let manifest = { - "package": { "name": owner + "/" }, + "package": { "name": WASMER_TEST_OWNER + "/" }, "command": [ { "module": "php/php:php", @@ -544,7 +536,7 @@ describe("Registry", function() { let appConfig = { name: app_name, - owner: owner, + owner: WASMER_TEST_OWNER, package: pkg, default: true }; From 118e46155f1de5de7c9c0af8e9395fee7c6d2181 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Fri, 2 Aug 2024 19:07:27 +0200 Subject: [PATCH 18/32] chore: Replace removed function from `ModuleHash` --- src/tasks/post_message_payload.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tasks/post_message_payload.rs b/src/tasks/post_message_payload.rs index 1e3f0aa6..a18c4e70 100644 --- a/src/tasks/post_message_payload.rs +++ b/src/tasks/post_message_payload.rs @@ -277,7 +277,7 @@ mod tests { let engine = wasmer::Engine::default(); let module = wasmer::Module::new(&engine, wasm).unwrap(); let msg = PostMessagePayload::Notification(Notification::CacheModule { - hash: ModuleHash::hash(wasm), + hash: ModuleHash::xxhash(wasm), module: module.into(), }); @@ -286,7 +286,7 @@ mod tests { match round_tripped { PostMessagePayload::Notification(Notification::CacheModule { hash, module: _ }) => { - assert_eq!(hash, ModuleHash::hash(wasm)); + assert_eq!(hash, ModuleHash::xxhash(wasm)); } _ => unreachable!(), }; From 18eee39e7ade9ed843457058b49d9e0bdf9d6d53 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Fri, 2 Aug 2024 19:08:21 +0200 Subject: [PATCH 19/32] chore: rollback rollup change --- rollup.config.mjs | 140 +++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/rollup.config.mjs b/rollup.config.mjs index b3f45895..ff41ac94 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -1,5 +1,5 @@ import terser from "@rollup/plugin-terser"; -import pkg from "./package.json" with { type: "json" }; +import pkg from "./package.json" assert { type: "json" }; import dts from "rollup-plugin-dts"; import typescript from "@rollup/plugin-typescript"; import replace from "@rollup/plugin-replace"; @@ -22,79 +22,79 @@ const banner = `/*! */`; const makeConfig = (env = "development", input, name, plugins = []) => { - const config = { - input, - external: EXTERNAL, - output: [ - { - banner, - name: name, - file: `dist/${name}.umd.js`, - format: "umd", - exports: "auto", - globals: GLOBALS, - }, - { - banner, - file: `dist/${name}.cjs`, - format: "cjs", - exports: "auto", - globals: GLOBALS, - }, - { - banner, - file: `dist/${name}.js`, - format: "es", - exports: "named", - globals: GLOBALS, - }, - ], - plugins: [ - typescript(), - ...plugins, - copy({ - targets: [ - { src: ['pkg/wasmer_js_bg.wasm', 'pkg/wasmer_js_bg.wasm.d.ts'], dest: 'dist' }, - ] - }) - ], - }; + const config = { + input, + external: EXTERNAL, + output: [ + { + banner, + name: name, + file: `dist/${name}.umd.js`, + format: "umd", + exports: "auto", + globals: GLOBALS, + }, + { + banner, + file: `dist/${name}.cjs`, + format: "cjs", + exports: "auto", + globals: GLOBALS, + }, + { + banner, + file: `dist/${name}.js`, + format: "es", + exports: "named", + globals: GLOBALS, + }, + ], + plugins: [ + typescript(), + ...plugins, + copy({ + targets: [ + { src: ['pkg/wasmer_js_bg.wasm', 'pkg/wasmer_js_bg.wasm.d.ts'], dest: 'dist' }, + ] + }) + ], + }; - if (env === "production") { - config.plugins.push( - terser({ - output: { - comments: /^!/, - }, - }), - ); - } - config.plugins.push( - replace({ - values: { - "globalThis.wasmUrl": `"https://unpkg.com/${pkg.name}@${pkg.version}/dist/wasmer_js_bg.wasm"`, - "globalThis.workerUrl": `"https://unpkg.com/${pkg.name}@${pkg.version}/dist/WasmerSDK.js"`, - }, - preventAssignment: true, - }), - ); + if (env === "production") { + config.plugins.push( + terser({ + output: { + comments: /^!/, + }, + }), + ); + } + config.plugins.push( + replace({ + values: { + "globalThis.wasmUrl": `"https://unpkg.com/${pkg.name}@${pkg.version}/dist/wasmer_js_bg.wasm"`, + "globalThis.workerUrl": `"https://unpkg.com/${pkg.name}@${pkg.version}/dist/WasmerSDK.js"`, + }, + preventAssignment: true, + }), + ); - return config; + return config; }; export default commandLineArgs => { - let env = commandLineArgs.environment === "BUILD:production" ? "production" : null; - const configs = [ - makeConfig(env, "WasmerSDK.ts", LIBRARY_NAME), - makeConfig(env, "WasmerSDKBundled.ts", `${LIBRARY_NAME}Bundled`, [wasm({ - maxFileSize: 100 * 1024 * 1024, - })]), - { - input: "./pkg/wasmer_js.d.ts", - output: [{ file: "dist/pkg/wasmer_js.d.ts", format: "es" }], - plugins: [dts()], - }, - ]; + let env = commandLineArgs.environment === "BUILD:production" ? "production" : null; + const configs = [ + makeConfig(env, "WasmerSDK.ts", LIBRARY_NAME), + makeConfig(env, "WasmerSDKBundled.ts", `${LIBRARY_NAME}Bundled`, [wasm({ + maxFileSize: 100 * 1024 * 1024, + })]), + { + input: "./pkg/wasmer_js.d.ts", + output: [{ file: "dist/pkg/wasmer_js.d.ts", format: "es" }], + plugins: [dts()], + }, + ]; - return configs; + return configs; }; From 2e4679a3ae6d2b47607e4492e143ea144f5a7f34 Mon Sep 17 00:00:00 2001 From: edoardo <48774736+xdoardo@users.noreply.github.com> Date: Fri, 2 Aug 2024 19:38:58 +0200 Subject: [PATCH 20/32] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 795b6b96..de73a149 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: - name: Build Package run: npm run build - name: Integration Tests - run: npm run test + run: WASMER_TOKEN=${{ secrets.WAPM_DEV_TOKEN }} WASMER_TEST_OWNER=ciuser npm run test examples: name: Build Examples From 0b5eab9f4922ded6a5fe6bcec34ff38ffa2080c9 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 5 Aug 2024 18:18:32 +0200 Subject: [PATCH 21/32] fix: Use single input for initalization function --- WasmerSDK.ts | 72 ++- WasmerSDKBundled.ts | 21 +- src/registry/mod.rs | 4 +- src/registry/package/mod.rs | 2 +- src/tasks/worker.js | 80 ++- tests/directory.test.ts | 76 +-- tests/integration.test.ts | 988 ++++++++++++++++---------------- tests/registry.test.ts | 1063 +++++++++++++++++------------------ tests/run.test.ts | 268 ++++----- 9 files changed, 1287 insertions(+), 1287 deletions(-) diff --git a/WasmerSDK.ts b/WasmerSDK.ts index 7f1c8d03..605e2259 100644 --- a/WasmerSDK.ts +++ b/WasmerSDK.ts @@ -1,44 +1,50 @@ export * from "./pkg/wasmer_js"; // @ts-ignore -import load, { InitInput, InitOutput, ThreadPoolWorker, setWorkerUrl, RegistryConfig } from "./pkg/wasmer_js"; +import load, { + InitInput, + InitOutput, + ThreadPoolWorker, + setWorkerUrl, +} from "./pkg/wasmer_js"; + +export type WasmerInitInput = { + module?: InitInput | Promise; + memory?: WebAssembly.Memory; + registryUrl?: string; + token?: string; +}; -type RegistryInput = { - registry_url?: string, - token?: string -} /** * Initialize the underlying WebAssembly module. */ -export const init = async (module_or_path?: InitInput | Promise, maybe_memory?: WebAssembly.Memory, maybe_registry?: string | RegistryInput): Promise => { - if (!module_or_path) { - // This will be replaced by the rollup bundler at the SDK build time - // to point to a valid http location of the SDK using unpkg.com. - let wasmUrl = (globalThis as any)["wasmUrl"]; - if (wasmUrl) { - module_or_path = new URL(wasmUrl); - } - } +export const init = async (initValue: WasmerInitInput): Promise => { + if (!initValue.module) { + // This will be replaced by the rollup bundler at the SDK build time + // to point to a valid http location of the SDK using unpkg.com. + let wasmUrl = (globalThis as any)["wasmUrl"]; + if (wasmUrl) { + initValue.module = new URL(wasmUrl); + } + } - if (typeof maybe_registry === 'string' || maybe_registry instanceof String) { - let token = String(maybe_registry); - (globalThis as any)["__WASMER_REGISTRY__"] = { token: token }; - } else { - (globalThis as any)["__WASMER_REGISTRY__"] = maybe_registry; - } + (globalThis as any)["__WASMER_REGISTRY__"] = { + registryUrl: initValue.registryUrl, + token: initValue.token, + }; - return load(module_or_path, maybe_memory); -} + return load(initValue.module, initValue.memory); +}; /** * Set a deafult working Worker Url. Which in this case will be * an unpkg url that is set up at the SDK build time. */ export const setDefaultWorkerUrl = () => { - let workerUrl = (globalThis as any)["workerUrl"]; - if (workerUrl) { - setWorkerUrl(workerUrl) - } -} + let workerUrl = (globalThis as any)["workerUrl"]; + if (workerUrl) { + setWorkerUrl(workerUrl); + } +}; // HACK: We save these to the global scope because it's the most reliable way to // make sure worker.js gets access to them. Normal exports are removed when @@ -47,5 +53,13 @@ export const setDefaultWorkerUrl = () => { // HACK: some bundlers such as webpack uses this on dev mode. // We add this functions to allow dev mode work in those bundlers. -(globalThis as any).$RefreshReg$ = (globalThis as any).$RefreshReg$ || function() {/**/ }; -(globalThis as any).$RefreshSig$ = (globalThis as any).$RefreshSig$ || function() { return function() { } }; +(globalThis as any).$RefreshReg$ = + (globalThis as any).$RefreshReg$ || + function () { + /**/ + }; +(globalThis as any).$RefreshSig$ = + (globalThis as any).$RefreshSig$ || + function () { + return function () {}; + }; diff --git a/WasmerSDKBundled.ts b/WasmerSDKBundled.ts index cdf0e343..150a4ec3 100644 --- a/WasmerSDKBundled.ts +++ b/WasmerSDKBundled.ts @@ -1,5 +1,10 @@ export * from "./WasmerSDK"; -import { init as load, InitInput, InitOutput } from "./WasmerSDK"; +import { + init as load, + InitInput, + InitOutput, + WasmerInitInput, +} from "./WasmerSDK"; // @ts-ignore import wasm_bytes from "./pkg/wasmer_js_bg.wasm"; @@ -7,10 +12,10 @@ import wasm_bytes from "./pkg/wasmer_js_bg.wasm"; * Initialize the underlying WebAssembly module, defaulting to an embedded * copy of the `*.wasm` file. */ -export const init = async (module_or_path?: InitInput | Promise, maybe_memory?: WebAssembly.Memory): Promise => { - if (!module_or_path) { - // @ts-ignore - module_or_path = await wasm_bytes(); - } - return load(module_or_path, maybe_memory); -} +export const init = async (initValue: WasmerInitInput): Promise => { + if (!initValue.module) { + // @ts-ignore + initValue.module = await wasm_bytes(); + } + return load(initValue); +}; diff --git a/src/registry/mod.rs b/src/registry/mod.rs index ece8710a..70a31311 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -21,7 +21,7 @@ impl TryFromJsValue for RegistryConfig { fn try_from_js_value(value: wasm_bindgen::prelude::JsValue) -> Result { let token_key = JsValue::from_str("token"); - let registry_url_key = JsValue::from_str("registry_url"); + let registry_url_key = JsValue::from_str("registryUrl"); let token = if has(&value, &token_key)? { let token = get(&value, &token_key)?; if let Some(token) = token.as_string() { @@ -39,6 +39,8 @@ impl TryFromJsValue for RegistryConfig { let registry_url = get(&value, ®istry_url_key)?; if let Some(registry_url) = registry_url.as_string() { Some(registry_url) + } else if registry_url.is_null() || registry_url.is_undefined() { + Some(crate::DEFAULT_REGISTRY.into()) } else { return Err(JsValue::from_str( "Cannot create registry url from non-string object!", diff --git a/src/registry/package/mod.rs b/src/registry/package/mod.rs index 5ee9cae5..05d4bb44 100644 --- a/src/registry/package/mod.rs +++ b/src/registry/package/mod.rs @@ -103,7 +103,7 @@ impl Wasmer { let signed_url = wasmer_api::query::get_signed_url_for_package_upload( client, Some(60 * 30), - Some(format!("test-{}", random()).replace('.', "-")).as_deref(), + Some(format!("js-{}", random()).replace('.', "-")).as_deref(), None, None, ) diff --git a/src/tasks/worker.js b/src/tasks/worker.js index 18c5b979..82f1757d 100644 --- a/src/tasks/worker.js +++ b/src/tasks/worker.js @@ -4,54 +4,50 @@ globalThis.onerror = console.error; let pendingMessages = []; let worker = undefined; let handleMessage = async data => { - if (worker) { - await worker.handle(data); - } else { - // We start off by buffering up all messages until we finish initializing. - pendingMessages.push(data); - } + if (worker) { + await worker.handle(data); + } else { + // We start off by buffering up all messages until we finish initializing. + pendingMessages.push(data); + } }; globalThis.onmessage = async ev => { - if (ev.data.type == "init") { - const { memory, module, id, import_url } = ev.data; - const imported = await import( - new URL(import_url, self.location.origin) - ); + if (ev.data.type == "init") { + const { memory, module, id, import_url } = ev.data; + const imported = await import(new URL(import_url, self.location.origin)); - // HACK: How we load our imports will change depending on how the code - // is deployed. If we are being used in "wasm-pack test" then we can - // access the things we want from the imported object. Otherwise, if we - // are being used from a bundler, chances are those things are no longer - // directly accessible and we need to get them from the - // __WASMER_INTERNALS__ object stashed on the global scope when the - // package was imported. - let init; - let ThreadPoolWorker; - if ("ThreadPoolWorker" in imported) { - if ("default" in imported) { - init = imported.default; - } - else if ("init" in imported) { - init = imported.init; - } - ThreadPoolWorker = imported.ThreadPoolWorker; - } else { - init = globalThis["__WASMER_INTERNALS__"].init; - ThreadPoolWorker = - globalThis["__WASMER_INTERNALS__"].ThreadPoolWorker; - } + // HACK: How we load our imports will change depending on how the code + // is deployed. If we are being used in "wasm-pack test" then we can + // access the things we want from the imported object. Otherwise, if we + // are being used from a bundler, chances are those things are no longer + // directly accessible and we need to get them from the + // __WASMER_INTERNALS__ object stashed on the global scope when the + // package was imported. + let init; + let ThreadPoolWorker; + if ("ThreadPoolWorker" in imported) { + if ("default" in imported) { + init = imported.default; + } else if ("init" in imported) { + init = imported.init; + } + ThreadPoolWorker = imported.ThreadPoolWorker; + } else { + init = globalThis["__WASMER_INTERNALS__"].init; + ThreadPoolWorker = globalThis["__WASMER_INTERNALS__"].ThreadPoolWorker; + } - await init(module, memory); + await init({ module: module, memory: memory }); - worker = new ThreadPoolWorker(id); + worker = new ThreadPoolWorker(id); - // Now that we're initialized, we need to handle any buffered messages - for (const msg of pendingMessages.splice(0, pendingMessages.length)) { - await worker.handle(msg); - } - } else { - // Handle the message like normal. - await handleMessage(ev.data); + // Now that we're initialized, we need to handle any buffered messages + for (const msg of pendingMessages.splice(0, pendingMessages.length)) { + await worker.handle(msg); } + } else { + // Handle the message like normal. + await handleMessage(ev.data); + } }; diff --git a/tests/directory.test.ts b/tests/directory.test.ts index 9dfc3955..38b1a087 100644 --- a/tests/directory.test.ts +++ b/tests/directory.test.ts @@ -5,58 +5,60 @@ const decoder = new TextDecoder("utf-8"); const encoder = new TextEncoder(); const initialized = (async () => { - await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url)); - initializeLogger("warn"); + await init({ + module: new URL("../dist/wasmer_js_bg.wasm", import.meta.url), + }); + initializeLogger("warn"); })(); describe("In-Memory Directory", function () { - this.timeout("60s").beforeAll(async () => await initialized); + this.timeout("60s").beforeAll(async () => await initialized); - it("read empty dir", async () => { - const dir = new Directory(); + it("read empty dir", async () => { + const dir = new Directory(); - const contents = await dir.readDir("/"); + const contents = await dir.readDir("/"); - expect(contents.length).to.equal(0); - }); - - it("can round-trip a file", async () => { - const dir = new Directory(); + expect(contents.length).to.equal(0); + }); - await dir.writeFile("/file.txt", encoder.encode("Hello, World!")); - const contents = await dir.readFile("/file.txt"); + it("can round-trip a file", async () => { + const dir = new Directory(); - expect(decoder.decode(contents)).to.equal("Hello, World!"); - }); + await dir.writeFile("/file.txt", encoder.encode("Hello, World!")); + const contents = await dir.readFile("/file.txt"); - it("read dir with file", async () => { - const dir = new Directory(); + expect(decoder.decode(contents)).to.equal("Hello, World!"); + }); - await dir.writeFile("/file.txt", new Uint8Array()); - const contents = await dir.readDir("/"); + it("read dir with file", async () => { + const dir = new Directory(); - expect(contents).to.deep.equal([{ name: "file.txt", type: "file" }]); - }); + await dir.writeFile("/file.txt", new Uint8Array()); + const contents = await dir.readDir("/"); - it("create child dir", async () => { - const dir = new Directory(); + expect(contents).to.deep.equal([{ name: "file.txt", type: "file" }]); + }); - await dir.createDir("/tmp/"); + it("create child dir", async () => { + const dir = new Directory(); - expect(await dir.readDir("/")).to.deep.equal([ - { name: "tmp", type: "dir" }, - ]); - }); + await dir.createDir("/tmp/"); - it("can be created with DirectoryInit", async () => { - const dir = new Directory({ - "/file.txt": "file", - "/another/nested/file.txt": "another", - }); + expect(await dir.readDir("/")).to.deep.equal([ + { name: "tmp", type: "dir" }, + ]); + }); - expect(await dir.readTextFile("/file.txt")).to.equal("file"); - expect(await dir.readTextFile("/another/nested/file.txt")).to.equal( - "another", - ); + it("can be created with DirectoryInit", async () => { + const dir = new Directory({ + "/file.txt": "file", + "/another/nested/file.txt": "another", }); + + expect(await dir.readTextFile("/file.txt")).to.equal("file"); + expect(await dir.readTextFile("/another/nested/file.txt")).to.equal( + "another", + ); + }); }); diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 2ab6de6b..6646e7a1 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -5,371 +5,371 @@ const encoder = new TextEncoder(); const decoder = new TextDecoder("utf-8"); const initialized = (async () => { - await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url)); - initializeLogger("warn"); + await init({ + module: new URL("../dist/wasmer_js_bg.wasm", import.meta.url), + }); + initializeLogger("warn"); })(); const ansiEscapeCode = /\u001B\[[\d;]*[JDm]/g; describe("Wasmer.spawn", function () { - this.timeout("120s").beforeAll(async () => { - await initialized; + this.timeout("120s").beforeAll(async () => { + await initialized; + }); + + it("Can run quickjs", async () => { + const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); + const instance = await pkg.commands["quickjs"].run({ + args: ["--eval", "console.log('Hello, World!')"], }); - - it("Can run quickjs", async () => { - const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); - const instance = await pkg.commands["quickjs"].run({ - args: ["--eval", "console.log('Hello, World!')"], - }); - const output = await instance.wait(); - - expect(output.code).to.equal(0); - expect(output.ok).to.be.true; - expect(output.stdout).to.equal("Hello, World!\n"); - expect(output.stderr.length).to.equal(0); - }); - - it("Can capture exit codes", async () => { - const pkg = await Wasmer.fromRegistry("saghul/quickjs"); - const instance = await pkg.commands["quickjs"].run({ - args: ["--std", "--eval", "std.exit(42)"], - }); - const output = await instance.wait(); - - expect(output.code).to.equal(42); - expect(output.ok).to.be.false; - expect(output.stdout.length).to.equal(0); - expect(output.stderr.length).to.equal(0); + const output = await instance.wait(); + + expect(output.code).to.equal(0); + expect(output.ok).to.be.true; + expect(output.stdout).to.equal("Hello, World!\n"); + expect(output.stderr.length).to.equal(0); + }); + + it("Can capture exit codes", async () => { + const pkg = await Wasmer.fromRegistry("saghul/quickjs"); + const instance = await pkg.commands["quickjs"].run({ + args: ["--std", "--eval", "std.exit(42)"], }); - - it("Can pass stdin to a dumb echo program", async () => { - const pkg = await Wasmer.fromRegistry( - "christoph/wasix-test-stdinout@0.1.1", - ); - const instance = await pkg.commands["stdinout-loop"].run({ - stdin: "Hello\nWorld!\n", - }); - - const output = await instance.wait(); - - expect(output.ok).to.be.true; - expect(output.code).to.equal(0); - expect(output.stderr).to.equal("Hello\n\nWorld!\n\n"); + const output = await instance.wait(); + + expect(output.code).to.equal(42); + expect(output.ok).to.be.false; + expect(output.stdout.length).to.equal(0); + expect(output.stderr.length).to.equal(0); + }); + + it("Can pass stdin to a dumb echo program", async () => { + const pkg = await Wasmer.fromRegistry( + "christoph/wasix-test-stdinout@0.1.1", + ); + const instance = await pkg.commands["stdinout-loop"].run({ + stdin: "Hello\nWorld!\n", }); - it("Can communicate with a dumb echo program", async () => { - // First, start our program in the background - const pkg = await Wasmer.fromRegistry( - "christoph/wasix-test-stdinout@0.1.1", - ); - const instance = await pkg.commands["stdinout-loop"].run(); - - const stdin = instance.stdin!.getWriter(); - const stdout = new BufReader(instance.stdout); - - await stdin.write(encoder.encode("Hello,")); - await stdin.write(encoder.encode(" World!\n")); - // Note: The program is reading line-by-line, so we can't do - // stdout.readLine() before the "\n" was sent - expect(await stdout.readLine()).to.equal("Hello, World!\n"); - await stdin.write(encoder.encode("Done\n")); - expect(await stdout.readLine()).to.equal("Done\n"); - - // Closing stdin will break out of the reading loop - await stdin.close(); - // And wait for the program to exit - const output = await instance.wait(); - - expect(output.ok).to.be.true; - expect(output.code).to.equal(0); + const output = await instance.wait(); + + expect(output.ok).to.be.true; + expect(output.code).to.equal(0); + expect(output.stderr).to.equal("Hello\n\nWorld!\n\n"); + }); + + it("Can communicate with a dumb echo program", async () => { + // First, start our program in the background + const pkg = await Wasmer.fromRegistry( + "christoph/wasix-test-stdinout@0.1.1", + ); + const instance = await pkg.commands["stdinout-loop"].run(); + + const stdin = instance.stdin!.getWriter(); + const stdout = new BufReader(instance.stdout); + + await stdin.write(encoder.encode("Hello,")); + await stdin.write(encoder.encode(" World!\n")); + // Note: The program is reading line-by-line, so we can't do + // stdout.readLine() before the "\n" was sent + expect(await stdout.readLine()).to.equal("Hello, World!\n"); + await stdin.write(encoder.encode("Done\n")); + expect(await stdout.readLine()).to.equal("Done\n"); + + // Closing stdin will break out of the reading loop + await stdin.close(); + // And wait for the program to exit + const output = await instance.wait(); + + expect(output.ok).to.be.true; + expect(output.code).to.equal(0); + }); + + it("Can communicate with a TTY-aware program", async () => { + // First, start QuickJS up in the background + const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); + const instance = await pkg.commands["quickjs"].run({ + args: ["--interactive", "--std"], }); - it("Can communicate with a TTY-aware program", async () => { - // First, start QuickJS up in the background - const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); - const instance = await pkg.commands["quickjs"].run({ - args: ["--interactive", "--std"], - }); - - const stdin = new RealisticWriter(instance.stdin!); - const stdout = new BufReader(instance.stdout); - - // QuickJS prints a prompt when it first starts up. Let's read it. - expect(await stdout.readLine()).to.equal( - 'QuickJS - Type "\\h" for help\n', - ); - - // Then, send a command to the REPL - await stdin.writeln("console.log('Hello, World!')"); - // The TTY echoes back a bunch of escape codes and stuff. - expect(await stdout.readAnsiLine()).to.equal( - "qjs > console.log('Hello, World!')\n", - ); - // Random newline. - expect(await stdout.readLine()).to.equal("\n"); - // QuickJS also echoes your input back. Because reasons. - expect(await stdout.readAnsiLine()).to.equal( - "console.log('Hello, World!')\n", - ); - // We get the text we asked for. - expect(await stdout.readLine()).to.equal("Hello, World!\n"); - // console.log() evaluates to undefined - expect(await stdout.readAnsiLine()).to.equal("undefined\n"); - - // Now that the first command is done, QuickJS will show the prompt - // again - expect(await stdout.readAnsiLine()).to.equal("qjs > \n"); - - // We're all done. Tell the command to exit. - await stdin.writeln("std.exit(42)"); - // Our input gets echoed by the TTY - expect(await stdout.readLine()).to.equal("qjs > std.exit(42)\n"); - // Random newline. - expect(await stdout.readLine()).to.equal("\n"); - // QuickJS printed the command we just ran. - expect(await stdout.readAnsiLine()).to.equal("std.exit(42)\n"); - - // Wait for the instance to shut down. - await stdin.close(); - const output = await instance.wait(); - - expect(output.code).to.equal(42); - expect(output.stderr).to.equal(""); + const stdin = new RealisticWriter(instance.stdin!); + const stdout = new BufReader(instance.stdout); + + // QuickJS prints a prompt when it first starts up. Let's read it. + expect(await stdout.readLine()).to.equal('QuickJS - Type "\\h" for help\n'); + + // Then, send a command to the REPL + await stdin.writeln("console.log('Hello, World!')"); + // The TTY echoes back a bunch of escape codes and stuff. + expect(await stdout.readAnsiLine()).to.equal( + "qjs > console.log('Hello, World!')\n", + ); + // Random newline. + expect(await stdout.readLine()).to.equal("\n"); + // QuickJS also echoes your input back. Because reasons. + expect(await stdout.readAnsiLine()).to.equal( + "console.log('Hello, World!')\n", + ); + // We get the text we asked for. + expect(await stdout.readLine()).to.equal("Hello, World!\n"); + // console.log() evaluates to undefined + expect(await stdout.readAnsiLine()).to.equal("undefined\n"); + + // Now that the first command is done, QuickJS will show the prompt + // again + expect(await stdout.readAnsiLine()).to.equal("qjs > \n"); + + // We're all done. Tell the command to exit. + await stdin.writeln("std.exit(42)"); + // Our input gets echoed by the TTY + expect(await stdout.readLine()).to.equal("qjs > std.exit(42)\n"); + // Random newline. + expect(await stdout.readLine()).to.equal("\n"); + // QuickJS printed the command we just ran. + expect(await stdout.readAnsiLine()).to.equal("std.exit(42)\n"); + + // Wait for the instance to shut down. + await stdin.close(); + const output = await instance.wait(); + + expect(output.code).to.equal(42); + expect(output.stderr).to.equal(""); + }); + + it("can communicate with a subprocess interactively", async () => { + const pkg = await Wasmer.fromRegistry("sharrattj/bash"); + const instance = await pkg.commands["bash"].run({ + uses: ["christoph/wasix-test-stdinout@0.1.1"], }); - it("can communicate with a subprocess interactively", async () => { - const pkg = await Wasmer.fromRegistry("sharrattj/bash"); - const instance = await pkg.commands["bash"].run({ - uses: ["christoph/wasix-test-stdinout@0.1.1"], - }); - - const stdin = new RealisticWriter(instance.stdin!); - const stdout = new BufReader(instance.stdout); - - // Start the stdinout-loop program - await stdin.writeln("stdinout-loop"); - // echo from the TTY - expect(await stdout.readLine()).to.equal("stdinout-loop\n"); - // The stdinout-loop program should be running now. Let's send it - // something - await stdin.writeln("First"); - // It printed back our input - expect(await stdout.readLine()).to.equal("\n"); - expect(await stdout.readLine()).to.equal("First\n"); - // Write the next line of input - await stdin.writeln("Second"); - // Echo from program - expect(await stdout.readLine()).to.equal("\n"); - expect(await stdout.readLine()).to.equal("Second\n"); - - await stdin.close(); - const output = await instance.wait(); - - expect(output.code).to.equal(0); - // It looks like bash does its own TTY echoing, except it printed to - // stderr instead of stdout like wasmer_wasix::os::Tty - expect(output.stderr).to.equal( - "bash-5.1# stdinout-loop\n\n\nFirst\n\n\n\nSecond\n\n\n\nbash-5.1# exit\n", - ); + const stdin = new RealisticWriter(instance.stdin!); + const stdout = new BufReader(instance.stdout); + + // Start the stdinout-loop program + await stdin.writeln("stdinout-loop"); + // echo from the TTY + expect(await stdout.readLine()).to.equal("stdinout-loop\n"); + // The stdinout-loop program should be running now. Let's send it + // something + await stdin.writeln("First"); + // It printed back our input + expect(await stdout.readLine()).to.equal("\n"); + expect(await stdout.readLine()).to.equal("First\n"); + // Write the next line of input + await stdin.writeln("Second"); + // Echo from program + expect(await stdout.readLine()).to.equal("\n"); + expect(await stdout.readLine()).to.equal("Second\n"); + + await stdin.close(); + const output = await instance.wait(); + + expect(output.code).to.equal(0); + // It looks like bash does its own TTY echoing, except it printed to + // stderr instead of stdout like wasmer_wasix::os::Tty + expect(output.stderr).to.equal( + "bash-5.1# stdinout-loop\n\n\nFirst\n\n\n\nSecond\n\n\n\nbash-5.1# exit\n", + ); + }); + + it("Can communicate with Python", async () => { + // First, start python up in the background + const pkg = await Wasmer.fromRegistry("python/python@0.1.0"); + const instance = await pkg.entrypoint!.run(); + + const stdin = new RealisticWriter(instance.stdin!); + const stdout = new BufReader(instance.stdout); + const stderr = new BufReader(instance.stderr); + + // First, we'll read the prompt + expect(await stderr.readLine()).to.equal( + "Python 3.6.7 (default, Feb 14 2020, 03:17:48) \n", + ); + expect(await stderr.readLine()).to.equal( + "[Wasm WASI vClang 9.0.0 (https://github.com/llvm/llvm-project 0399d5a9682b3cef7 on generic\n", + ); + expect(await stderr.readLine()).to.equal( + 'Type "help", "copyright", "credits" or "license" for more information.\n', + ); + + // Then, send the command to the REPL + await stdin.writeln("import sys"); + // TTY echo + expect(await stdout.readLine()).to.equal("import sys\n"); + await stdin.writeln("print(1 + 1)"); + // TTY echo + expect(await stdout.readLine()).to.equal("\n"); + expect(await stdout.readLine()).to.equal("print(1 + 1)\n"); + // Our output + expect(await stdout.readLine()).to.equal("\n"); + expect(await stdout.readLine()).to.equal("2\n"); + // We've done what we want, so let's shut it down + await stdin.writeln("sys.exit(42)"); + // TTY echo + expect(await stdout.readLine()).to.equal("sys.exit(42)\n"); + expect(await stdout.readLine()).to.equal("\n"); + + // Wait for the instance to shut down. + await stdin.close(); + await stdout.close(); + await stderr.close(); + const output = await instance.wait(); + + expect(output.ok).to.be.false; + expect(output.code).to.equal(42); + expect(output.stdout).to.equal(""); + // Python prints the prompts to stderr, but our TTY handling prints + // echoed characters to stdout + expect(output.stderr).to.equal(">>> >>> >>> >>> >>> "); + }); + + it("can see a mounted directory", async () => { + const dir = new Directory(); + const pkg = await Wasmer.fromRegistry("sharrattj/coreutils"); + + const instance = await pkg.commands["ls"].run({ + args: ["/"], + mount: { "/mounted": dir }, }); - - it("Can communicate with Python", async () => { - // First, start python up in the background - const pkg = await Wasmer.fromRegistry("python/python@0.1.0"); - const instance = await pkg.entrypoint!.run(); - - const stdin = new RealisticWriter(instance.stdin!); - const stdout = new BufReader(instance.stdout); - const stderr = new BufReader(instance.stderr); - - // First, we'll read the prompt - expect(await stderr.readLine()).to.equal( - "Python 3.6.7 (default, Feb 14 2020, 03:17:48) \n", - ); - expect(await stderr.readLine()).to.equal( - "[Wasm WASI vClang 9.0.0 (https://github.com/llvm/llvm-project 0399d5a9682b3cef7 on generic\n", - ); - expect(await stderr.readLine()).to.equal( - 'Type "help", "copyright", "credits" or "license" for more information.\n', - ); - - // Then, send the command to the REPL - await stdin.writeln("import sys"); - // TTY echo - expect(await stdout.readLine()).to.equal("import sys\n"); - await stdin.writeln("print(1 + 1)"); - // TTY echo - expect(await stdout.readLine()).to.equal("\n"); - expect(await stdout.readLine()).to.equal("print(1 + 1)\n"); - // Our output - expect(await stdout.readLine()).to.equal("\n"); - expect(await stdout.readLine()).to.equal("2\n"); - // We've done what we want, so let's shut it down - await stdin.writeln("sys.exit(42)"); - // TTY echo - expect(await stdout.readLine()).to.equal("sys.exit(42)\n"); - expect(await stdout.readLine()).to.equal("\n"); - - // Wait for the instance to shut down. - await stdin.close(); - await stdout.close(); - await stderr.close(); - const output = await instance.wait(); - - expect(output.ok).to.be.false; - expect(output.code).to.equal(42); - expect(output.stdout).to.equal(""); - // Python prints the prompts to stderr, but our TTY handling prints - // echoed characters to stdout - expect(output.stderr).to.equal(">>> >>> >>> >>> >>> "); + const output = await instance.wait(); + + const stdout = output.stdout; + expect(stdout).to.contain("mounted"); + expect(output.ok).to.be.true; + }); + + it("can see files in a mounted directory", async () => { + const dir = new Directory(); + await dir.writeFile("/file.txt", new Uint8Array()); + const pkg = await Wasmer.fromRegistry("sharrattj/coreutils"); + + const instance = await pkg.commands["ls"].run({ + stdin: "", + args: ["/mounted"], + mount: { "/mounted": dir }, }); + const output = await instance.wait(); - it("can see a mounted directory", async () => { - const dir = new Directory(); - const pkg = await Wasmer.fromRegistry("sharrattj/coreutils"); + expect(output.ok).to.be.true; + expect(output.stdout).to.equal("file.txt\n"); + expect(output.stderr).to.equal(""); + }); - const instance = await pkg.commands["ls"].run({ - args: ["/"], - mount: { "/mounted": dir }, - }); - const output = await instance.wait(); + it("can read from a mounted file", async () => { + const dir = new Directory(); + await dir.writeFile("/file.txt", encoder.encode("Hello, World!")); + const pkg = await Wasmer.fromRegistry("sharrattj/coreutils"); - const stdout = output.stdout; - expect(stdout).to.contain("mounted"); - expect(output.ok).to.be.true; + const instance = await pkg.commands["cat"].run({ + args: ["/mounted/file.txt"], + mount: { "/mounted": dir }, }); + const output = await instance.wait(); - it("can see files in a mounted directory", async () => { - const dir = new Directory(); - await dir.writeFile("/file.txt", new Uint8Array()); - const pkg = await Wasmer.fromRegistry("sharrattj/coreutils"); - - const instance = await pkg.commands["ls"].run({ - stdin: "", - args: ["/mounted"], - mount: { "/mounted": dir }, - }); - const output = await instance.wait(); - - expect(output.ok).to.be.true; - expect(output.stdout).to.equal("file.txt\n"); - expect(output.stderr).to.equal(""); - }); + const stdout = output.stdout; + expect(stdout).to.equal("Hello, World!"); + expect(output.ok).to.be.true; + }); - it("can read from a mounted file", async () => { - const dir = new Directory(); - await dir.writeFile("/file.txt", encoder.encode("Hello, World!")); - const pkg = await Wasmer.fromRegistry("sharrattj/coreutils"); + it("can delete files from a mounted directory", async () => { + const dir = new Directory(); + await dir.writeFile("/file.txt", encoder.encode("Hello, World!")); + const pkg = await Wasmer.fromRegistry("sharrattj/coreutils"); - const instance = await pkg.commands["cat"].run({ - args: ["/mounted/file.txt"], - mount: { "/mounted": dir }, - }); - const output = await instance.wait(); - - const stdout = output.stdout; - expect(stdout).to.equal("Hello, World!"); - expect(output.ok).to.be.true; + const instance = await pkg.commands["rm"].run({ + args: ["/mounted/file.txt"], + mount: { "/mounted": dir }, }); + const output = await instance.wait(); - it("can delete files from a mounted directory", async () => { - const dir = new Directory(); - await dir.writeFile("/file.txt", encoder.encode("Hello, World!")); - const pkg = await Wasmer.fromRegistry("sharrattj/coreutils"); + expect(dir.readDir("/")).to.be.empty; + expect(output.ok).to.be.true; + }); - const instance = await pkg.commands["rm"].run({ - args: ["/mounted/file.txt"], - mount: { "/mounted": dir }, - }); - const output = await instance.wait(); + it("can delete directories from a mounted directory", async () => { + const dir = new Directory(); + await dir.createDir("/nested-dir"); + const pkg = await Wasmer.fromRegistry("sharrattj/coreutils"); - expect(dir.readDir("/")).to.be.empty; - expect(output.ok).to.be.true; + const instance = await pkg.commands["rmdir"].run({ + args: ["/mounted/nested-dir"], + mount: { "/mounted": dir }, }); + const output = await instance.wait(); - it("can delete directories from a mounted directory", async () => { - const dir = new Directory(); - await dir.createDir("/nested-dir"); - const pkg = await Wasmer.fromRegistry("sharrattj/coreutils"); + expect(dir.readDir("/")).to.be.empty; + expect(output.ok).to.be.true; + }); - const instance = await pkg.commands["rmdir"].run({ - args: ["/mounted/nested-dir"], - mount: { "/mounted": dir }, - }); - const output = await instance.wait(); + it("can write to a mounted directory", async () => { + const dir = new Directory(); + const pkg = await Wasmer.fromRegistry("sharrattj/bash"); - expect(dir.readDir("/")).to.be.empty; - expect(output.ok).to.be.true; + const instance = await pkg.commands["bash"].run({ + args: ["-c", "echo 'Something else' > /mounted/another-file.txt"], + mount: { "/mounted": dir }, }); + await instance.wait(); - it("can write to a mounted directory", async () => { - const dir = new Directory(); - const pkg = await Wasmer.fromRegistry("sharrattj/bash"); - - const instance = await pkg.commands["bash"].run({ - args: ["-c", "echo 'Something else' > /mounted/another-file.txt"], - mount: { "/mounted": dir }, - }); - await instance.wait(); - - expect(await dir.readTextFile("/another-file.txt")).to.equal( - "Something else\n", - ); - }); + expect(await dir.readTextFile("/another-file.txt")).to.equal( + "Something else\n", + ); + }); }); // FIXME: Re-enable these test and move it to the "Wasmer.spawn" test suite // when we fix TTY handling with static inputs. describe.skip("failing tty handling tests", function () { - let wasmer: Wasmer; + let wasmer: Wasmer; - this.timeout("120s").beforeAll(async () => { - await initialized; + this.timeout("120s").beforeAll(async () => { + await initialized; - // Note: technically we should use a separate Wasmer instance so tests can't - // interact with each other, but in this case the caching benefits mean we - // complete in tens of seconds rather than several minutes. - wasmer = new Wasmer(); - }); + // Note: technically we should use a separate Wasmer instance so tests can't + // interact with each other, but in this case the caching benefits mean we + // complete in tens of seconds rather than several minutes. + wasmer = new Wasmer(); + }); - it("can run a bash session non-interactively", async () => { - const pkg = await Wasmer.fromRegistry("sharrattj/bash"); + it("can run a bash session non-interactively", async () => { + const pkg = await Wasmer.fromRegistry("sharrattj/bash"); - const instance = await pkg.commands["bash"].run({ - stdin: "ls / && exit 42\n", - }); - console.log("Spawned"); + const instance = await pkg.commands["bash"].run({ + stdin: "ls / && exit 42\n", + }); + console.log("Spawned"); - const { code, stdout, stderr } = await instance.wait(); + const { code, stdout, stderr } = await instance.wait(); - expect(code).to.equal(42); - expect(stdout).to.equal("bin\nlib\ntmp\n"); - expect(stderr).to.equal(""); - }); + expect(code).to.equal(42); + expect(stdout).to.equal("bin\nlib\ntmp\n"); + expect(stderr).to.equal(""); + }); - it.skip("can communicate with a subprocess", async () => { - const pkg = await Wasmer.fromRegistry("sharrattj/bash"); + it.skip("can communicate with a subprocess", async () => { + const pkg = await Wasmer.fromRegistry("sharrattj/bash"); - const instance = await pkg.commands["bash"].run({ - uses: ["christoph/wasix-test-stdinout@0.1.1"], - }); + const instance = await pkg.commands["bash"].run({ + uses: ["christoph/wasix-test-stdinout@0.1.1"], + }); - const stdin = instance.stdin!.getWriter(); - const stdout = new BufReader(instance.stdout); + const stdin = instance.stdin!.getWriter(); + const stdout = new BufReader(instance.stdout); - await stdin.write(encoder.encode("stdinout-loop\n")); - // the stdinout-loop program should be running now - await stdin.write(encoder.encode("First\n")); - expect(await stdout.readLine()).to.equal("First\n"); - await stdin.write(encoder.encode("Second\n")); - expect(await stdout.readLine()).to.equal("Second\n"); + await stdin.write(encoder.encode("stdinout-loop\n")); + // the stdinout-loop program should be running now + await stdin.write(encoder.encode("First\n")); + expect(await stdout.readLine()).to.equal("First\n"); + await stdin.write(encoder.encode("Second\n")); + expect(await stdout.readLine()).to.equal("Second\n"); - await stdin.close(); - const output = await instance.wait(); + await stdin.close(); + const output = await instance.wait(); - console.log(output); - expect(output.code).to.equal(0); - }); + console.log(output); + expect(output.code).to.equal(0); + }); }); /** @@ -380,216 +380,216 @@ describe.skip("failing tty handling tests", function () { * the other end. */ class RealisticWriter { - private encoder = new TextEncoder(); - constructor(readonly stream: WritableStream) {} + private encoder = new TextEncoder(); + constructor(readonly stream: WritableStream) {} - async writeln(text: string): Promise { - await this.write(text + "\r\n"); - } + async writeln(text: string): Promise { + await this.write(text + "\r\n"); + } - async write(text: string): Promise { - const writer = this.stream.getWriter(); - - try { - const message = this.encoder.encode(text); - - for (const byte of message) { - await writer.ready; - await writer.write(Uint8Array.of(byte)); - } - } finally { - // Note: wait for all bytes to be flushed before returning. - await writer.ready; - writer.releaseLock(); - } - } + async write(text: string): Promise { + const writer = this.stream.getWriter(); - async close(): Promise { - await this.stream.close(); + try { + const message = this.encoder.encode(text); + + for (const byte of message) { + await writer.ready; + await writer.write(Uint8Array.of(byte)); + } + } finally { + // Note: wait for all bytes to be flushed before returning. + await writer.ready; + writer.releaseLock(); } + } + + async close(): Promise { + await this.stream.close(); + } } /** * A streams adapter to simplify consuming them interactively. */ class BufReader { - private buffer?: Uint8Array; - private decoder = new TextDecoder(); - private chunks: AsyncGenerator; - - constructor( - stream: ReadableStream, - private verbose: boolean = false, - ) { - this.chunks = chunks(stream); - } + private buffer?: Uint8Array; + private decoder = new TextDecoder(); + private chunks: AsyncGenerator; - /** - * Consume data until the next newline character or EOF. - */ - async readLine(): Promise { - const pieces: Uint8Array[] = []; - - while ((await this.fillBuffer()) && this.buffer) { - const ASCII_NEWLINE = 0x0a; - const position = this.buffer.findIndex(b => b == ASCII_NEWLINE); - - this.log({ buffer: this.peek(), position }); - - if (position < 0) { - // Consume the entire chunk. - pieces.push(this.consume()); - } else { - // Looks like we've found the newline. Consume everything up to - // and including it, and stop reading. - pieces.push(this.consume(position + 1)); - break; - } - } - - const line = pieces.map(piece => this.decoder.decode(piece)).join(""); - this.log({ line }); - return line; + constructor( + stream: ReadableStream, + private verbose: boolean = false, + ) { + this.chunks = chunks(stream); + } + + /** + * Consume data until the next newline character or EOF. + */ + async readLine(): Promise { + const pieces: Uint8Array[] = []; + + while ((await this.fillBuffer()) && this.buffer) { + const ASCII_NEWLINE = 0x0a; + const position = this.buffer.findIndex(b => b == ASCII_NEWLINE); + + this.log({ buffer: this.peek(), position }); + + if (position < 0) { + // Consume the entire chunk. + pieces.push(this.consume()); + } else { + // Looks like we've found the newline. Consume everything up to + // and including it, and stop reading. + pieces.push(this.consume(position + 1)); + break; + } } - /** - * Read a line of text, interpreting the ANSI escape codes for clearing the - * line and stripping any other formatting. - */ - async readAnsiLine(): Promise { - const rawLine = await this.readLine(); - - // Note: QuickJS uses the "move left by n columns" escape code for - // clearing the line. - const pieces = rawLine.split(/\x1b\[\d+D/); - const lastPiece = pieces.pop() || rawLine; - return lastPiece.replace(ansiEscapeCode, ""); + const line = pieces.map(piece => this.decoder.decode(piece)).join(""); + this.log({ line }); + return line; + } + + /** + * Read a line of text, interpreting the ANSI escape codes for clearing the + * line and stripping any other formatting. + */ + async readAnsiLine(): Promise { + const rawLine = await this.readLine(); + + // Note: QuickJS uses the "move left by n columns" escape code for + // clearing the line. + const pieces = rawLine.split(/\x1b\[\d+D/); + const lastPiece = pieces.pop() || rawLine; + return lastPiece.replace(ansiEscapeCode, ""); + } + + async readToEnd(): Promise { + // Note: We want to merge all chunks into a single buffer and decode in + // one hit. Otherwise we'll have O(n²) performance issues and run the + // risk of chunks not being aligned to UTF-8 code point boundaries when + // we decode them. + + const chunks: Uint8Array[] = []; + + while (await this.fillBuffer()) { + this.log({ + len: chunks.length + 1, + nextChunk: this.peek(), + }); + chunks.push(this.consume()); } - async readToEnd(): Promise { - // Note: We want to merge all chunks into a single buffer and decode in - // one hit. Otherwise we'll have O(n²) performance issues and run the - // risk of chunks not being aligned to UTF-8 code point boundaries when - // we decode them. - - const chunks: Uint8Array[] = []; - - while (await this.fillBuffer()) { - this.log({ - len: chunks.length + 1, - nextChunk: this.peek(), - }); - chunks.push(this.consume()); - } - - const totalByteCount = chunks.reduce( - (accumulator, element) => accumulator + element.byteLength, - 0, - ); - const buffer = new Uint8Array(totalByteCount); - let offset = 0; - - for (const chunk of chunks) { - buffer.set(chunk, offset); - offset += chunk.byteLength; - } - - const text = this.decoder.decode(buffer); - this.log({ text }); - return text; - } + const totalByteCount = chunks.reduce( + (accumulator, element) => accumulator + element.byteLength, + 0, + ); + const buffer = new Uint8Array(totalByteCount); + let offset = 0; - async close() { - await this.chunks.return(undefined); + for (const chunk of chunks) { + buffer.set(chunk, offset); + offset += chunk.byteLength; } - peek(): string | undefined { - if (this.buffer) { - return this.decoder.decode(this.buffer); - } - } + const text = this.decoder.decode(buffer); + this.log({ text }); + return text; + } - /** - * Try to read more bytes into the buffer if it was previously empty. - * @returns whether the buffer was filled. - */ - private async fillBuffer() { - if (this.buffer && this.buffer.byteLength > 0) { - return true; - } - - const chunk = await this.chunks.next(); - - if (chunk.value && chunk.value.byteLength > 0) { - this.buffer = chunk.value; - return true; - } else { - this.buffer = undefined; - return false; - } + async close() { + await this.chunks.return(undefined); + } + + peek(): string | undefined { + if (this.buffer) { + return this.decoder.decode(this.buffer); + } + } + + /** + * Try to read more bytes into the buffer if it was previously empty. + * @returns whether the buffer was filled. + */ + private async fillBuffer() { + if (this.buffer && this.buffer.byteLength > 0) { + return true; } - /** - * Remove some bytes from the front of `this.buffer`, returning the bytes - * that were removed. The buffer will be set to `undefined` if all bytes - * have been consumed. - * - * @param amount The number of bytes to remove - * @returns The removed bytes - * @throws If the buffer was `undefined` or more bytes were requested than - * are available - */ - private consume(amount?: number): Uint8Array { - if (!this.buffer) { - throw new Error(); - } - - if (amount) { - if (amount > this.buffer.byteLength) { - throw new Error(); - } - - const before = this.buffer.slice(0, amount); - const rest = this.buffer.slice(amount); - this.buffer = rest.length > 0 ? rest : undefined; - - return before; - } else { - const buffer = this.buffer; - this.buffer = undefined; - return buffer; - } + const chunk = await this.chunks.next(); + + if (chunk.value && chunk.value.byteLength > 0) { + this.buffer = chunk.value; + return true; + } else { + this.buffer = undefined; + return false; + } + } + + /** + * Remove some bytes from the front of `this.buffer`, returning the bytes + * that were removed. The buffer will be set to `undefined` if all bytes + * have been consumed. + * + * @param amount The number of bytes to remove + * @returns The removed bytes + * @throws If the buffer was `undefined` or more bytes were requested than + * are available + */ + private consume(amount?: number): Uint8Array { + if (!this.buffer) { + throw new Error(); } - /** - * Log a piece of information if the `verbose` flag is set. - */ - private log(value: any) { - if (this.verbose) { - console.log(value); - } + if (amount) { + if (amount > this.buffer.byteLength) { + throw new Error(); + } + + const before = this.buffer.slice(0, amount); + const rest = this.buffer.slice(amount); + this.buffer = rest.length > 0 ? rest : undefined; + + return before; + } else { + const buffer = this.buffer; + this.buffer = undefined; + return buffer; + } + } + + /** + * Log a piece of information if the `verbose` flag is set. + */ + private log(value: any) { + if (this.verbose) { + console.log(value); } + } } /** * Turn a ReadableStream into an async generator. */ async function* chunks( - stream: ReadableStream, + stream: ReadableStream, ): AsyncGenerator { - const reader = stream.getReader(); + const reader = stream.getReader(); - try { - let chunk: ReadableStreamReadResult; + try { + let chunk: ReadableStreamReadResult; - do { - chunk = await reader.read(); + do { + chunk = await reader.read(); - if (chunk.value) { - yield chunk.value; - } - } while (!chunk.done); - } finally { - reader.releaseLock(); - } + if (chunk.value) { + yield chunk.value; + } + } while (!chunk.done); + } finally { + reader.releaseLock(); + } } diff --git a/tests/registry.test.ts b/tests/registry.test.ts index d8e21963..c56a9501 100644 --- a/tests/registry.test.ts +++ b/tests/registry.test.ts @@ -1,546 +1,525 @@ import { assert, expect } from "@esm-bundle/chai"; import { init, initializeLogger, Wasmer } from ".."; -const pkg_name = "test-js-sdk-pkg" -const app_name = "test-js-sdk-app" - -describe("Registry", function() { - this.timeout("60s"); - const WASMER_TEST_OWNER = process.env.WASMER_TEST_OWNER; - - it("can be initialized", async () => { - await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url), undefined, - { registry_url: "https://registry.wasmer.wtf/graphql", token: process.env.WASMER_TOKEN } - ); - initializeLogger("error"); - }) - - it("can use unnamed packages twice", async () => { - let manifest = - { - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "import os; print([f for f in os.walk('/public')]); " - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - }, - - "fs": { - "public": { - "index.html": "Hello, js!" - } - } - } - - let wasmerPackage = await Wasmer.createPackage(manifest); - - let appConfig = { - name: app_name, - owner: WASMER_TEST_OWNER, - package: wasmerPackage, - env: [["test", "new_value"]], - default: true - - }; - - await Wasmer.deployApp(appConfig); - - let appConfig2 = { - name: app_name + "2", - owner: WASMER_TEST_OWNER, - package: wasmerPackage, - env: [["test", "new_value"]], - default: true - - }; - - await Wasmer.deployApp(appConfig2); - }) - - it("can deploy unnamed packages", async () => { - let manifest = - { - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "import os; print([f for f in os.walk('/public')]); " - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - }, - - "fs": { - "public": { - "index.html": "Hello, js!" - } - } - } - - let wasmerPackage = await Wasmer.createPackage(manifest); - - let appConfig = { - name: app_name, - owner: WASMER_TEST_OWNER, - package: wasmerPackage, - env: [["test", "new_value"]], - default: true - - }; - - await Wasmer.deployApp(appConfig); - - }) - - - it("has global context", async () => { - let v = (globalThis as any)["__WASMER_REGISTRY__"]; - expect(typeof v != "undefined") - }); - - - it("can create a package with atoms", async () => { - let manifest = - { - "module": [ - { - "name": "test", - "abi": "wasi", - "source": new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]) - }, - { - "name": "other-test", - "source": new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]) - } - ], - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "import os; print([f for f in os.walk('/public')]); " - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - }, - - "fs": { - "public": { - "index.html": "Hello, js!", - "index_timestamp.html": { data: "Hello, js!", modified: 987656789 }, - "index_date.html": { data: "Hello, js!", modified: new Date() } - } - } - } - await Wasmer.createPackage(manifest); - }) - - - it("can create an unnamed package", async () => { - let manifest = - { - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "import os; print([f for f in os.walk('/public')]); " - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - }, - - "fs": { - "public": { - "index.html": "Hello, js!", - "index_timestamp.html": { data: "Hello, js!", modified: 987656789 }, - "index_date.html": { data: "Hello, js!", modified: new Date() } - } - } - } - await Wasmer.createPackage(manifest); - }) - - - - it("can't publish unnamed packages", async () => { - let manifest = - { - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "import os; print([f for f in os.walk('/public')]); " - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - }, - - "fs": { - "public": { - "index.html": "Hello, js!" - } - } - } - - let wasmerPackage = await Wasmer.createPackage(manifest); - try { - await Wasmer.publishPackage(wasmerPackage); - assert.fail("publishes the package", "should not publish the package") - } catch { - return - } - }) - - it("can publish named packages", async () => { - let manifest = - { - "package": { - "name": WASMER_TEST_OWNER + "/" + pkg_name - }, - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "import os; print([f for f in os.walk('/public')]); " - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - }, - - "fs": { - "public": { - "index.html": "Hello, js!" - } - } - } - - let wasmerPackage = await Wasmer.createPackage(manifest); - await Wasmer.publishPackage(wasmerPackage); - }) - - - it("can deploy apps", async () => { - let appConfig = { - name: app_name, - owner: WASMER_TEST_OWNER, - package: "sha256:34a3b5f5a9108c2b258eb51e9d0978b6778a3696b9c7e713adab33293fb5e4f1", - env: [["test", "new_value"]], - default: true - - }; - - await Wasmer.deployApp(appConfig); - }) - - it("fails deploying apps with unpublished packages", async () => { - - let manifest = - { - "package": { - "name": WASMER_TEST_OWNER + "/" + pkg_name - }, - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "import os; print([f for f in os.walk('/public')]); " - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - }, - - "fs": { - "public": { - "index.html": "A totally new file!" - } - } - } - - let wasmerPackage = await Wasmer.createPackage(manifest); - - let appConfig = { - name: app_name, - owner: WASMER_TEST_OWNER, - package: wasmerPackage, - env: [["test", "new_value"]], - default: true - - }; - - try { - await Wasmer.deployApp(appConfig); - assert.fail("deploys the app", "should not deploy the app") - } catch { - return - } - }) - - - it("can deploy apps with user-created packages", async () => { - - let manifest = - { - "package": { - "name": WASMER_TEST_OWNER + "/" + pkg_name - }, - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "import os; print([f for f in os.walk('/public')]); " - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - }, - - "fs": { - "public": { - "index.html": "Hello, js!" - } - } - } - - let wasmerPackage = await Wasmer.createPackage(manifest); - await Wasmer.publishPackage(wasmerPackage); - - let appConfig = - { - name: app_name, - owner: WASMER_TEST_OWNER, - package: wasmerPackage, - env: [["test", "new_value"]], - default: true - - }; - - await Wasmer.deployApp(appConfig); - }) - - it("can run user-created packages", async () => { - let manifest = { - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "print(\"hello, js!\")" - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - } - }; - - let pkg = await Wasmer.createPackage(manifest); - let instance = await pkg.commands["hello"].run(); - - const output = await instance.wait(); - assert(output.stdout === "hello, js!\n") - }) - - - - it("can mount fs", async () => { - let manifest = - { - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "import os; print([f for f in os.walk('/public')]); " - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - }, - - "fs": { - "public": { - "index.html": "Hello, js!" - } - } - } - - - let pkg = await Wasmer.createPackage(manifest); - let instance = await pkg.commands["hello"].run(); - - const output = await instance.wait(); - assert(output.stdout.includes("index.html")) - }) - - it("can read metadata", async () => { - let manifest = - { - "package": { - "readme": "This is my readme!", - "license": { data: "This is my license!", modified: new Date() } - }, - "command": [ - { - "module": "wasmer/python:python", - "name": "hello", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-c", - "import os; print([f for f in os.walk('/public')]); " - ] - } - } - } - ], - "dependencies": { - "wasmer/python": "3.12.5+build.7" - }, - - "fs": { - "public": { - "index.html": "Hello, js!" - } - } - } - - - let pkg = await Wasmer.createPackage(manifest); - let instance = await pkg.commands["hello"].run(); - - const output = await instance.wait(); - assert(output.stdout.includes("index.html")) - }) - - it("can deploy a php app", async () => { - - let manifest = { - "package": { "name": WASMER_TEST_OWNER + "/" }, - "command": [ - { - "module": "php/php:php", - "name": "run", - "runner": "wasi", - "annotations": { - "wasi": { - "main-args": [ - "-t", - "/app", - "-S", - "localhost:8080" - ] - } - } - } - ], - "dependencies": { - "php/php": "=8.3.4" - }, - "fs": { - "/app": { - "index.php": " { + await init({ + module: new URL("../dist/wasmer_js_bg.wasm", import.meta.url), + registryUrl: "https://registry.wasmer.wtf/graphql", + token: process.env.WASMER_TOKEN, + }); + + initializeLogger("error"); + }); + + const WASMER_TEST_OWNER = process.env.WASMER_TEST_OWNER; + + it("has global context", async () => { + let v = (globalThis as any)["__WASMER_REGISTRY__"]; + expect(typeof v != "undefined"); + }); + + it("can deploy apps", async () => { + let appConfig = { + name: app_name, + owner: WASMER_TEST_OWNER, + package: + "sha256:34a3b5f5a9108c2b258eb51e9d0978b6778a3696b9c7e713adab33293fb5e4f1", + env: [["test", "new_value"]], + default: true, + }; + + await Wasmer.deployApp(appConfig); + }); + + it("can create a package with atoms", async () => { + let manifest = { + module: [ + { + name: "test", + abi: "wasi", + source: new Uint8Array([ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, + ]), + }, + { + name: "other-test", + source: new Uint8Array([ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, + ]), + }, + ], + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); ", + ], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + + fs: { + public: { + "index.html": "Hello, js!", + "index_timestamp.html": { + data: "Hello, js!", + modified: 987656789, + }, + "index_date.html": { + data: "Hello, js!", + modified: new Date(), + }, + }, + }, + }; + await Wasmer.createPackage(manifest); + }); + + it("can create an unnamed package", async () => { + let manifest = { + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); ", + ], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + + fs: { + public: { + "index.html": "Hello, js!", + "index_timestamp.html": { + data: "Hello, js!", + modified: 987656789, + }, + "index_date.html": { + data: "Hello, js!", + modified: new Date(), + }, + }, + }, + }; + + await Wasmer.createPackage(manifest); + }); + + it("can use unnamed packages twice", async () => { + let manifest = { + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); ", + ], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + + fs: { + public: { + "index.html": "Hello, js!", + }, + }, + }; + + let wasmerPackage = await Wasmer.createPackage(manifest); + + let appConfig = { + name: app_name, + owner: WASMER_TEST_OWNER, + package: wasmerPackage, + env: [["test", "new_value"]], + default: true, + }; + + await Wasmer.deployApp(appConfig); + + let appConfig2 = { + name: app_name + "2", + owner: WASMER_TEST_OWNER, + package: wasmerPackage, + env: [["test", "new_value"]], + default: true, + }; + + await Wasmer.deployApp(appConfig2); + }); + + it("can deploy unnamed packages", async () => { + let manifest = { + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); ", + ], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + + fs: { + public: { + "index.html": "Hello, js!", + }, + }, + }; + + let wasmerPackage = await Wasmer.createPackage(manifest); + + let appConfig = { + name: app_name, + owner: WASMER_TEST_OWNER, + package: wasmerPackage, + env: [["test", "new_value"]], + default: true, + }; + + await Wasmer.deployApp(appConfig); + }); + + it("can't publish unnamed packages", async () => { + let manifest = { + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); ", + ], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + + fs: { + public: { + "index.html": "Hello, js!", + }, + }, + }; + + let wasmerPackage = await Wasmer.createPackage(manifest); + try { + await Wasmer.publishPackage(wasmerPackage); + assert.fail("publishes the package", "should not publish the package"); + } catch { + return; + } + }); + + it("can publish named packages", async () => { + let manifest = { + package: { + name: WASMER_TEST_OWNER + "/" + pkg_name, + }, + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); ", + ], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + + fs: { + public: { + "index.html": "Hello, js!", + }, + }, + }; + + let wasmerPackage = await Wasmer.createPackage(manifest); + await Wasmer.publishPackage(wasmerPackage); + }); + + it("fails deploying apps with unpublished packages", async () => { + let manifest = { + package: { + name: WASMER_TEST_OWNER + "/" + pkg_name, + }, + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); ", + ], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + + fs: { + public: { + "index.html": "A totally new file!", + }, + }, + }; + + let wasmerPackage = await Wasmer.createPackage(manifest); + + let appConfig = { + name: app_name, + owner: WASMER_TEST_OWNER, + package: wasmerPackage, + env: [["test", "new_value"]], + default: true, + }; + + try { + await Wasmer.deployApp(appConfig); + assert.fail("deploys the app", "should not deploy the app"); + } catch { + return; + } + }); + + it("can deploy apps with user-created packages", async () => { + let manifest = { + package: { + name: WASMER_TEST_OWNER + "/" + pkg_name, + }, + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); ", + ], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + + fs: { + public: { + "index.html": "Hello, js!", + }, + }, + }; + + let wasmerPackage = await Wasmer.createPackage(manifest); + await Wasmer.publishPackage(wasmerPackage); + + let appConfig = { + name: app_name, + owner: WASMER_TEST_OWNER, + package: wasmerPackage, + env: [["test", "new_value"]], + default: true, + }; + + await Wasmer.deployApp(appConfig); + }); + + it("can run user-created packages", async () => { + let manifest = { + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": ["-c", 'print("hello, js!")'], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + }; + + let pkg = await Wasmer.createPackage(manifest); + let instance = await pkg.commands["hello"].run(); + + const output = await instance.wait(); + assert(output.stdout === "hello, js!\n"); + }); + + it("can mount fs", async () => { + let manifest = { + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); ", + ], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + + fs: { + public: { + "index.html": "Hello, js!", + }, + }, + }; + + let pkg = await Wasmer.createPackage(manifest); + let instance = await pkg.commands["hello"].run(); + + const output = await instance.wait(); + assert(output.stdout.includes("index.html")); + }); + + it("can read metadata", async () => { + let manifest = { + package: { + readme: "This is my readme!", + license: { data: "This is my license!", modified: new Date() }, + }, + command: [ + { + module: "wasmer/python:python", + name: "hello", + runner: "wasi", + annotations: { + wasi: { + "main-args": [ + "-c", + "import os; print([f for f in os.walk('/public')]); ", + ], + }, + }, + }, + ], + dependencies: { + "wasmer/python": "3.12.5+build.7", + }, + + fs: { + public: { + "index.html": "Hello, js!", + }, + }, + }; + + let pkg = await Wasmer.createPackage(manifest); + let instance = await pkg.commands["hello"].run(); + + const output = await instance.wait(); + assert(output.stdout.includes("index.html")); + }); + + it("can deploy a php app", async () => { + let manifest = { + package: { name: WASMER_TEST_OWNER + "/" }, + command: [ + { + module: "php/php:php", + name: "run", + runner: "wasi", + annotations: { + wasi: { + "main-args": ["-t", "/app", "-S", "localhost:8080"], + }, + }, + }, + ], + dependencies: { + "php/php": "=8.3.4", + }, + fs: { + "/app": { + "index.php": " { - await init(new URL("../dist/wasmer_js_bg.wasm", import.meta.url)); - initializeLogger("warn"); + await init({ + module: new URL("../dist/wasmer_js_bg.wasm", import.meta.url), + }); + initializeLogger("warn"); })(); describe("run", function () { - this.timeout("60s").beforeAll(async () => await initialized); + this.timeout("60s").beforeAll(async () => await initialized); - it("can execute a noop program", async () => { - const noop = `( + it("can execute a noop program", async () => { + const noop = `( module (memory $memory 0) (export "memory" (memory $memory)) (func (export "_start") nop) )`; - const wasm = wat2wasm(noop); - const module = await WebAssembly.compile(wasm); + const wasm = wat2wasm(noop); + const module = await WebAssembly.compile(wasm); - const instance = await runWasix(module, { program: "noop" }); - const output = await instance.wait(); + const instance = await runWasix(module, { program: "noop" }); + const output = await instance.wait(); - expect(output.ok).to.be.true; - expect(output.code).to.equal(0); - }); - - it("can start quickjs", async () => { - const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); - const quickjs = pkg.commands["quickjs"].binary(); + expect(output.ok).to.be.true; + expect(output.code).to.equal(0); + }); - const instance = await runWasix(quickjs, { - program: "quickjs", - args: ["--eval", "console.log('Hello, World!')"], - }); - const output = await instance.wait(); + it("can start quickjs", async () => { + const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); + const quickjs = pkg.commands["quickjs"].binary(); - expect(output.ok).to.be.true; - expect(output.code).to.equal(0); - expect(output.stdout).to.contain("Hello, World!"); - expect(output.stderr).to.be.empty; + const instance = await runWasix(quickjs, { + program: "quickjs", + args: ["--eval", "console.log('Hello, World!')"], }); - - it("can read directories", async () => { - const dir = new Directory(); - await dir.writeFile("/file.txt", ""); - const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); - const quickjs = pkg.commands["quickjs"].binary(); - - const instance = await runWasix(quickjs, { - program: "quickjs", - args: [ - "--std", - "--eval", - `[dirs] = os.readdir("/"); console.log(dirs.join("\\n"))`, - ], - mount: { - "/mount": dir, - }, - }); - const output = await instance.wait(); - - expect(output.ok).to.be.true; - expect(output.code).to.equal(0); - expect(output.stdout).to.equal(".\n..\nmount\n"); - expect(output.stderr).to.be.empty; + const output = await instance.wait(); + + expect(output.ok).to.be.true; + expect(output.code).to.equal(0); + expect(output.stdout).to.contain("Hello, World!"); + expect(output.stderr).to.be.empty; + }); + + it("can read directories", async () => { + const dir = new Directory(); + await dir.writeFile("/file.txt", ""); + const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); + const quickjs = pkg.commands["quickjs"].binary(); + + const instance = await runWasix(quickjs, { + program: "quickjs", + args: [ + "--std", + "--eval", + `[dirs] = os.readdir("/"); console.log(dirs.join("\\n"))`, + ], + mount: { + "/mount": dir, + }, }); - - it("can read files", async () => { - const tmp = new Directory(); - await tmp.writeFile("/file.txt", "Hello, World!"); - const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); - const quickjs = pkg.commands["quickjs"].binary(); - - const instance = await runWasix(quickjs, { - program: "quickjs", - args: [ - "--std", - "--eval", - `console.log(std.open('/tmp/file.txt', "r").readAsString())`, - ], - mount: { - "/tmp": tmp, - }, - }); - const output = await instance.wait(); - - expect(output.ok).to.be.true; - expect(output.code).to.equal(0); - expect(output.stdout).to.equal("Hello, World!\n"); - expect(output.stderr).to.be.empty; + const output = await instance.wait(); + + expect(output.ok).to.be.true; + expect(output.code).to.equal(0); + expect(output.stdout).to.equal(".\n..\nmount\n"); + expect(output.stderr).to.be.empty; + }); + + it("can read files", async () => { + const tmp = new Directory(); + await tmp.writeFile("/file.txt", "Hello, World!"); + const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); + const quickjs = pkg.commands["quickjs"].binary(); + + const instance = await runWasix(quickjs, { + program: "quickjs", + args: [ + "--std", + "--eval", + `console.log(std.open('/tmp/file.txt', "r").readAsString())`, + ], + mount: { + "/tmp": tmp, + }, }); - - it("can read files mounted using DirectoryInit", async () => { - const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); - const quickjs = pkg.commands["quickjs"].binary(); - - const instance = await runWasix(quickjs, { - program: "quickjs", - args: [ - "--std", - "--eval", - `console.log(std.open('/tmp/file.txt', "r").readAsString())`, - ], - mount: { - "/tmp": { - "file.txt": "Hello, World!", - }, - }, - }); - const output = await instance.wait(); - - expect(output.ok).to.be.true; - expect(output.code).to.equal(0); - expect(output.stdout).to.equal("Hello, World!\n"); - expect(output.stderr).to.be.empty; + const output = await instance.wait(); + + expect(output.ok).to.be.true; + expect(output.code).to.equal(0); + expect(output.stdout).to.equal("Hello, World!\n"); + expect(output.stderr).to.be.empty; + }); + + it("can read files mounted using DirectoryInit", async () => { + const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); + const quickjs = pkg.commands["quickjs"].binary(); + + const instance = await runWasix(quickjs, { + program: "quickjs", + args: [ + "--std", + "--eval", + `console.log(std.open('/tmp/file.txt', "r").readAsString())`, + ], + mount: { + "/tmp": { + "file.txt": "Hello, World!", + }, + }, }); - - it("can write files", async () => { - const dir = new Directory(); - const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); - const quickjs = pkg.commands["quickjs"].binary(); - const script = ` + const output = await instance.wait(); + + expect(output.ok).to.be.true; + expect(output.code).to.equal(0); + expect(output.stdout).to.equal("Hello, World!\n"); + expect(output.stderr).to.be.empty; + }); + + it("can write files", async () => { + const dir = new Directory(); + const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); + const quickjs = pkg.commands["quickjs"].binary(); + const script = ` const f = std.open('/mount/file.txt', 'w'); f.puts('Hello, World!'); f.close(); `; - const instance = await runWasix(quickjs, { - program: "quickjs", - args: ["--std", "--eval", script], - mount: { - "/mount": dir, - }, - }); - const output = await instance.wait(); - - expect(output.ok).to.be.true; - expect(await dir.readTextFile("/file.txt")).to.equal("Hello, World!"); + const instance = await runWasix(quickjs, { + program: "quickjs", + args: ["--std", "--eval", script], + mount: { + "/mount": dir, + }, }); + const output = await instance.wait(); - it("can accept strings as stdin", async () => { - const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); - const quickjs = pkg.commands["quickjs"].binary(); + expect(output.ok).to.be.true; + expect(await dir.readTextFile("/file.txt")).to.equal("Hello, World!"); + }); - const instance = await runWasix(quickjs, { - program: "quickjs", - args: ["--interactive", "--std"], - stdin: "console.log('Hello, World!');\nstd.exit(42)\n", - }); - const output = await instance.wait(); + it("can accept strings as stdin", async () => { + const pkg = await Wasmer.fromRegistry("saghul/quickjs@0.0.3"); + const quickjs = pkg.commands["quickjs"].binary(); - expect(output.code).to.equal(42); - expect(output.stdout).to.contain("Hello, World!\n"); - expect(output.stderr).to.be.empty; + const instance = await runWasix(quickjs, { + program: "quickjs", + args: ["--interactive", "--std"], + stdin: "console.log('Hello, World!');\nstd.exit(42)\n", }); -}) + const output = await instance.wait(); + + expect(output.code).to.equal(42); + expect(output.stdout).to.contain("Hello, World!\n"); + expect(output.stderr).to.be.empty; + }); +}); From 26cfdc36c422ff32a90a1218494583100a151c70 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 5 Aug 2024 18:18:56 +0200 Subject: [PATCH 22/32] chore: Make linter happy --- .../coi-serviceworker.js | 302 ++++++------ examples/cdn-coi-serviceworker/index.html | 57 +-- examples/cdn/index.html | 57 +-- examples/ffmpeg-react/index.html | 2 +- examples/ffmpeg-react/postcss.config.js | 2 +- examples/ffmpeg-react/src/App.tsx | 437 +++++++++--------- examples/ffmpeg-react/src/hooks.tsx | 189 ++++---- examples/ffmpeg-react/src/main.tsx | 14 +- examples/ffmpeg-react/vite.config.ts | 4 +- examples/markdown-editor-improved/index.html | 23 +- examples/markdown-editor-improved/index.ts | 64 +-- examples/markdown-editor-improved/style.css | 55 +-- .../markdown-editor-improved/vite.config.js | 11 +- examples/markdown-editor/index.html | 23 +- examples/markdown-editor/index.ts | 60 +-- examples/markdown-editor/style.css | 55 +-- examples/markdown-editor/vite.config.js | 53 ++- examples/simplimage/index.tsx | 18 +- examples/wasmer.sh/index.html | 37 +- examples/wasmer.sh/index.ts | 56 +-- examples/wasmer.sh/vite.config.js | 18 +- rollup.config.mjs | 150 +++--- web-dev-server.config.mjs | 55 ++- 23 files changed, 906 insertions(+), 836 deletions(-) diff --git a/examples/cdn-coi-serviceworker/coi-serviceworker.js b/examples/cdn-coi-serviceworker/coi-serviceworker.js index 45e36b97..49eecc7a 100644 --- a/examples/cdn-coi-serviceworker/coi-serviceworker.js +++ b/examples/cdn-coi-serviceworker/coi-serviceworker.js @@ -1,147 +1,167 @@ // File taken from: https://github.com/gzuidhof/coi-serviceworker/tree/master /*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */ let coepCredentialless = false; -if (typeof window === 'undefined') { - self.addEventListener("install", () => self.skipWaiting()); - self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim())); - - self.addEventListener("message", (ev) => { - if (!ev.data) { - return; - } else if (ev.data.type === "deregister") { - self.registration - .unregister() - .then(() => { - return self.clients.matchAll(); - }) - .then(clients => { - clients.forEach((client) => client.navigate(client.url)); - }); - } else if (ev.data.type === "coepCredentialless") { - coepCredentialless = ev.data.value; - } - }); - - self.addEventListener("fetch", function (event) { - const r = event.request; - if (r.cache === "only-if-cached" && r.mode !== "same-origin") { - return; - } - - const request = (coepCredentialless && r.mode === "no-cors") - ? new Request(r, { - credentials: "omit", - }) - : r; - event.respondWith( - fetch(request) - .then((response) => { - if (response.status === 0) { - return response; - } - - const newHeaders = new Headers(response.headers); - newHeaders.set("Cross-Origin-Embedder-Policy", - coepCredentialless ? "credentialless" : "require-corp" - ); - if (!coepCredentialless) { - newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin"); - } - newHeaders.set("Cross-Origin-Opener-Policy", "same-origin"); - - return new Response(response.body, { - status: response.status, - statusText: response.statusText, - headers: newHeaders, - }); - }) - .catch((e) => console.error(e)) - ); - }); - +if (typeof window === "undefined") { + self.addEventListener("install", () => self.skipWaiting()); + self.addEventListener("activate", event => + event.waitUntil(self.clients.claim()), + ); + + self.addEventListener("message", ev => { + if (!ev.data) { + return; + } else if (ev.data.type === "deregister") { + self.registration + .unregister() + .then(() => { + return self.clients.matchAll(); + }) + .then(clients => { + clients.forEach(client => client.navigate(client.url)); + }); + } else if (ev.data.type === "coepCredentialless") { + coepCredentialless = ev.data.value; + } + }); + + self.addEventListener("fetch", function (event) { + const r = event.request; + if (r.cache === "only-if-cached" && r.mode !== "same-origin") { + return; + } + + const request = + coepCredentialless && r.mode === "no-cors" + ? new Request(r, { + credentials: "omit", + }) + : r; + event.respondWith( + fetch(request) + .then(response => { + if (response.status === 0) { + return response; + } + + const newHeaders = new Headers(response.headers); + newHeaders.set( + "Cross-Origin-Embedder-Policy", + coepCredentialless ? "credentialless" : "require-corp", + ); + if (!coepCredentialless) { + newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin"); + } + newHeaders.set("Cross-Origin-Opener-Policy", "same-origin"); + + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: newHeaders, + }); + }) + .catch(e => console.error(e)), + ); + }); } else { - (() => { - const reloadedBySelf = window.sessionStorage.getItem("coiReloadedBySelf"); - window.sessionStorage.removeItem("coiReloadedBySelf"); - const coepDegrading = (reloadedBySelf == "coepdegrade"); - - // You can customize the behavior of this script through a global `coi` variable. - const coi = { - shouldRegister: () => !reloadedBySelf, - shouldDeregister: () => false, - coepCredentialless: () => true, - coepDegrade: () => true, - doReload: () => window.location.reload(), - quiet: false, - ...window.coi - }; - - const n = navigator; - const controlling = n.serviceWorker && n.serviceWorker.controller; - - // Record the failure if the page is served by serviceWorker. - if (controlling && !window.crossOriginIsolated) { - window.sessionStorage.setItem("coiCoepHasFailed", "true"); - } - const coepHasFailed = window.sessionStorage.getItem("coiCoepHasFailed"); - - if (controlling) { - // Reload only on the first failure. - const reloadToDegrade = coi.coepDegrade() && !( - coepDegrading || window.crossOriginIsolated + (() => { + const reloadedBySelf = window.sessionStorage.getItem("coiReloadedBySelf"); + window.sessionStorage.removeItem("coiReloadedBySelf"); + const coepDegrading = reloadedBySelf == "coepdegrade"; + + // You can customize the behavior of this script through a global `coi` variable. + const coi = { + shouldRegister: () => !reloadedBySelf, + shouldDeregister: () => false, + coepCredentialless: () => true, + coepDegrade: () => true, + doReload: () => window.location.reload(), + quiet: false, + ...window.coi, + }; + + const n = navigator; + const controlling = n.serviceWorker && n.serviceWorker.controller; + + // Record the failure if the page is served by serviceWorker. + if (controlling && !window.crossOriginIsolated) { + window.sessionStorage.setItem("coiCoepHasFailed", "true"); + } + const coepHasFailed = window.sessionStorage.getItem("coiCoepHasFailed"); + + if (controlling) { + // Reload only on the first failure. + const reloadToDegrade = + coi.coepDegrade() && !(coepDegrading || window.crossOriginIsolated); + n.serviceWorker.controller.postMessage({ + type: "coepCredentialless", + value: + reloadToDegrade || (coepHasFailed && coi.coepDegrade()) + ? false + : coi.coepCredentialless(), + }); + if (reloadToDegrade) { + !coi.quiet && console.log("Reloading page to degrade COEP."); + window.sessionStorage.setItem("coiReloadedBySelf", "coepdegrade"); + coi.doReload("coepdegrade"); + } + + if (coi.shouldDeregister()) { + n.serviceWorker.controller.postMessage({ type: "deregister" }); + } + } + + // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are + // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here. + if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return; + + if (!window.isSecureContext) { + !coi.quiet && + console.log( + "COOP/COEP Service Worker not registered, a secure context is required.", + ); + return; + } + + // In some environments (e.g. Firefox private mode) this won't be available + if (!n.serviceWorker) { + !coi.quiet && + console.error( + "COOP/COEP Service Worker not registered, perhaps due to private mode.", + ); + return; + } + + n.serviceWorker.register(window.document.currentScript.src).then( + registration => { + !coi.quiet && + console.log( + "COOP/COEP Service Worker registered", + registration.scope, + ); + + registration.addEventListener("updatefound", () => { + !coi.quiet && + console.log( + "Reloading page to make use of updated COOP/COEP Service Worker.", ); - n.serviceWorker.controller.postMessage({ - type: "coepCredentialless", - value: (reloadToDegrade || coepHasFailed && coi.coepDegrade()) - ? false - : coi.coepCredentialless(), - }); - if (reloadToDegrade) { - !coi.quiet && console.log("Reloading page to degrade COEP."); - window.sessionStorage.setItem("coiReloadedBySelf", "coepdegrade"); - coi.doReload("coepdegrade"); - } - - if (coi.shouldDeregister()) { - n.serviceWorker.controller.postMessage({ type: "deregister" }); - } - } - - // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are - // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here. - if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return; - - if (!window.isSecureContext) { - !coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required."); - return; - } - - // In some environments (e.g. Firefox private mode) this won't be available - if (!n.serviceWorker) { - !coi.quiet && console.error("COOP/COEP Service Worker not registered, perhaps due to private mode."); - return; + window.sessionStorage.setItem("coiReloadedBySelf", "updatefound"); + coi.doReload(); + }); + + // If the registration is active, but it's not controlling the page + if (registration.active && !n.serviceWorker.controller) { + !coi.quiet && + console.log( + "Reloading page to make use of COOP/COEP Service Worker.", + ); + window.sessionStorage.setItem("coiReloadedBySelf", "notcontrolling"); + coi.doReload(); } - - n.serviceWorker.register(window.document.currentScript.src).then( - (registration) => { - !coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope); - - registration.addEventListener("updatefound", () => { - !coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker."); - window.sessionStorage.setItem("coiReloadedBySelf", "updatefound"); - coi.doReload(); - }); - - // If the registration is active, but it's not controlling the page - if (registration.active && !n.serviceWorker.controller) { - !coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker."); - window.sessionStorage.setItem("coiReloadedBySelf", "notcontrolling"); - coi.doReload(); - } - }, - (err) => { - !coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err); - } - ); - })(); -} \ No newline at end of file + }, + err => { + !coi.quiet && + console.error("COOP/COEP Service Worker failed to register:", err); + }, + ); + })(); +} diff --git a/examples/cdn-coi-serviceworker/index.html b/examples/cdn-coi-serviceworker/index.html index 1e9754b4..d7430116 100644 --- a/examples/cdn-coi-serviceworker/index.html +++ b/examples/cdn-coi-serviceworker/index.html @@ -1,44 +1,45 @@ - + - - - - + + + Wasmer JavaScript SDK - + - +

- - + diff --git a/examples/cdn/index.html b/examples/cdn/index.html index 1151b2be..66009d44 100644 --- a/examples/cdn/index.html +++ b/examples/cdn/index.html @@ -1,43 +1,44 @@ - + - - - - + + + Wasmer JavaScript SDK - + - +

- - + diff --git a/examples/ffmpeg-react/index.html b/examples/ffmpeg-react/index.html index 1bddefeb..b2ea281e 100644 --- a/examples/ffmpeg-react/index.html +++ b/examples/ffmpeg-react/index.html @@ -1,4 +1,4 @@ - + diff --git a/examples/ffmpeg-react/postcss.config.js b/examples/ffmpeg-react/postcss.config.js index 2e7af2b7..2aa7205d 100644 --- a/examples/ffmpeg-react/postcss.config.js +++ b/examples/ffmpeg-react/postcss.config.js @@ -3,4 +3,4 @@ export default { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/examples/ffmpeg-react/src/App.tsx b/examples/ffmpeg-react/src/App.tsx index 45b730d1..c63bb6ad 100644 --- a/examples/ffmpeg-react/src/App.tsx +++ b/examples/ffmpeg-react/src/App.tsx @@ -4,260 +4,257 @@ import { useDropzone } from "react-dropzone"; import { useWasmerPackage, useWasmerSdk } from "./hooks"; interface VideoProps { - preview: boolean; - fileSrc?: ReturnType; - file: File | null; + preview: boolean; + fileSrc?: ReturnType; + file: File | null; } enum PROCESSING_STATUS { - NOT_STARTED, - RUNNING, - FINISHED, - FAILED, + NOT_STARTED, + RUNNING, + FINISHED, + FAILED, } export default function App() { - const sdk = useWasmerSdk(); - const pkg = useWasmerPackage("wasmer/ffmpeg"); - console.log(pkg); + const sdk = useWasmerSdk(); + const pkg = useWasmerPackage("wasmer/ffmpeg"); + console.log(pkg); - const [userVideo, setUserVideo] = useState({ - preview: false, - fileSrc: "", - file: null, - }); - - const [outputVideo, setOutputVideo] = useState({ - preview: false, - fileSrc: "", - file: null, - }); + const [userVideo, setUserVideo] = useState({ + preview: false, + fileSrc: "", + file: null, + }); - const [processingStatus, setProcessingStatus] = useState( - PROCESSING_STATUS.NOT_STARTED, - ); + const [outputVideo, setOutputVideo] = useState({ + preview: false, + fileSrc: "", + file: null, + }); - const previewVideoRef = useRef(null); - const outputVideoRef = useRef(null); + const [processingStatus, setProcessingStatus] = useState( + PROCESSING_STATUS.NOT_STARTED, + ); - const [fileU8Arr, setFileU8Arr] = useState(null); + const previewVideoRef = useRef(null); + const outputVideoRef = useRef(null); - const onDrop = useCallback((acceptedFiles: File[]) => { - const file = acceptedFiles[0]; - console.log(file); - setUserVideo({ - preview: true, - fileSrc: URL.createObjectURL(file), - file, - }); - }, []); + const [fileU8Arr, setFileU8Arr] = useState(null); - const { getRootProps, getInputProps } = useDropzone({ - maxFiles: 1, - accept: { - "video/*": [], - }, - onDrop, + const onDrop = useCallback((acceptedFiles: File[]) => { + const file = acceptedFiles[0]; + console.log(file); + setUserVideo({ + preview: true, + fileSrc: URL.createObjectURL(file), + file, }); + }, []); - useEffect(() => { - if (!userVideo.fileSrc || !previewVideoRef.current) return; - previewVideoRef.current?.load(); - }, [userVideo.fileSrc]); + const { getRootProps, getInputProps } = useDropzone({ + maxFiles: 1, + accept: { + "video/*": [], + }, + onDrop, + }); - useEffect(() => { - if (!outputVideo.fileSrc || !outputVideoRef.current) return; - outputVideoRef.current?.load(); - }, [outputVideo.fileSrc]); + useEffect(() => { + if (!userVideo.fileSrc || !previewVideoRef.current) return; + previewVideoRef.current?.load(); + }, [userVideo.fileSrc]); + useEffect(() => { + if (!outputVideo.fileSrc || !outputVideoRef.current) return; + outputVideoRef.current?.load(); + }, [outputVideo.fileSrc]); - useEffect(() => { - if (!userVideo.file) { - return; - } - const reader = new FileReader(); + useEffect(() => { + if (!userVideo.file) { + return; + } + const reader = new FileReader(); - reader.onload = function (e: ProgressEvent) { - if (!e.target) return; - const arrayBuffer = e.target.result as ArrayBuffer; - const u8arr = new Uint8Array(arrayBuffer); + reader.onload = function (e: ProgressEvent) { + if (!e.target) return; + const arrayBuffer = e.target.result as ArrayBuffer; + const u8arr = new Uint8Array(arrayBuffer); - setFileU8Arr(u8arr); - }; + setFileU8Arr(u8arr); + }; - reader.readAsArrayBuffer(userVideo.file); - }, [userVideo.file]); + reader.readAsArrayBuffer(userVideo.file); + }, [userVideo.file]); - useEffect(() => { - if (processingStatus === PROCESSING_STATUS.FAILED) { - setTimeout(() => { - setProcessingStatus(PROCESSING_STATUS.NOT_STARTED); - }, 4000); - } - }, [processingStatus]); - const runFfmpegProcessing = async () => { - if (!fileU8Arr || sdk.state != "loaded") return; + useEffect(() => { + if (processingStatus === PROCESSING_STATUS.FAILED) { + setTimeout(() => { + setProcessingStatus(PROCESSING_STATUS.NOT_STARTED); + }, 4000); + } + }, [processingStatus]); + const runFfmpegProcessing = async () => { + if (!fileU8Arr || sdk.state != "loaded") return; - setProcessingStatus(PROCESSING_STATUS.RUNNING); + setProcessingStatus(PROCESSING_STATUS.RUNNING); - const tmp = new sdk.Directory(); - await tmp.writeFile("input.mp4", fileU8Arr); + const tmp = new sdk.Directory(); + await tmp.writeFile("input.mp4", fileU8Arr); - if (pkg.state != "loaded" || !pkg.pkg.entrypoint) return; + if (pkg.state != "loaded" || !pkg.pkg.entrypoint) return; - const instance = await pkg.entrypoint!.run({ - args: [ - "-i", - "/videos/input.mp4", - "-vf", - "format=gray", - "/videos/output.mp4", - ], - mount: { "/videos": tmp }, - }); + const instance = await pkg.entrypoint!.run({ + args: [ + "-i", + "/videos/input.mp4", + "-vf", + "format=gray", + "/videos/output.mp4", + ], + mount: { "/videos": tmp }, + }); - await instance.stdin?.close(); - let output = await instance.wait(); + await instance.stdin?.close(); + let output = await instance.wait(); - if (output.ok) { - console.log(output.stderr); - const contents = await tmp.readFile("output.mp4"); + if (output.ok) { + console.log(output.stderr); + const contents = await tmp.readFile("output.mp4"); - const u8arr = new Uint8Array(contents.buffer); - const file = new File([u8arr], "output.mp4", { - type: "video/mp4", - }); - setOutputVideo({ - preview: true, - fileSrc: URL.createObjectURL(file), - file, - }); - setProcessingStatus(PROCESSING_STATUS.FINISHED); - } else { - console.log(output.stderr); - setProcessingStatus(PROCESSING_STATUS.FAILED); - } - }; + const u8arr = new Uint8Array(contents.buffer); + const file = new File([u8arr], "output.mp4", { + type: "video/mp4", + }); + setOutputVideo({ + preview: true, + fileSrc: URL.createObjectURL(file), + file, + }); + setProcessingStatus(PROCESSING_STATUS.FINISHED); + } else { + console.log(output.stderr); + setProcessingStatus(PROCESSING_STATUS.FAILED); + } + }; - return ( -
- {!!userVideo.fileSrc ? ( -
-
- - Input video - - -
+ return ( +
+ {!!userVideo.fileSrc ? ( +
+
+ + Input video + + +
- {!!outputVideo.fileSrc && - processingStatus === PROCESSING_STATUS.FINISHED && ( -
- - Output Video - - -
- )} -
- ) : ( -
- -
-
-
-
-
+ {!!outputVideo.fileSrc && + processingStatus === PROCESSING_STATUS.FINISHED && ( +
+ + Output Video + + +
)} +
+ ) : ( +
+ +
+
+
+
+
+ )} - + - { - outputVideo.fileSrc && - Download - } -
- ); + {outputVideo.fileSrc && ( + + Download + + )} + + ); } function ProcessingStatus({ status }: { status: PROCESSING_STATUS }) { - switch (status) { - case PROCESSING_STATUS.FAILED: - return ( -
- Processing failed. Please try again. -
- ); - case PROCESSING_STATUS.FINISHED: - return
Processing finished.
; - case PROCESSING_STATUS.RUNNING: - return ( -
- FFmpeg is processing. Please wait. -
- ); - case PROCESSING_STATUS.NOT_STARTED: - return
Run FFmpeg Processing
; - } + switch (status) { + case PROCESSING_STATUS.FAILED: + return ( +
Processing failed. Please try again.
+ ); + case PROCESSING_STATUS.FINISHED: + return
Processing finished.
; + case PROCESSING_STATUS.RUNNING: + return ( +
+ FFmpeg is processing. Please wait. +
+ ); + case PROCESSING_STATUS.NOT_STARTED: + return
Run FFmpeg Processing
; + } } diff --git a/examples/ffmpeg-react/src/hooks.tsx b/examples/ffmpeg-react/src/hooks.tsx index 3e1fd5d6..6031a605 100644 --- a/examples/ffmpeg-react/src/hooks.tsx +++ b/examples/ffmpeg-react/src/hooks.tsx @@ -1,17 +1,26 @@ import React, { useContext, useEffect, useState } from "react"; -import type { Command, Runtime, Wasmer, init, initializeLogger } from "@wasmer/sdk"; +import type { + Command, + Runtime, + Wasmer, + init, + initializeLogger, +} from "@wasmer/sdk"; type LoadedSdkState = { state: "loaded" } & typeof import("@wasmer/sdk"); -export type WasmerSdkState = LoadedSdkState | { state: "loading" } | { state: "error", error: any }; +export type WasmerSdkState = + | LoadedSdkState + | { state: "loading" } + | { state: "error"; error: any }; export type WasmerSdkProps = { - /** - * The filter passed to {@link initializeLogger}. - */ - log?: string, - wasm?: Parameters[0], - children: React.ReactElement, -} + /** + * The filter passed to {@link initializeLogger}. + */ + log?: string; + wasm?: Parameters[0]; + children: React.ReactElement; +}; const Context = React.createContext(null); @@ -24,89 +33,101 @@ let pending: Promise | undefined = undefined; * A wrapper component which will automatically initialize the Wasmer SDK. */ export function WasmerSdk(props?: WasmerSdkProps) { - const [state, setState] = useState(); - - useEffect(() => { - if (typeof pending == "undefined") { - pending = (async function () { - console.log("Importing @wasmer/sdk"); - const imported = await import("@wasmer/sdk/dist/WasmerSDKBundled"); - console.log("Imported @wasmer/sdk"); - await imported.init(props?.wasm); - imported.initializeLogger(props?.log); - return imported; - })() - } - - pending - .then(sdk => setState({ state: "loaded", ...sdk })) - .catch(e => setState({ state: "error", error: e })); - }, []) - - return ( - - {props?.children} - - ) + const [state, setState] = useState(); + + useEffect(() => { + if (typeof pending == "undefined") { + pending = (async function () { + console.log("Importing @wasmer/sdk"); + const imported = await import("@wasmer/sdk/dist/WasmerSDKBundled"); + console.log("Imported @wasmer/sdk"); + await imported.init(props?.wasm); + imported.initializeLogger(props?.log); + return imported; + })(); + } + + pending + .then(sdk => setState({ state: "loaded", ...sdk })) + .catch(e => setState({ state: "error", error: e })); + }, []); + + return ( + + {props?.children} + + ); } export function useWasmerSdk(): WasmerSdkState { - const ctx = useContext(Context); + const ctx = useContext(Context); - if (ctx == null) { - throw new Error("Attempting to use the Wasmer SDK outside of a component"); - } + if (ctx == null) { + throw new Error( + "Attempting to use the Wasmer SDK outside of a component", + ); + } - return ctx; + return ctx; } type LoadingPackageState = - { state: "loading-package" } - | { - state: "loaded", pkg: Wasmer, - commands: Record, - entrypoint?: Command, + | { state: "loading-package" } + | { + state: "loaded"; + pkg: Wasmer; + commands: Record; + entrypoint?: Command; } - | { state: "error", error: any }; + | { state: "error"; error: any }; export type UseWasmerPackageState = - | { state: "loading-sdk" } - | { state: "sdk-error", error: any } - | LoadingPackageState; - -export function useWasmerPackage(pkg: string | Uint8Array, runtime?: Runtime): UseWasmerPackageState { - const sdk = useWasmerSdk(); - const [state, setState] = useState(); - - // We can't do anything until the SDK has been loaded - switch (sdk.state) { - case "error": - return { state: "sdk-error", error: sdk.error }; - case "loading": - return { state: "loading-sdk" }; - case "loaded": - break; - default: - throw new Error(`Unknown SDK state: ${sdk}`); - } - - if (typeof state != "undefined") { - return state; - } - - const newState = { state: "loading-package" } as const; - setState(newState); - - console.warn("Loading pkg", pkg, state); - - const pending = (typeof pkg == "string") - ? sdk.Wasmer.fromRegistry(pkg, runtime) - : sdk.Wasmer.fromFile(pkg, runtime); - - pending - .then(pkg => { - setState({ state: "loaded", pkg, commands: pkg.commands, entrypoint: pkg.entrypoint }); - }) - .catch(error => setState({ state: "error", error })); - - return newState; + | { state: "loading-sdk" } + | { state: "sdk-error"; error: any } + | LoadingPackageState; + +export function useWasmerPackage( + pkg: string | Uint8Array, + runtime?: Runtime, +): UseWasmerPackageState { + const sdk = useWasmerSdk(); + const [state, setState] = useState(); + + // We can't do anything until the SDK has been loaded + switch (sdk.state) { + case "error": + return { state: "sdk-error", error: sdk.error }; + case "loading": + return { state: "loading-sdk" }; + case "loaded": + break; + default: + throw new Error(`Unknown SDK state: ${sdk}`); + } + + if (typeof state != "undefined") { + return state; + } + + const newState = { state: "loading-package" } as const; + setState(newState); + + console.warn("Loading pkg", pkg, state); + + const pending = + typeof pkg == "string" + ? sdk.Wasmer.fromRegistry(pkg, runtime) + : sdk.Wasmer.fromFile(pkg, runtime); + + pending + .then(pkg => { + setState({ + state: "loaded", + pkg, + commands: pkg.commands, + entrypoint: pkg.entrypoint, + }); + }) + .catch(error => setState({ state: "error", error })); + + return newState; } diff --git a/examples/ffmpeg-react/src/main.tsx b/examples/ffmpeg-react/src/main.tsx index a4a8d1bb..8f431b1c 100644 --- a/examples/ffmpeg-react/src/main.tsx +++ b/examples/ffmpeg-react/src/main.tsx @@ -1,13 +1,13 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.tsx' -import './index.css' -import { WasmerSdk } from './hooks.tsx' +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.tsx"; +import "./index.css"; +import { WasmerSdk } from "./hooks.tsx"; -ReactDOM.createRoot(document.getElementById('root')!).render( +ReactDOM.createRoot(document.getElementById("root")!).render( , -) +); diff --git a/examples/ffmpeg-react/vite.config.ts b/examples/ffmpeg-react/vite.config.ts index 61b63a98..8a5f140e 100644 --- a/examples/ffmpeg-react/vite.config.ts +++ b/examples/ffmpeg-react/vite.config.ts @@ -10,7 +10,7 @@ export default defineConfig({ "Cross-Origin-Embedder-Policy": "require-corp", }, fs: { - allow: ['../..'] - } + allow: ["../.."], + }, }, }); diff --git a/examples/markdown-editor-improved/index.html b/examples/markdown-editor-improved/index.html index fc2da4e9..4d3d5b00 100644 --- a/examples/markdown-editor-improved/index.html +++ b/examples/markdown-editor-improved/index.html @@ -1,19 +1,20 @@ - - - - + + + Wasmer Markdown Editor - - + + - +
- - + +
- - + diff --git a/examples/markdown-editor-improved/index.ts b/examples/markdown-editor-improved/index.ts index 15bbb976..df58a1fc 100644 --- a/examples/markdown-editor-improved/index.ts +++ b/examples/markdown-editor-improved/index.ts @@ -1,43 +1,51 @@ import { init, Wasmer, Command } from "@wasmer/sdk"; async function initialize() { - await init(); - return await Wasmer.fromRegistry("wasmer-examples/markdown-renderer"); + await init(); + return await Wasmer.fromRegistry("wasmer-examples/markdown-renderer"); } -function debounce(func: (...args: any[]) => void, delay: number): (...args: any[]) => void { - let debounceTimer: ReturnType; +function debounce( + func: (...args: any[]) => void, + delay: number, +): (...args: any[]) => void { + let debounceTimer: ReturnType; - return function(...args: any[]) { - clearTimeout(debounceTimer); - debounceTimer = setTimeout(() => func(...args), delay); - }; + return function (...args: any[]) { + clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => func(...args), delay); + }; } async function renderMarkdown(cmd: Command, markdown: string) { - const instance = await cmd.run(); - const stdin = instance.stdin.getWriter(); - const encoder = new TextEncoder(); - await stdin.write(encoder.encode(markdown)); - await stdin.close(); - - const result = await instance.wait(); - return result.ok ? result.stdout : null; + const instance = await cmd.run(); + const stdin = instance.stdin.getWriter(); + const encoder = new TextEncoder(); + await stdin.write(encoder.encode(markdown)); + await stdin.close(); + + const result = await instance.wait(); + return result.ok ? result.stdout : null; } async function main() { - const pkg = await initialize(); - const output = document.getElementById("html-output") as HTMLIFrameElement; - const markdownInput = document.getElementById("markdown-input") as HTMLTextAreaElement; - - const debouncedRender = debounce(async () => { - const renderedHtml = await renderMarkdown(pkg.entrypoint!, markdownInput.value); - if (renderedHtml) { - output.srcdoc = renderedHtml; - } - }, 500); // 500 milliseconds debounce period - - markdownInput.addEventListener("input", debouncedRender); + const pkg = await initialize(); + const output = document.getElementById("html-output") as HTMLIFrameElement; + const markdownInput = document.getElementById( + "markdown-input", + ) as HTMLTextAreaElement; + + const debouncedRender = debounce(async () => { + const renderedHtml = await renderMarkdown( + pkg.entrypoint!, + markdownInput.value, + ); + if (renderedHtml) { + output.srcdoc = renderedHtml; + } + }, 500); // 500 milliseconds debounce period + + markdownInput.addEventListener("input", debouncedRender); } main(); diff --git a/examples/markdown-editor-improved/style.css b/examples/markdown-editor-improved/style.css index 0408dded..4732b68b 100644 --- a/examples/markdown-editor-improved/style.css +++ b/examples/markdown-editor-improved/style.css @@ -1,40 +1,41 @@ body { - font-family: Arial, sans-serif; - margin: 0; - padding: 0; - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - background-color: #f4f4f4; + font-family: Arial, sans-serif; + margin: 0; + padding: 0; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #f4f4f4; } .editor-container { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 20px; - width: 80%; - max-width: 1200px; - margin: auto; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - padding: 20px; - background: white; - border-radius: 8px; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 20px; + width: 80%; + max-width: 1200px; + margin: auto; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + padding: 20px; + background: white; + border-radius: 8px; } -#markdown-input, #html-output { - border: 1px solid #ddd; - padding: 10px; - height: 400px; - overflow: auto; +#markdown-input, +#html-output { + border: 1px solid #ddd; + padding: 10px; + height: 400px; + overflow: auto; } #markdown-input { - resize: none; + resize: none; } #html-output { - background-color: #fff; - width: 100%; - border: none; + background-color: #fff; + width: 100%; + border: none; } diff --git a/examples/markdown-editor-improved/vite.config.js b/examples/markdown-editor-improved/vite.config.js index 732fa10a..b3ba1e7d 100644 --- a/examples/markdown-editor-improved/vite.config.js +++ b/examples/markdown-editor-improved/vite.config.js @@ -1,11 +1,10 @@ import { defineConfig } from "vite"; export default defineConfig({ - server: { - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", - }, + server: { + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", }, + }, }); - diff --git a/examples/markdown-editor/index.html b/examples/markdown-editor/index.html index fc2da4e9..4d3d5b00 100644 --- a/examples/markdown-editor/index.html +++ b/examples/markdown-editor/index.html @@ -1,19 +1,20 @@ - - - - + + + Wasmer Markdown Editor - - + + - +
- - + +
- - + diff --git a/examples/markdown-editor/index.ts b/examples/markdown-editor/index.ts index ba8127b8..282a5dd6 100644 --- a/examples/markdown-editor/index.ts +++ b/examples/markdown-editor/index.ts @@ -2,45 +2,49 @@ import { init, runWasix } from "@wasmer/sdk"; import markdownRendererUrl from "./markdown-renderer/target/wasm32-wasi/release/markdown-renderer.wasm?url"; async function initialize() { - await init(); - return WebAssembly.compileStreaming(fetch(markdownRendererUrl)); + await init(); + return WebAssembly.compileStreaming(fetch(markdownRendererUrl)); } -function debounce(func: (...args: any[]) => void, delay: number): (...args: any[]) => void { - let debounceTimer: ReturnType; +function debounce( + func: (...args: any[]) => void, + delay: number, +): (...args: any[]) => void { + let debounceTimer: ReturnType; - return function(...args: any[]) { - clearTimeout(debounceTimer); - debounceTimer = setTimeout(() => func(...args), delay); - }; + return function (...args: any[]) { + clearTimeout(debounceTimer); + debounceTimer = setTimeout(() => func(...args), delay); + }; } - async function renderMarkdown(module: WebAssembly.Module, markdown: string) { - const instance = await runWasix(module, {}); - const stdin = instance.stdin.getWriter(); - const encoder = new TextEncoder(); + const instance = await runWasix(module, {}); + const stdin = instance.stdin.getWriter(); + const encoder = new TextEncoder(); - await stdin.write(encoder.encode(markdown)); - await stdin.close(); + await stdin.write(encoder.encode(markdown)); + await stdin.close(); - const result = await instance.wait(); - return result.ok ? result.stdout : null; + const result = await instance.wait(); + return result.ok ? result.stdout : null; } async function main() { - const module = await initialize(); - const output = document.getElementById("html-output") as HTMLIFrameElement; - const markdownInput = document.getElementById("markdown-input") as HTMLTextAreaElement; - - const debouncedRender = debounce(async () => { - const renderedHtml = await renderMarkdown(module, markdownInput.value); - if (renderedHtml) { - output.srcdoc = renderedHtml; - } - }, 500); // 500 milliseconds debounce period - - markdownInput.addEventListener("input", debouncedRender); + const module = await initialize(); + const output = document.getElementById("html-output") as HTMLIFrameElement; + const markdownInput = document.getElementById( + "markdown-input", + ) as HTMLTextAreaElement; + + const debouncedRender = debounce(async () => { + const renderedHtml = await renderMarkdown(module, markdownInput.value); + if (renderedHtml) { + output.srcdoc = renderedHtml; + } + }, 500); // 500 milliseconds debounce period + + markdownInput.addEventListener("input", debouncedRender); } main(); diff --git a/examples/markdown-editor/style.css b/examples/markdown-editor/style.css index 0408dded..4732b68b 100644 --- a/examples/markdown-editor/style.css +++ b/examples/markdown-editor/style.css @@ -1,40 +1,41 @@ body { - font-family: Arial, sans-serif; - margin: 0; - padding: 0; - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - background-color: #f4f4f4; + font-family: Arial, sans-serif; + margin: 0; + padding: 0; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #f4f4f4; } .editor-container { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 20px; - width: 80%; - max-width: 1200px; - margin: auto; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - padding: 20px; - background: white; - border-radius: 8px; + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 20px; + width: 80%; + max-width: 1200px; + margin: auto; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + padding: 20px; + background: white; + border-radius: 8px; } -#markdown-input, #html-output { - border: 1px solid #ddd; - padding: 10px; - height: 400px; - overflow: auto; +#markdown-input, +#html-output { + border: 1px solid #ddd; + padding: 10px; + height: 400px; + overflow: auto; } #markdown-input { - resize: none; + resize: none; } #html-output { - background-color: #fff; - width: 100%; - border: none; + background-color: #fff; + width: 100%; + border: none; } diff --git a/examples/markdown-editor/vite.config.js b/examples/markdown-editor/vite.config.js index c911c64f..4a183245 100644 --- a/examples/markdown-editor/vite.config.js +++ b/examples/markdown-editor/vite.config.js @@ -2,32 +2,31 @@ import { defineConfig } from "vite"; import { exec } from "node:child_process"; export default defineConfig({ - server: { - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", - }, + server: { + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", }, - plugins: [ - { - name: "cargo-build", - buildStart: () => { - return new Promise((resolve, reject) => { - exec( - "cargo build --target=wasm32-wasi --manifest-path=markdown-renderer/Cargo.toml --release --quiet", - (err, stdout, stderr) => { - if (err) { - console.log("Stdout:", stdout); - console.log("Stderr:", stderr); - reject(err); - } else { - resolve(); - } - } - ); - }); - } - }, - ] + }, + plugins: [ + { + name: "cargo-build", + buildStart: () => { + return new Promise((resolve, reject) => { + exec( + "cargo build --target=wasm32-wasi --manifest-path=markdown-renderer/Cargo.toml --release --quiet", + (err, stdout, stderr) => { + if (err) { + console.log("Stdout:", stdout); + console.log("Stderr:", stderr); + reject(err); + } else { + resolve(); + } + }, + ); + }); + }, + }, + ], }); - diff --git a/examples/simplimage/index.tsx b/examples/simplimage/index.tsx index 6cb92c6e..14ec061f 100644 --- a/examples/simplimage/index.tsx +++ b/examples/simplimage/index.tsx @@ -20,12 +20,12 @@ function ImageEditor(props: { file: File }) { const onSubmit = () => { if ((resizeWidth ?? 0) > 0 && (resizeHeight ?? 0) > 0) { resizeImage(file, resizeWidth ?? 0, resizeHeight ?? 0) - .then((blob) => { + .then(blob => { const url = URL.createObjectURL(blob); setOutputBlob(blob); setOutputBlobUrl(url); }) - .catch((e) => { + .catch(e => { setError(e.message); }); } @@ -64,7 +64,7 @@ function ImageEditor(props: { file: File }) { placeholder="200" aria-label="width" value={resizeWidth ?? ""} - onInput={(e) => { + onInput={e => { try { const value = parseInt(e.currentTarget.value); setResizeWidth(value); @@ -78,7 +78,7 @@ function ImageEditor(props: { file: File }) { placeholder="400" aria-label="height" value={resizeHeight ?? ""} - onInput={(e) => { + onInput={e => { try { const value = parseInt(e.currentTarget.value); setResizeHeight(value); @@ -143,7 +143,7 @@ function App() { type="file" id="img-input" accept="image/jpeg,image/jpg,image/png" - onChange={(e) => { + onChange={e => { const file = e.currentTarget.files?.item(0); if (!file) { return; @@ -199,7 +199,13 @@ async function resizeImage( const stdin = await file.arrayBuffer(); const instance = await runWasix(MODULE, { - args: ["resize", "--width", width.toString(), "--height", height.toString()], + args: [ + "resize", + "--width", + width.toString(), + "--height", + height.toString(), + ], stdin, }); diff --git a/examples/wasmer.sh/index.html b/examples/wasmer.sh/index.html index bd8e63c5..410ac142 100644 --- a/examples/wasmer.sh/index.html +++ b/examples/wasmer.sh/index.html @@ -1,27 +1,28 @@ - - - - + + + Wasmer Shell - + - +
- - + diff --git a/examples/wasmer.sh/index.ts b/examples/wasmer.sh/index.ts index e1fb791f..178ebfda 100644 --- a/examples/wasmer.sh/index.ts +++ b/examples/wasmer.sh/index.ts @@ -14,35 +14,39 @@ const args = params.getAll("arg"); const logFilter = params.get("log") || "warn"; async function main() { - // Note: We dynamically import the Wasmer SDK to make sure the bundler puts - // it in its own chunk. This works around an issue where just importing - // xterm.js runs top-level code which accesses the DOM, and if it's in the - // same chunk as @wasmer/sdk, each Web Worker will try to run this code and - // crash. - // See https://github.com/wasmerio/wasmer-js/issues/373 - const { Wasmer, init, initializeLogger } = await import("@wasmer/sdk"); - - await init(wasmerSDKUrl); - initializeLogger(logFilter); - - const term = new Terminal({ cursorBlink: true, convertEol: true }); - const fit = new FitAddon(); - term.loadAddon(fit); - term.open(document.getElementById("terminal")!); - fit.fit(); - - term.writeln("Starting..."); - const pkg = await Wasmer.fromRegistry(packageName); - term.reset(); - const instance = await pkg.entrypoint!.run({ args, uses }); - connectStreams(instance, term); + // Note: We dynamically import the Wasmer SDK to make sure the bundler puts + // it in its own chunk. This works around an issue where just importing + // xterm.js runs top-level code which accesses the DOM, and if it's in the + // same chunk as @wasmer/sdk, each Web Worker will try to run this code and + // crash. + // See https://github.com/wasmerio/wasmer-js/issues/373 + const { Wasmer, init, initializeLogger } = await import("@wasmer/sdk"); + + await init(wasmerSDKUrl); + initializeLogger(logFilter); + + const term = new Terminal({ cursorBlink: true, convertEol: true }); + const fit = new FitAddon(); + term.loadAddon(fit); + term.open(document.getElementById("terminal")!); + fit.fit(); + + term.writeln("Starting..."); + const pkg = await Wasmer.fromRegistry(packageName); + term.reset(); + const instance = await pkg.entrypoint!.run({ args, uses }); + connectStreams(instance, term); } function connectStreams(instance: Instance, term: Terminal) { - const stdin = instance.stdin?.getWriter(); - term.onData(data => stdin?.write(encoder.encode(data))); - instance.stdout.pipeTo(new WritableStream({ write: chunk => term.write(chunk) })); - instance.stderr.pipeTo(new WritableStream({ write: chunk => term.write(chunk) })); + const stdin = instance.stdin?.getWriter(); + term.onData(data => stdin?.write(encoder.encode(data))); + instance.stdout.pipeTo( + new WritableStream({ write: chunk => term.write(chunk) }), + ); + instance.stderr.pipeTo( + new WritableStream({ write: chunk => term.write(chunk) }), + ); } main(); diff --git a/examples/wasmer.sh/vite.config.js b/examples/wasmer.sh/vite.config.js index 0e34ac70..55b150d7 100644 --- a/examples/wasmer.sh/vite.config.js +++ b/examples/wasmer.sh/vite.config.js @@ -1,15 +1,15 @@ import { defineConfig } from "vite"; export default defineConfig({ - server: { - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", - }, + server: { + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", }, - build: { - modulePreload: { - polyfill: false, - }, + }, + build: { + modulePreload: { + polyfill: false, }, + }, }); diff --git a/rollup.config.mjs b/rollup.config.mjs index ff41ac94..daef51e0 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -1,10 +1,10 @@ import terser from "@rollup/plugin-terser"; -import pkg from "./package.json" assert { type: "json" }; +import pkg from "./package.json" with { type: "json" }; import dts from "rollup-plugin-dts"; import typescript from "@rollup/plugin-typescript"; import replace from "@rollup/plugin-replace"; -import copy from 'rollup-plugin-copy'; -import { wasm } from '@rollup/plugin-wasm'; +import copy from "rollup-plugin-copy"; +import { wasm } from "@rollup/plugin-wasm"; const LIBRARY_NAME = "WasmerSDK"; // Change with your library's name const EXTERNAL = []; // Indicate which modules should be treated as external @@ -22,79 +22,85 @@ const banner = `/*! */`; const makeConfig = (env = "development", input, name, plugins = []) => { - const config = { - input, - external: EXTERNAL, - output: [ - { - banner, - name: name, - file: `dist/${name}.umd.js`, - format: "umd", - exports: "auto", - globals: GLOBALS, - }, - { - banner, - file: `dist/${name}.cjs`, - format: "cjs", - exports: "auto", - globals: GLOBALS, - }, - { - banner, - file: `dist/${name}.js`, - format: "es", - exports: "named", - globals: GLOBALS, - }, - ], - plugins: [ - typescript(), - ...plugins, - copy({ - targets: [ - { src: ['pkg/wasmer_js_bg.wasm', 'pkg/wasmer_js_bg.wasm.d.ts'], dest: 'dist' }, - ] - }) - ], - }; + const config = { + input, + external: EXTERNAL, + output: [ + { + banner, + name: name, + file: `dist/${name}.umd.js`, + format: "umd", + exports: "auto", + globals: GLOBALS, + }, + { + banner, + file: `dist/${name}.cjs`, + format: "cjs", + exports: "auto", + globals: GLOBALS, + }, + { + banner, + file: `dist/${name}.js`, + format: "es", + exports: "named", + globals: GLOBALS, + }, + ], + plugins: [ + typescript(), + ...plugins, + copy({ + targets: [ + { + src: ["pkg/wasmer_js_bg.wasm", "pkg/wasmer_js_bg.wasm.d.ts"], + dest: "dist", + }, + ], + }), + ], + }; - if (env === "production") { - config.plugins.push( - terser({ - output: { - comments: /^!/, - }, - }), - ); - } - config.plugins.push( - replace({ - values: { - "globalThis.wasmUrl": `"https://unpkg.com/${pkg.name}@${pkg.version}/dist/wasmer_js_bg.wasm"`, - "globalThis.workerUrl": `"https://unpkg.com/${pkg.name}@${pkg.version}/dist/WasmerSDK.js"`, - }, - preventAssignment: true, - }), - ); + if (env === "production") { + config.plugins.push( + terser({ + output: { + comments: /^!/, + }, + }), + ); + } + config.plugins.push( + replace({ + values: { + "globalThis.wasmUrl": `"https://unpkg.com/${pkg.name}@${pkg.version}/dist/wasmer_js_bg.wasm"`, + "globalThis.workerUrl": `"https://unpkg.com/${pkg.name}@${pkg.version}/dist/WasmerSDK.js"`, + }, + preventAssignment: true, + }), + ); - return config; + return config; }; export default commandLineArgs => { - let env = commandLineArgs.environment === "BUILD:production" ? "production" : null; - const configs = [ - makeConfig(env, "WasmerSDK.ts", LIBRARY_NAME), - makeConfig(env, "WasmerSDKBundled.ts", `${LIBRARY_NAME}Bundled`, [wasm({ - maxFileSize: 100 * 1024 * 1024, - })]), - { - input: "./pkg/wasmer_js.d.ts", - output: [{ file: "dist/pkg/wasmer_js.d.ts", format: "es" }], - plugins: [dts()], - }, - ]; + let env = + commandLineArgs.environment === "BUILD:production" ? "production" : null; + const configs = [ + makeConfig(env, "WasmerSDK.ts", LIBRARY_NAME), + makeConfig(env, "WasmerSDKBundled.ts", `${LIBRARY_NAME}Bundled`, [ + wasm({ + maxFileSize: 100 * 1024 * 1024, + }), + ]), + { + input: "./pkg/wasmer_js.d.ts", + output: [{ file: "dist/pkg/wasmer_js.d.ts", format: "es" }], + plugins: [dts()], + }, + ]; - return configs; + return configs; }; diff --git a/web-dev-server.config.mjs b/web-dev-server.config.mjs index be740187..bd7f08c8 100644 --- a/web-dev-server.config.mjs +++ b/web-dev-server.config.mjs @@ -1,40 +1,39 @@ import { chromeLauncher } from "@web/test-runner"; import { esbuildPlugin } from "@web/dev-server-esbuild"; -import { fromRollup } from '@web/dev-server-rollup'; -import rollupReplace from '@rollup/plugin-replace'; -import dotenv from 'dotenv'; +import { fromRollup } from "@web/dev-server-rollup"; +import rollupReplace from "@rollup/plugin-replace"; +import dotenv from "dotenv"; dotenv.config(); - async function add_headers(ctx, next) { - ctx.set("Cross-Origin-Opener-Policy", "same-origin"); - ctx.set("Cross-Origin-Embedder-Policy", "require-corp"); - await next(ctx); + ctx.set("Cross-Origin-Opener-Policy", "same-origin"); + ctx.set("Cross-Origin-Embedder-Policy", "require-corp"); + await next(ctx); } - const envPlugin = fromRollup(rollupReplace)({ - preventAssignment: true, - values: { - 'process.env.WASMER_TOKEN': JSON.stringify(process.env.WASMER_TOKEN), - 'process.env.WASMER_TEST_OWNER': JSON.stringify(process.env.WASMER_TEST_OWNER), - }, + preventAssignment: true, + values: { + "process.env.WASMER_TOKEN": JSON.stringify(process.env.WASMER_TOKEN), + "process.env.WASMER_TEST_OWNER": JSON.stringify( + process.env.WASMER_TEST_OWNER, + ), + }, }); export default { - files: ["tests/**/*.test.ts"], - plugins: [esbuildPlugin({ ts: true }), envPlugin], - middlewares: [add_headers], - browsers: [chromeLauncher({ launchOptions: { devtools: true } })], - testsFinishTimeout: 10 * 60 * 1000, - environmentVariables: { - API_URL: process.env.HELLO, - }, - groups: [{ - "name": "reg", - "files": ["tests/registry.test.ts"] - }] + files: ["tests/**/*.test.ts"], + plugins: [esbuildPlugin({ ts: true }), envPlugin], + middlewares: [add_headers], + browsers: [chromeLauncher({ launchOptions: { devtools: true } })], + testsFinishTimeout: 10 * 60 * 1000, + environmentVariables: { + API_URL: process.env.HELLO, + }, + groups: [ + { + name: "reg", + files: ["tests/registry.test.ts"], + }, + ], }; - - - From c64882938ee0c2e3cc03ea424fd22541f759521d Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 5 Aug 2024 19:00:47 +0200 Subject: [PATCH 23/32] chore: fix tests --- examples/ffmpeg-react/src/hooks.tsx | 2 +- examples/simplimage/index.tsx | 2 +- examples/wasmer.sh/index.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/ffmpeg-react/src/hooks.tsx b/examples/ffmpeg-react/src/hooks.tsx index 6031a605..bcfe3a67 100644 --- a/examples/ffmpeg-react/src/hooks.tsx +++ b/examples/ffmpeg-react/src/hooks.tsx @@ -41,7 +41,7 @@ export function WasmerSdk(props?: WasmerSdkProps) { console.log("Importing @wasmer/sdk"); const imported = await import("@wasmer/sdk/dist/WasmerSDKBundled"); console.log("Imported @wasmer/sdk"); - await imported.init(props?.wasm); + await imported.init({module: props?.wasm}); imported.initializeLogger(props?.log); return imported; })(); diff --git a/examples/simplimage/index.tsx b/examples/simplimage/index.tsx index 14ec061f..db802a03 100644 --- a/examples/simplimage/index.tsx +++ b/examples/simplimage/index.tsx @@ -186,7 +186,7 @@ async function resizeImage( height: number, ): Promise { const { init, runWasix, initializeLogger } = await import("@wasmer/sdk"); - await init(wasmSdkUrl); + await init({module: wasmSdkUrl}); if (!MODULE) { await initialize(); diff --git a/examples/wasmer.sh/index.ts b/examples/wasmer.sh/index.ts index 178ebfda..c0b13eb2 100644 --- a/examples/wasmer.sh/index.ts +++ b/examples/wasmer.sh/index.ts @@ -20,9 +20,9 @@ async function main() { // same chunk as @wasmer/sdk, each Web Worker will try to run this code and // crash. // See https://github.com/wasmerio/wasmer-js/issues/373 - const { Wasmer, init, initializeLogger } = await import("@wasmer/sdk"); + const { Wasmer, init, initializeLogger } = await import("@wasmer/sdk"}); - await init(wasmerSDKUrl); + await init({module: wasmerSDKUrl}); initializeLogger(logFilter); const term = new Terminal({ cursorBlink: true, convertEol: true }); From 3a9019c6f519d99d210b633baabe519bb9e5fb69 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 5 Aug 2024 19:06:22 +0200 Subject: [PATCH 24/32] fix: remove typo --- examples/wasmer.sh/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/wasmer.sh/index.ts b/examples/wasmer.sh/index.ts index c0b13eb2..b23e8fad 100644 --- a/examples/wasmer.sh/index.ts +++ b/examples/wasmer.sh/index.ts @@ -20,7 +20,7 @@ async function main() { // same chunk as @wasmer/sdk, each Web Worker will try to run this code and // crash. // See https://github.com/wasmerio/wasmer-js/issues/373 - const { Wasmer, init, initializeLogger } = await import("@wasmer/sdk"}); + const { Wasmer, init, initializeLogger } = await import("@wasmer/sdk"); await init({module: wasmerSDKUrl}); initializeLogger(logFilter); From 591bdcbf6b9c77bab957c514cd7517797e0d1d15 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 5 Aug 2024 19:06:49 +0200 Subject: [PATCH 25/32] fix: with -> assert --- rollup.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup.config.mjs b/rollup.config.mjs index daef51e0..0a5f62fd 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -1,5 +1,5 @@ import terser from "@rollup/plugin-terser"; -import pkg from "./package.json" with { type: "json" }; +import pkg from "./package.json" assert { type: "json" }; import dts from "rollup-plugin-dts"; import typescript from "@rollup/plugin-typescript"; import replace from "@rollup/plugin-replace"; From f691359c1465ac13b6fb00284354de2291fa44b5 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 5 Aug 2024 19:33:33 +0200 Subject: [PATCH 26/32] chore: fix ci tests --- src/tasks/worker.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tasks/worker.js b/src/tasks/worker.js index 82f1757d..6f40a2a0 100644 --- a/src/tasks/worker.js +++ b/src/tasks/worker.js @@ -33,12 +33,14 @@ globalThis.onmessage = async ev => { init = imported.init; } ThreadPoolWorker = imported.ThreadPoolWorker; + + await init(module, memory); } else { init = globalThis["__WASMER_INTERNALS__"].init; ThreadPoolWorker = globalThis["__WASMER_INTERNALS__"].ThreadPoolWorker; + await init({ module: module, memory: memory }); } - await init({ module: module, memory: memory }); worker = new ThreadPoolWorker(id); From f0442e9ead1ca2194d5ec9e0cbef2571b7914224 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 5 Aug 2024 19:42:56 +0200 Subject: [PATCH 27/32] chore: fix ci docs --- WasmerSDK.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/WasmerSDK.ts b/WasmerSDK.ts index 605e2259..4d395415 100644 --- a/WasmerSDK.ts +++ b/WasmerSDK.ts @@ -3,6 +3,7 @@ export * from "./pkg/wasmer_js"; import load, { InitInput, InitOutput, + // @ts-ignore ThreadPoolWorker, setWorkerUrl, } from "./pkg/wasmer_js"; @@ -17,7 +18,12 @@ export type WasmerInitInput = { /** * Initialize the underlying WebAssembly module. */ -export const init = async (initValue: WasmerInitInput): Promise => { +export const init = async (initValue: WasmerInitInput | undefined): Promise => { + + if (!initValue) { + initValue = {}; + } + if (!initValue.module) { // This will be replaced by the rollup bundler at the SDK build time // to point to a valid http location of the SDK using unpkg.com. From 29447c9edf4ef60a402cdc19bbf980732c936a4f Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 5 Aug 2024 19:51:58 +0200 Subject: [PATCH 28/32] chore: add `.env` to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3e15d65d..7ad9b119 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/ dist/ *.log pkg/ +.env From 37d1326a2b874956698b48f4fc0099d31e3cfd14 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Mon, 5 Aug 2024 20:05:37 +0200 Subject: [PATCH 29/32] chore: release 0.7.0-alpha Release-As: 0.7.0-alpha From e94ce1db6ff5d2545f41d2b2536ddd0f6c8c600e Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Tue, 6 Aug 2024 08:48:36 +0200 Subject: [PATCH 30/32] chore: release 0.7.0-alpha.1 Release-As: 0.7.0-alpha.1 From ad91eb507cb4b243ce1d3eddbad22e511c5171f1 Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Tue, 6 Aug 2024 10:01:53 +0200 Subject: [PATCH 31/32] chore: Update rollup and change type for entry function --- WasmerSDKBundled.ts | 6 +++++- package.json | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/WasmerSDKBundled.ts b/WasmerSDKBundled.ts index 150a4ec3..2745021e 100644 --- a/WasmerSDKBundled.ts +++ b/WasmerSDKBundled.ts @@ -12,7 +12,11 @@ import wasm_bytes from "./pkg/wasmer_js_bg.wasm"; * Initialize the underlying WebAssembly module, defaulting to an embedded * copy of the `*.wasm` file. */ -export const init = async (initValue: WasmerInitInput): Promise => { +export const init = async (initValue: WasmerInitInput | undefined): Promise => { + if (!initValue) { + initValue = {} + } + if (!initValue.module) { // @ts-ignore initValue.module = await wasm_bytes(); diff --git a/package.json b/package.json index e8b8734d..8d3cfb0a 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@web/test-runner": "^0.18.0", "prettier": "^3.1.0", "rimraf": "^5.0.5", - "rollup": "^4.8.0", + "rollup": "^4.9.1", "rollup-plugin-copy": "^3.5.0", "rollup-plugin-dts": "^6.1.0", "typedoc": "^0.25.4", From 0464d616fc5a9c80a14f3ef609ea00d03f60f04b Mon Sep 17 00:00:00 2001 From: Edoardo Marangoni Date: Tue, 6 Aug 2024 10:36:10 +0200 Subject: [PATCH 32/32] fix: Use a global variable to signal which init function to use --- WasmerSDK.ts | 1 + src/tasks/worker.js | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/WasmerSDK.ts b/WasmerSDK.ts index 4d395415..74b25fe2 100644 --- a/WasmerSDK.ts +++ b/WasmerSDK.ts @@ -56,6 +56,7 @@ export const setDefaultWorkerUrl = () => { // make sure worker.js gets access to them. Normal exports are removed when // using a bundler. (globalThis as any)["__WASMER_INTERNALS__"] = { ThreadPoolWorker, init }; +(globalThis as any)["__WASMER_INIT__"] = true; // HACK: some bundlers such as webpack uses this on dev mode. // We add this functions to allow dev mode work in those bundlers. diff --git a/src/tasks/worker.js b/src/tasks/worker.js index 6f40a2a0..7f1b7635 100644 --- a/src/tasks/worker.js +++ b/src/tasks/worker.js @@ -34,13 +34,17 @@ globalThis.onmessage = async ev => { } ThreadPoolWorker = imported.ThreadPoolWorker; - await init(module, memory); + //await init(module, memory); } else { init = globalThis["__WASMER_INTERNALS__"].init; ThreadPoolWorker = globalThis["__WASMER_INTERNALS__"].ThreadPoolWorker; - await init({ module: module, memory: memory }); } + if (globalThis["__WASMER_INIT__"]) { + await init({ module: module, memory: memory }); + } else { + await init(module, memory); + } worker = new ThreadPoolWorker(id);