From 943f8f75be76121131d133d9f033f6da01b1dfb0 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 4 Jun 2023 12:50:17 -0700 Subject: [PATCH 1/3] deps: latest toolchain --- Cargo.lock | 436 +++------------- Cargo.toml | 38 +- bin/main.rs | 3 - bin/self_build.rs | 60 +++ .../js-component-bindgen-component/Cargo.toml | 8 +- .../js-component-bindgen-component/src/lib.rs | 19 +- .../wit/js-component-bindgen.wit | 108 ++-- crates/js-component-bindgen/Cargo.toml | 2 - crates/js-component-bindgen/src/identifier.rs | 69 ++- crates/js-component-bindgen/src/lib.rs | 35 +- .../src/transpile_bindgen.rs | 387 +++++++++----- crates/js-component-bindgen/src/ts_bindgen.rs | 156 ++++-- crates/wasm-tools-component/src/lib.rs | 30 +- .../wasm-tools-component/wit/wasm-tools.wit | 112 ++--- ...si_preview1_component_adapter.command.wasm | Bin 88115 -> 0 bytes ...si_preview1_component_adapter.reactor.wasm | Bin 87996 -> 0 bytes lib/wasi_snapshot_preview1.command.wasm | Bin 0 -> 106094 bytes lib/wasi_snapshot_preview1.reactor.wasm | Bin 0 -> 105973 bytes package.json | 14 +- .../lib/nodejs/{preopens.js => cli-base.js} | 48 +- .../preview2-shim/lib/nodejs/environment.js | 19 - packages/preview2-shim/lib/nodejs/exit.js | 3 - .../preview2-shim/lib/nodejs/filesystem.js | 473 +++++++++--------- packages/preview2-shim/lib/nodejs/io.js | 157 +++++- packages/preview2-shim/lib/nodejs/poll.js | 7 - packages/preview2-shim/lib/nodejs/random.js | 31 +- packages/preview2-shim/lib/nodejs/stderr.js | 4 - packages/preview2-shim/lib/nodejs/streams.js | 97 ---- src/api.js | 7 +- src/cmd/componentize.js | 2 +- src/cmd/opt.js | 4 +- src/cmd/transpile.js | 40 +- src/cmd/wasm-tools.js | 12 +- test/api.js | 24 +- test/cli.js | 20 +- test/codegen.js | 2 +- test/fixtures/componentize/source.wit | 4 +- .../components/dummy_proxy.component.wasm | Bin 11502 -> 0 bytes .../components/dummy_reactor.component.wasm | Bin 32329 -> 0 bytes .../components/flavorful.component.wasm | Bin 77217 -> 79500 bytes test/fixtures/components/lists.component.wasm | Bin 96727 -> 98832 bytes .../components/many-arguments.component.wasm | Bin 37408 -> 37404 bytes .../components/many_arguments.component.wasm | Bin 37979 -> 0 bytes .../components/numbers.component.wasm | Bin 52906 -> 54365 bytes .../components/records.component.wasm | Bin 52324 -> 53521 bytes test/fixtures/components/smoke.component.wasm | Bin 33176 -> 33238 bytes .../components/strings.component.wasm | Bin 39348 -> 39500 bytes .../components/variants.component.wasm | Bin 76489 -> 78013 bytes test/fixtures/wit/flavorful.wit | 43 ++ test/runtime/dummy_proxy.ts | 2 + test/runtime/flavorful.ts | 102 ++-- test/runtime/lists.ts | 215 ++++---- .../{many_arguments.ts => many-arguments.ts} | 2 +- test/runtime/numbers.ts | 111 ++-- test/runtime/records.ts | 51 +- test/runtime/smoke.ts | 10 +- test/runtime/strings.ts | 14 +- test/runtime/variants.ts | 83 +-- update-preview2.sh | 5 +- update-tests.sh | 9 +- wit-bindgen | 2 +- 61 files changed, 1565 insertions(+), 1515 deletions(-) delete mode 100644 bin/main.rs create mode 100644 bin/self_build.rs delete mode 100644 lib/wasi_preview1_component_adapter.command.wasm delete mode 100644 lib/wasi_preview1_component_adapter.reactor.wasm create mode 100644 lib/wasi_snapshot_preview1.command.wasm create mode 100644 lib/wasi_snapshot_preview1.reactor.wasm rename packages/preview2-shim/lib/nodejs/{preopens.js => cli-base.js} (66%) delete mode 100644 packages/preview2-shim/lib/nodejs/environment.js delete mode 100644 packages/preview2-shim/lib/nodejs/exit.js delete mode 100644 packages/preview2-shim/lib/nodejs/poll.js delete mode 100644 packages/preview2-shim/lib/nodejs/stderr.js delete mode 100644 packages/preview2-shim/lib/nodejs/streams.js delete mode 100644 test/fixtures/components/dummy_proxy.component.wasm delete mode 100644 test/fixtures/components/dummy_reactor.component.wasm delete mode 100644 test/fixtures/components/many_arguments.component.wasm create mode 100644 test/fixtures/wit/flavorful.wit rename test/runtime/{many_arguments.ts => many-arguments.ts} (95%) diff --git a/Cargo.lock b/Cargo.lock index b69b83bc6..f5724c4bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,60 +13,11 @@ dependencies = [ "version_check", ] -[[package]] -name = "anstream" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is-terminal", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" - -[[package]] -name = "anstyle-parse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" -dependencies = [ - "anstyle", - "windows-sys", -] - [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "autocfg" @@ -76,9 +27,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bitflags" @@ -88,15 +39,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813" - -[[package]] -name = "cc" -version = "1.0.79" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "6776fc96284a0bb647b615056fc496d1fe1644a7ab01829818a6d91cae888b84" [[package]] name = "cfg-if" @@ -104,59 +49,10 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clap" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a" -dependencies = [ - "clap_builder", - "clap_derive", - "once_cell", -] - -[[package]] -name = "clap_builder" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6" -dependencies = [ - "anstream", - "anstyle", - "bitflags 1.3.2", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "cranelift-entity" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" +version = "0.97.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=f7ae056a0a757492c0842ca3f2852f9a5f4bd19c#f7ae056a0a757492c0842ca3f2852f9a5f4bd19c" dependencies = [ "serde", ] @@ -170,27 +66,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "fallible-iterator" version = "0.2.0" @@ -241,12 +116,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - [[package]] name = "id-arena" version = "2.2.1" @@ -274,33 +143,11 @@ dependencies = [ "serde", ] -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys", -] - -[[package]] -name = "is-terminal" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi", - "io-lifetimes", - "rustix", - "windows-sys", -] - [[package]] name = "jco" version = "0.1.0" dependencies = [ + "anyhow", "js-component-bindgen", "wit-component", ] @@ -311,11 +158,9 @@ version = "0.1.0" dependencies = [ "anyhow", "base64", - "clap", "heck", "indexmap", "wasmtime-environ", - "wit-bindgen", "wit-component", "wit-parser", ] @@ -325,15 +170,9 @@ name = "js-component-bindgen-component" version = "0.1.0" dependencies = [ "anyhow", - "base64", - "clap", - "heck", - "indexmap", "js-component-bindgen", "wasmtime-environ", "wit-bindgen", - "wit-component", - "wit-parser", ] [[package]] @@ -342,26 +181,11 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" -[[package]] -name = "libc" -version = "0.2.141" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" - -[[package]] -name = "linux-raw-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" - [[package]] name = "log" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "memchr" @@ -383,9 +207,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "percent-encoding" @@ -395,9 +219,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] @@ -415,41 +239,33 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] [[package]] -name = "rustix" -version = "0.37.11" +name = "semver" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys", -] +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", @@ -462,17 +278,11 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "syn" -version = "2.0.15" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -481,9 +291,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.6" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "thiserror" @@ -537,9 +347,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-normalization" @@ -579,12 +389,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "version_check" version = "0.9.4" @@ -593,33 +397,24 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasm-encoder" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasm-encoder" -version = "0.27.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77053dc709db790691d3732cfc458adc5acc881dec524965c608effdcd9c581" +checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27e6532071b112df81d756fabafab8cc882b84ae3d6ce9b90f3d90c80e4abe05" +checksum = "36e5156581ff4a302405c44ca7c85347563ca431d15f1a773f12c9c7b9a6cdc9" dependencies = [ "anyhow", "indexmap", "serde", - "wasm-encoder 0.27.0", - "wasmparser 0.105.0", + "wasm-encoder", + "wasmparser", ] [[package]] @@ -627,9 +422,9 @@ name = "wasm-tools-js" version = "0.1.0" dependencies = [ "anyhow", - "wasm-encoder 0.27.0", + "wasm-encoder", "wasm-metadata", - "wasmparser 0.105.0", + "wasmparser", "wasmprinter", "wat", "wit-bindgen", @@ -639,45 +434,33 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.102.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" -dependencies = [ - "indexmap", - "url", -] - -[[package]] -name = "wasmparser" -version = "0.105.0" +version = "0.107.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83be9e0b3f9570dc1979a33ae7b89d032c73211564232b99976553e5c155ec32" +checksum = "29e3ac9b780c7dda0cac7a52a5d6d2d6707cc6e3451c9db209b6c758f40d7acb" dependencies = [ "indexmap", - "url", + "semver", ] [[package]] name = "wasmprinter" -version = "0.2.57" +version = "0.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b0e5ed7a74a065637f0d7798ce5f29cadb064980d24b0c82af5200122fa0d8" +checksum = "cc960b30b84abca377768f3c62cff3a1c74db8c0f6759ed581827da0bd3a3fed" dependencies = [ "anyhow", - "wasmparser 0.105.0", + "wasmparser", ] [[package]] name = "wasmtime-component-util" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e02ca7a4a3c69d72b88f26f0192e333958df6892415ac9ab84dcc42c9000c2" +version = "10.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=f7ae056a0a757492c0842ca3f2852f9a5f4bd19c#f7ae056a0a757492c0842ca3f2852f9a5f4bd19c" [[package]] name = "wasmtime-environ" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" +version = "10.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=f7ae056a0a757492c0842ca3f2852f9a5f4bd19c#f7ae056a0a757492c0842ca3f2852f9a5f4bd19c" dependencies = [ "anyhow", "cranelift-entity", @@ -688,8 +471,8 @@ dependencies = [ "serde", "target-lexicon", "thiserror", - "wasm-encoder 0.25.0", - "wasmparser 0.102.0", + "wasm-encoder", + "wasmparser", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -697,116 +480,49 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" +version = "10.0.0" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=f7ae056a0a757492c0842ca3f2852f9a5f4bd19c#f7ae056a0a757492c0842ca3f2852f9a5f4bd19c" dependencies = [ "cranelift-entity", "serde", "thiserror", - "wasmparser 0.102.0", + "wasmparser", ] [[package]] name = "wast" -version = "58.0.0" +version = "60.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372eecae2d10a5091c2005b32377d7ecd6feecdf2c05838056d02d8b4f07c429" +checksum = "bd06cc744b536e30387e72a48fdd492105b9c938bb4f415c39c616a7a0a697ad" dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.27.0", + "wasm-encoder", ] [[package]] name = "wat" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d47446190e112ab1579ab40b3ad7e319d859d74e5134683f04e9f0747bf4173" +checksum = "5abe520f0ab205366e9ac7d3e6b2fc71de44e32a2b58f2ec871b6b575bdcea3b" dependencies = [ "wast", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - [[package]] name = "wit-bindgen" -version = "0.6.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=c1eb6ba2ffe303574219a3787aa1187d70d877ec#c1eb6ba2ffe303574219a3787aa1187d70d877ec" +version = "0.7.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=e69cf5db8754f829637e25491c560ec0d9728852#e69cf5db8754f829637e25491c560ec0d9728852" dependencies = [ - "bitflags 2.2.1", + "bitflags 2.3.1", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.6.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=c1eb6ba2ffe303574219a3787aa1187d70d877ec#c1eb6ba2ffe303574219a3787aa1187d70d877ec" +version = "0.7.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=e69cf5db8754f829637e25491c560ec0d9728852#e69cf5db8754f829637e25491c560ec0d9728852" dependencies = [ "anyhow", "wit-component", @@ -815,8 +531,8 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.6.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=c1eb6ba2ffe303574219a3787aa1187d70d877ec#c1eb6ba2ffe303574219a3787aa1187d70d877ec" +version = "0.7.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=e69cf5db8754f829637e25491c560ec0d9728852#e69cf5db8754f829637e25491c560ec0d9728852" dependencies = [ "heck", "wasm-metadata", @@ -827,8 +543,8 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-lib" -version = "0.6.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=c1eb6ba2ffe303574219a3787aa1187d70d877ec#c1eb6ba2ffe303574219a3787aa1187d70d877ec" +version = "0.7.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=e69cf5db8754f829637e25491c560ec0d9728852#e69cf5db8754f829637e25491c560ec0d9728852" dependencies = [ "heck", "wit-bindgen-core", @@ -836,8 +552,8 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.6.0" -source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=c1eb6ba2ffe303574219a3787aa1187d70d877ec#c1eb6ba2ffe303574219a3787aa1187d70d877ec" +version = "0.7.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?rev=e69cf5db8754f829637e25491c560ec0d9728852#e69cf5db8754f829637e25491c560ec0d9728852" dependencies = [ "anyhow", "proc-macro2", @@ -849,33 +565,33 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.9.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3cc435c15009f80d5e114d0dd3792959171f1fc28be2418e0f570d83b925a33" +checksum = "7cbd4c7f8f400327c482c88571f373844b7889e61460650d650fc5881bb3575c" dependencies = [ "anyhow", "bitflags 1.3.2", "indexmap", "log", - "url", - "wasm-encoder 0.27.0", + "wasm-encoder", "wasm-metadata", - "wasmparser 0.105.0", + "wasmparser", "wat", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca2581061573ef6d1754983d7a9b3ed5871ef859d52708ea9a0f5af32919172" +checksum = "6daec9f093dbaea0e94043eeb92ece327bbbe70c86b1f41aca9bbfefd7f050f0" dependencies = [ "anyhow", "id-arena", "indexmap", "log", "pulldown-cmark", + "semver", "unicode-xid", "url", ] diff --git a/Cargo.toml b/Cargo.toml index 9d8b86e22..8a423493c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,13 +5,17 @@ edition.workspace = true publish = false [[bin]] -name = "jco" -path = "bin/main.rs" +name = "self-build" +path = "bin/self_build.rs" -[build-dependencies] +[dependencies] +anyhow = { workspace = true } js-component-bindgen = { path = "./crates/js-component-bindgen" } wit-component = { workspace = true } +[build-dependencies] +js-component-bindgen = { path = "./crates/js-component-bindgen" } +wit-component = { workspace = true } [workspace] members = ["crates/js-component-bindgen", "crates/js-component-bindgen-component", "crates/wasm-tools-component"] @@ -22,21 +26,17 @@ edition = "2021" version = "0.1.0" [workspace.dependencies] -anyhow = "1.0.69" -base64 = "0.21.0" -bitflags = "1.3.2" -clap = { version = "4.1.8", features = ["derive"] } -env_logger = "0.10.0" +anyhow = "1.0.71" +base64 = "0.21.2" heck = { version = "0.4", features = ["unicode"] } indexmap = "1.9" -pulldown-cmark = { version = "0.8", default-features = false } -wasm-encoder = "0.27.0" -wasm-metadata = "0.6.0" -wasmparser = "0.105.0" -wasmprinter = "0.2.57" -wasmtime = { version = "8.0.1", features = ["component-model"] } -wasmtime-environ = "8.0.1" -wat = "1.0.64" -wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "c1eb6ba2ffe303574219a3787aa1187d70d877ec" } -wit-component = { version = "0.9.0", features = ['dummy-module'] } -wit-parser = "0.7.1" +wasm-encoder = "0.29.0" +wasm-metadata = "0.8.0" +wasmparser = "0.107.0" +wasmprinter = "0.2.59" +wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", features = ["component-model"], rev = "f7ae056a0a757492c0842ca3f2852f9a5f4bd19c" } +wasmtime-environ = { git = "https://github.com/bytecodealliance/wasmtime", rev = "f7ae056a0a757492c0842ca3f2852f9a5f4bd19c" } +wat = "1.0.66" +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "e69cf5db8754f829637e25491c560ec0d9728852" } +wit-component = { version = "0.11.0", features = ['dummy-module'] } +wit-parser = "0.8.0" diff --git a/bin/main.rs b/bin/main.rs deleted file mode 100644 index db91fc694..000000000 --- a/bin/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - () -} diff --git a/bin/self_build.rs b/bin/self_build.rs new file mode 100644 index 000000000..d6bcb8c6d --- /dev/null +++ b/bin/self_build.rs @@ -0,0 +1,60 @@ +use std::{collections::HashMap, fs, io::Write, path::PathBuf}; + +use js_component_bindgen; +use wit_component::ComponentEncoder; + +use anyhow::Result; + +fn main() -> Result<()> { + fn transpile(component_path: &str, name: String) -> Result<()> { + let component = fs::read(&component_path).expect("wasm bindgen component missing"); + + let adapter_path = "lib/wasi_snapshot_preview1.reactor.wasm"; + let adapter = fs::read(&adapter_path).expect("preview1 adapter file missing"); + + let mut encoder = ComponentEncoder::default() + .validate(true) + .module(&component)?; + + encoder = encoder.adapter("wasi_snapshot_preview1", &adapter)?; + + let adapted_component = encoder.encode()?; + + let import_map = HashMap::from([( + "wasi:*".to_string(), + "@bytecodealliance/preview2-shim/*".to_string(), + )]); + let opts = js_component_bindgen::TranspileOpts { + name, + no_typescript: false, + instantiation: false, + map: Some(import_map), + no_nodejs_compat: false, + base64_cutoff: 5000_usize, + tla_compat: false, + valid_lifting_optimization: false, + }; + + let transpiled = js_component_bindgen::transpile(adapted_component, opts)?; + + for (filename, contents) in transpiled.files.iter() { + let outfile = PathBuf::from("./obj").join(filename); + fs::create_dir_all(outfile.parent().unwrap()).unwrap(); + let mut file = fs::File::create(outfile).unwrap(); + file.write_all(contents).unwrap(); + } + + Ok(()) + } + + transpile( + "target/wasm32-wasi/release/js_component_bindgen_component.wasm", + "js-component-bindgen-component".to_string(), + )?; + transpile( + "target/wasm32-wasi/release/wasm_tools_js.wasm", + "wasm-tools".to_string(), + )?; + + Ok(()) +} diff --git a/crates/js-component-bindgen-component/Cargo.toml b/crates/js-component-bindgen-component/Cargo.toml index a61870549..d91c5cbb3 100644 --- a/crates/js-component-bindgen-component/Cargo.toml +++ b/crates/js-component-bindgen-component/Cargo.toml @@ -11,12 +11,6 @@ crate-type = ["cdylib"] [dependencies] anyhow = { workspace = true } -heck = { workspace = true } -clap = { workspace = true, optional = true } js-component-bindgen = { path = "../js-component-bindgen" } -wasmtime-environ = { workspace = true, features = ['component-model'] } +wasmtime-environ = { workspace = true } wit-bindgen = { workspace = true } -wit-component = { workspace = true } -wit-parser = { workspace = true } -indexmap = { workspace = true } -base64 = { workspace = true } diff --git a/crates/js-component-bindgen-component/src/lib.rs b/crates/js-component-bindgen-component/src/lib.rs index f7c2574d7..b88758d8c 100644 --- a/crates/js-component-bindgen-component/src/lib.rs +++ b/crates/js-component-bindgen-component/src/lib.rs @@ -29,27 +29,12 @@ macro_rules! uwriteln { wit_bindgen::generate!("js-component-bindgen"); -use crate::exports::*; - struct JsComponentBindgenComponent; -export_js_component_bindgen_component!(JsComponentBindgenComponent); - -// fn init() { -// static INIT: Once = Once::new(); -// INIT.call_once(|| { -// let prev_hook = std::panic::take_hook(); -// std::panic::set_hook(Box::new(move |info| { -// console::error(&info.to_string()); -// prev_hook(info); -// })); -// }); -// } +export_js_component_bindgen!(JsComponentBindgenComponent); -impl exports::Exports for JsComponentBindgenComponent { +impl JsComponentBindgen for JsComponentBindgenComponent { fn generate(component: Vec, options: GenerateOptions) -> Result { - // init(); - let opts = js_component_bindgen::TranspileOpts { name: options.name, no_typescript: options.no_typescript.unwrap_or(false), diff --git a/crates/js-component-bindgen-component/wit/js-component-bindgen.wit b/crates/js-component-bindgen-component/wit/js-component-bindgen.wit index 002f25466..d24c15a71 100644 --- a/crates/js-component-bindgen-component/wit/js-component-bindgen.wit +++ b/crates/js-component-bindgen-component/wit/js-component-bindgen.wit @@ -1,56 +1,56 @@ -default world js-component-bindgen-component { - export exports: interface { - type files = list>> - type maps = list> - - record generate-options { - /// Name to use for the generated component - name: string, - - /// Disables generation of `*.d.ts` files and instead only generates `*.js` - /// source files. - no-typescript: option, - - /// Provide a custom JS instantiation API for the component instead - /// of the direct importable native ESM output. - instantiation: option, - - /// Mappings of component import specifiers to JS import specifiers. - map: option, - - /// Enables all compat flags: --tla-compat. - compat: option, - - /// Disables compatibility in Node.js without a fetch global. - no-nodejs-compat: option, - - /// Set the cutoff byte size for base64 inlining core Wasm in instantiation mode - /// (set to 0 to disable all base64 inlining) - base64-cutoff: option, - - /// Enables compatibility for JS environments without top-level await support - /// via an async $init promise export to wait for instead. - tla-compat: option, - - /// Disable verification of component Wasm data structures when - /// lifting as a production optimization - valid-lifting-optimization: option, - } - - enum export-type { - function, - instance, - } - - record transpiled { - files: files, - imports: list, - exports: list> - } - - /// Generate the file structure for the transpilation of a component - /// into a JS embedding, returns the file list and imports and exports of the - /// output JS transpiled component - generate: func(component: list, options: generate-options) -> result +package local:js-component-bindgen + +world js-component-bindgen { + type files = list>> + type maps = list> + + record generate-options { + /// Name to use for the generated component + name: string, + + /// Disables generation of `*.d.ts` files and instead only generates `*.js` + /// source files. + no-typescript: option, + + /// Provide a custom JS instantiation API for the component instead + /// of the direct importable native ESM output. + instantiation: option, + + /// Mappings of component import specifiers to JS import specifiers. + map: option, + + /// Enables all compat flags: --tla-compat. + compat: option, + + /// Disables compatibility in Node.js without a fetch global. + no-nodejs-compat: option, + + /// Set the cutoff byte size for base64 inlining core Wasm in instantiation mode + /// (set to 0 to disable all base64 inlining) + base64-cutoff: option, + + /// Enables compatibility for JS environments without top-level await support + /// via an async $init promise export to wait for instead. + tla-compat: option, + + /// Disable verification of component Wasm data structures when + /// lifting as a production optimization + valid-lifting-optimization: option, + } + + enum export-type { + function, + instance, } + + record transpiled { + files: files, + imports: list, + exports: list> + } + + /// Generate the file structure for the transpilation of a component + /// into a JS embedding, returns the file list and imports and exports of the + /// output JS transpiled component + export generate: func(component: list, options: generate-options) -> result } diff --git a/crates/js-component-bindgen/Cargo.toml b/crates/js-component-bindgen/Cargo.toml index 7f5be7f45..ef24f066a 100644 --- a/crates/js-component-bindgen/Cargo.toml +++ b/crates/js-component-bindgen/Cargo.toml @@ -16,9 +16,7 @@ transpile-bindgen = [] [dependencies] anyhow = { workspace = true } heck = { workspace = true } -clap = { workspace = true, optional = true } wasmtime-environ = { workspace = true, features = ['component-model'] } -wit-bindgen = { workspace = true } wit-component = { workspace = true } wit-parser = { workspace = true } indexmap = { workspace = true } diff --git a/crates/js-component-bindgen/src/identifier.rs b/crates/js-component-bindgen/src/identifier.rs index f607193eb..fa21fe581 100644 --- a/crates/js-component-bindgen/src/identifier.rs +++ b/crates/js-component-bindgen/src/identifier.rs @@ -12,7 +12,7 @@ pub fn is_js_identifier(s: &str) -> bool { return false; } } - return true; + NOT_IDENTIFIERS.binary_search(&s).is_err() } // https://tc39.es/ecma262/#prod-IdentifierStartChar @@ -34,3 +34,70 @@ fn is_js_identifier_char(code: char) -> bool { _ => false, }; } + +pub fn maybe_quote_id(name: &str) -> String { + if is_js_identifier(name) { + name.to_string() + } else { + format!("'{name}'") + } +} + +pub fn maybe_quote_member(name: &str) -> String { + if name == "*" { + "".to_string() + } else if is_js_identifier(name) { + format!(".{name}") + } else { + format!("['{name}']") + } +} + +const NOT_IDENTIFIERS: &'static [&'static str] = &[ + "await", + "break", + "case", + "catch", + "class", + "const", + "continue", + "debugger", + "default", + "delete", + "do", + "else", + "enum", + "export", + "extends", + "false", + "finally", + "for", + "function", + "if", + "implements", + "import", + "in", + "instanceof", + "interface", + "let", + "new", + "null", + "package", + "private", + "protected", + "public", + "return", + "static", + "super", + "switch", + "this", + "throw", + "true", + "try", + "typeof", + "var", + "void", + "while", + "with", + "yield", +]; diff --git a/crates/js-component-bindgen/src/lib.rs b/crates/js-component-bindgen/src/lib.rs index dd7af53bb..9f27163c3 100644 --- a/crates/js-component-bindgen/src/lib.rs +++ b/crates/js-component-bindgen/src/lib.rs @@ -10,10 +10,10 @@ pub mod source; mod ts_bindgen; mod transpile_bindgen; +use transpile_bindgen::transpile_bindgen; pub use transpile_bindgen::TranspileOpts; use anyhow::{bail, Context}; -use heck::*; use wasmtime_environ::component::Export; use wasmtime_environ::component::{ComponentTypesBuilder, Translator}; use wasmtime_environ::wasmparser::{Validator, WasmFeatures}; @@ -72,7 +72,7 @@ pub fn transpile(component: Vec, opts: TranspileOpts) -> Result, opts: TranspileOpts) -> Result impt.to_string(), - None => impt.to_string(), - } - } else { - impt.to_string() - } - }) - .collect(); - - let exports = component - .exports - .iter() - .filter(|expt| { - matches!( - expt.1, - Export::Instance(_) | Export::Module(_) | Export::LiftedFunction { .. } - ) - }) - .map(|expt| (expt.0.to_lower_camel_case(), expt.1.clone())) - .collect(); - - transpile_bindgen::transpile_bindgen( + let (imports, exports) = transpile_bindgen( &name, &component, &modules, &resolve, world_id, opts, &mut files, ); diff --git a/crates/js-component-bindgen/src/transpile_bindgen.rs b/crates/js-component-bindgen/src/transpile_bindgen.rs index a725b64b5..c75b7ee2a 100644 --- a/crates/js-component-bindgen/src/transpile_bindgen.rs +++ b/crates/js-component-bindgen/src/transpile_bindgen.rs @@ -1,12 +1,13 @@ use crate::files::Files; use crate::function_bindgen::{ErrHandling, FunctionBindgen}; -use crate::identifier::is_js_identifier; +use crate::identifier::{maybe_quote_id, maybe_quote_member}; use crate::intrinsics::{render_intrinsics, Intrinsic}; use crate::source; use crate::{uwrite, uwriteln}; use base64::{engine::general_purpose, Engine as _}; use heck::*; use indexmap::IndexMap; +use std::collections::btree_map::Entry; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::fmt::Write; use std::mem; @@ -43,7 +44,7 @@ pub struct TranspileOpts { pub valid_lifting_optimization: bool, } -struct JsBindgen { +struct JsBindgen<'a> { /// The source code for the "main" file that's going to be created for the /// component we're generating bindings for. This is incrementally added to /// over time and primarily contains the main `instantiate` function as well @@ -51,13 +52,13 @@ struct JsBindgen { src: Source, /// JS output imports map from imported specifier, to a list of bindings - imports: HashMap>, + imports: BTreeMap>, /// Core module count core_module_cnt: usize, /// Various options for code generation. - pub opts: TranspileOpts, + opts: &'a TranspileOpts, /// List of all intrinsics emitted to `src` so far. all_intrinsics: BTreeSet, @@ -71,18 +72,43 @@ pub fn transpile_bindgen( id: WorldId, opts: TranspileOpts, files: &mut Files, -) { +) -> (Vec, Vec<(String, Export)>) { let mut bindgen = JsBindgen { src: Source::default(), - imports: HashMap::new(), + imports: BTreeMap::new(), core_module_cnt: 0, - opts, + opts: &opts, all_intrinsics: BTreeSet::new(), }; bindgen.core_module_cnt = modules.len(); // bindings is the actual `instantiate` method itself, created by this // structure. + + // populate reverse map from import names to world items + let mut imports = BTreeMap::new(); + let mut exports = BTreeMap::new(); + for (key, _) in &resolve.worlds[id].imports { + let name = match key { + WorldKey::Name(name) => name.to_string(), + WorldKey::Interface(iface) => match resolve.id_of(*iface) { + Some(name) => name.to_string(), + None => continue, + }, + }; + imports.insert(name, key.clone()); + } + for (key, _) in &resolve.worlds[id].exports { + let name = match key { + WorldKey::Name(name) => name.to_string(), + WorldKey::Interface(iface) => match resolve.id_of(*iface) { + Some(name) => name.to_string(), + None => continue, + }, + }; + exports.insert(name, key.clone()); + } + let mut instantiator = Instantiator { src: Source::default(), sizes: SizeAlign::default(), @@ -92,16 +118,32 @@ pub fn transpile_bindgen( resolve, world: id, component, + imports, + exports, }; instantiator.sizes.fill(resolve); - instantiator.instantiate(); + let exports = instantiator.instantiate(); + let imports = instantiator + .imports + .keys() + .map(|key| match parse_world_key(key) { + Some((ns, package_name, _)) => { + map_import(&opts.map, &key[0..ns.len() + package_name.len() + 1]).unwrap_or_else( + || key[ns.len() + 1..ns.len() + package_name.len() + 1].to_string(), + ) + } + None => map_import(&opts.map, key).unwrap_or_else(|| key.to_string()), + }) + .collect(); instantiator.gen.src.js(&instantiator.src.js); instantiator.gen.src.js_init(&instantiator.src.js_init); bindgen.finish_component(name, files); + + (imports, exports) } -impl JsBindgen { +impl<'a> JsBindgen<'a> { fn finish_component(&mut self, name: &str, files: &mut Files) { let mut output = source::Source::default(); let mut compilation_promises = source::Source::default(); @@ -229,29 +271,6 @@ impl JsBindgen { files.push(&format!("{name}.js"), bytes); } - fn map_import(&self, impt: &str) -> String { - if let Some(map) = self.opts.map.as_ref() { - for (key, mapping) in map { - if key == impt { - return mapping.into(); - } - if let Some(wildcard_idx) = key.find('*') { - let lhs = &key[0..wildcard_idx]; - let rhs = &key[wildcard_idx + 1..]; - if impt.starts_with(lhs) && impt.ends_with(rhs) { - let matched = - &impt[wildcard_idx..wildcard_idx + impt.len() - lhs.len() - rhs.len()]; - return mapping.replace('*', matched); - } - } - } - if let Some(mapping) = map.get(impt) { - return mapping.into(); - } - } - impt.into() - } - fn intrinsic(&mut self, intrinsic: Intrinsic) -> String { self.all_intrinsics.insert(intrinsic); return intrinsic.name().to_string(); @@ -261,19 +280,21 @@ impl JsBindgen { /// Helper structure used to generate the `instantiate` method of a component. /// /// This is the main structure for parsing the output of Wasmtime. -struct Instantiator<'a> { +struct Instantiator<'a, 'b> { src: Source, - gen: &'a mut JsBindgen, + gen: &'a mut JsBindgen<'b>, modules: &'a PrimaryMap>, instances: PrimaryMap, resolve: &'a Resolve, world: WorldId, sizes: SizeAlign, component: &'a Component, + exports: BTreeMap, + imports: BTreeMap, } -impl Instantiator<'_> { - fn instantiate(&mut self) { +impl Instantiator<'_, '_> { + fn instantiate(&mut self) -> Vec<(String, Export)> { // To avoid uncaught promise rejection errors, we attach an intermediate // Promise.all with a rejection handler, if there are multiple promises. if self.modules.len() > 1 { @@ -294,10 +315,11 @@ impl Instantiator<'_> { if self.gen.opts.instantiation { let js_init = mem::take(&mut self.src.js_init); self.src.js.push_str(&js_init); - self.src.js("return "); } - self.exports(&self.component.exports); + let exports = self.exports(&self.component.exports); + + exports } fn instantiation_global_initializer(&mut self, init: &GlobalInitializer) { @@ -348,13 +370,13 @@ impl Instantiator<'_> { // for now since it can't be tested and additionally JS doesn't // support multi-memory which transcoders rely on anyway. GlobalInitializer::Transcoder(Transcoder { - index, - op, - from, - from64, - to, - to64, - signature, + index: _, + op: _, + from: _, + from64: _, + to: _, + to64: _, + signature: _, }) => unimplemented!(), } } @@ -378,18 +400,10 @@ impl Instantiator<'_> { if !import_obj.is_empty() { imports.push_str(", {\n"); for (module, names) in import_obj { - if is_js_identifier(module) { - imports.push_str(module); - } else { - uwrite!(imports, "'{module}'"); - } + imports.push_str(&maybe_quote_id(module)); imports.push_str(": {\n"); for (name, val) in names { - if is_js_identifier(name) { - imports.push_str(name); - } else { - uwrite!(imports, "'{name}'"); - } + imports.push_str(&maybe_quote_id(name)); uwriteln!(imports, ": {val},"); } imports.push_str("},\n"); @@ -413,45 +427,114 @@ impl Instantiator<'_> { // time `wit-component` only supports root-level imports of instances // where instances export functions. let (import_index, path) = &self.component.imports[import.import]; - let (import_name, _import_ty) = &self.component.import_types[*import_index]; - let func = match &self.resolve.worlds[self.world].imports[import_name.as_str()] { - WorldItem::Function(f) => { - assert_eq!(path.len(), 0); - f + let (import_name, _) = &self.component.import_types[*import_index]; + + let world_item = &self.imports[import_name]; + + let (func, import_specifier, interface_name) = match parse_world_key(import_name) { + Some((ns, package_name, export)) => { + // namespaces must either be explicitly mapped to URLs + // or, if no map entry is provided by the user, a rewrite into "kebab name" is + // automatically performed with the default convention of using the package name as the import + // and the package name and interface name as the export kebab name itself + // if there is no other export using the interface name, the interface name is exported directly as + // an additional alias + let mapped_import_name = map_import( + &self.gen.opts.map, + &import_name[0..ns.len() + package_name.len() + 1], + ); + match &self.resolve.worlds[self.world].imports[world_item] { + WorldItem::Function(f) => { + assert_eq!(path.len(), 0); + ( + f, + mapped_import_name.unwrap_or_else(|| package_name.to_string()), + "".to_string(), + ) + } + WorldItem::Interface(i) => { + assert_eq!(path.len(), 1); + let func = &self.resolve.interfaces[*i].functions[&path[0]]; + ( + func, + mapped_import_name.unwrap_or_else(|| package_name.to_string()), + format!("{package_name}-{export}"), + ) + } + WorldItem::Type(_) => unreachable!(), + } } - WorldItem::Interface(i) => { - assert_eq!(path.len(), 1); - &self.resolve.interfaces[*i].functions[&path[0]] + None => { + let mapped_import_name = map_import(&self.gen.opts.map, import_name); + match &self.resolve.worlds[self.world].imports[world_item] { + WorldItem::Function(f) => { + assert_eq!(path.len(), 0); + ( + f, + mapped_import_name.unwrap_or_else(|| import_name.to_string()), + "default".to_string(), + ) + } + WorldItem::Interface(i) => { + assert_eq!(path.len(), 1); + let func = &self.resolve.interfaces[*i].functions[&path[0]]; + ( + func, + mapped_import_name.unwrap_or_else(|| import_name.to_string()), + "*".to_string(), + ) + } + WorldItem::Type(_) => unreachable!(), + } } - WorldItem::Type(_) => unreachable!(), }; + let func_or_interface_id = (if interface_name == "*" { + &func.name + } else { + interface_name.as_str() + }) + .to_lower_camel_case(); + let index = import.index.as_u32(); let callee = format!("lowering{index}Callee"); - let import_specifier = self.gen.map_import(import_name); + let imports_map = self + .gen + .imports + .entry(import_specifier.to_string()) + .or_insert(BTreeMap::new()); - let id = func.name.to_lower_camel_case(); + let local_name = if interface_name == "*" { + callee.to_string() + } else { + format!("interface{index}") + }; + + let entry = imports_map.entry(func_or_interface_id.to_string()); + let vacant_interface = matches!(entry, Entry::Vacant(_)); + let local_name = entry.or_insert(local_name); // instance imports are otherwise hoisted if self.gen.opts.instantiation { - uwriteln!( + if vacant_interface { + uwriteln!( + self.src.js, + "const {local_name} = imports{}{};", + maybe_quote_member(&import_specifier), + maybe_quote_member(&func_or_interface_id), + ); + } + } + + // in the interface case we must lower the interface function + if interface_name != "*" { + let fn_id = func.name.to_lower_camel_case(); + uwrite!( self.src.js, - "const {callee} = imports{}.{};", - if is_js_identifier(&import_specifier) { - format!(".{}", import_specifier) - } else { - format!("['{}']", import_specifier) - }, - id + "const lowering{index}Callee = {local_name}{};", + maybe_quote_member(&fn_id) ); - } else { - let imports_vec = self - .gen - .imports - .entry(import_specifier) - .or_insert(Vec::new()); - imports_vec.push((id, callee.clone())); } uwrite!(self.src.js, "\nfunction lowering{index}"); @@ -577,29 +660,31 @@ impl Instantiator<'_> { ExportItem::Name(s) => s, }; let i = export.instance.as_u32() as usize; - if is_js_identifier(name) { - format!("exports{i}.{name}") - } else { - format!("exports{i}['{name}']") - } + format!("exports{i}{}", maybe_quote_member(name)) } - fn exports(&mut self, exports: &IndexMap) { + fn exports(&mut self, exports: &IndexMap) -> Vec<(String, Export)> { if exports.is_empty() { if self.gen.opts.instantiation { - self.src.js("{}"); + self.src.js("return {}"); } - return; - } - - if self.gen.opts.instantiation { - uwriteln!(self.src.js, "{{"); + return Vec::new(); } - let mut camel_exports = Vec::new(); - for (name, export) in exports { - let item = &self.resolve.worlds[self.world].exports[name]; - let camel = name.to_lower_camel_case(); + let mut camel_exports = BTreeMap::new(); + let mut camel_aliases = BTreeMap::new(); + for (export_name, export) in exports.iter() { + let world_key = &self.exports[export_name]; + let item = &self.resolve.worlds[self.world].exports[world_key]; + let (camel_name, maybe_camel_alias) = + if let Some((_, package_name, export)) = parse_world_key(export_name) { + ( + format!("{package_name}-{export}").to_lower_camel_case(), + Some(export.to_lower_camel_case()), + ) + } else { + (export_name.to_lower_camel_case(), None) + }; match export { Export::LiftedFunction { ty: _, @@ -607,7 +692,7 @@ impl Instantiator<'_> { options, } => { self.export_bindgen( - name, + export_name, None, func, options, @@ -617,17 +702,13 @@ impl Instantiator<'_> { }, ); } - Export::Instance(exports) => { + Export::Instance(iface) => { let id = match item { WorldItem::Interface(id) => *id, WorldItem::Function(_) | WorldItem::Type(_) => unreachable!(), }; - if self.gen.opts.instantiation { - uwriteln!(self.src.js, "{camel}: {{"); - } else { - uwriteln!(self.src.js, "const {camel} = {{"); - } - for (func_name, export) in exports { + uwriteln!(self.src.js, "const {camel_name} = {{"); + for (func_name, export) in iface { let (func, options) = match export { Export::LiftedFunction { func, options, .. } => (func, options), Export::Type(_) => continue, // ignored @@ -635,18 +716,13 @@ impl Instantiator<'_> { }; self.export_bindgen( func_name, - Some(name), + Some(export_name), func, options, &self.resolve.interfaces[id].functions[func_name], ); } - self.src.js("\n}"); - if self.gen.opts.instantiation { - self.src.js(",\n"); - } else { - self.src.js(";\n"); - } + uwriteln!(self.src.js, "\n}};"); } // ignore type exports for now @@ -655,13 +731,50 @@ impl Instantiator<'_> { // This can't be tested at this time so leave it unimplemented Export::Module(_) => unimplemented!(), } - camel_exports.push(camel); + if let Some(camel_alias) = maybe_camel_alias { + camel_aliases.insert(camel_alias, camel_name.to_string()); + } + camel_exports.insert(camel_name, export); } - if self.gen.opts.instantiation { - self.src.js("}"); - } else { - uwriteln!(self.src.js, "\nexport {{ {} }}", camel_exports.join(", ")); + // only promote aliases which aren't collisions against existing exports + for (alias, name) in &camel_aliases { + if !camel_exports.contains_key(alias) { + camel_exports.insert(alias.to_string(), camel_exports.get(name).unwrap().clone()); + } } + uwrite!( + self.src.js, + "\n{} {{ ", + if self.gen.opts.instantiation { + "return" + } else { + "export" + } + ); + uwriteln!( + self.src.js, + "{} }}", + camel_exports + .iter() + .map(|(name, _)| { + let name = name.to_string(); + if let Some(original_name) = camel_aliases.get(&name) { + if self.gen.opts.instantiation { + format!("{name}: {original_name}") + } else { + format!("{original_name} as {name}") + } + } else { + name + } + }) + .collect::>() + .join(", ") + ); + camel_exports + .iter() + .map(|(name, &import)| (name.clone(), import.clone())) + .collect() } fn export_bindgen( @@ -673,7 +786,7 @@ impl Instantiator<'_> { func: &Function, ) { let name = name.to_lower_camel_case(); - if self.gen.opts.instantiation || instance_name.is_some() { + if instance_name.is_some() { self.src.js.push_str(&name); } else { uwrite!(self.src.js, "\nfunction {name}"); @@ -686,7 +799,7 @@ impl Instantiator<'_> { func, AbiVariant::GuestExport, ); - if self.gen.opts.instantiation || instance_name.is_some() { + if instance_name.is_some() { self.src.js(",\n"); } else { self.src.js("\n"); @@ -709,6 +822,42 @@ impl Source { } } +fn map_import(map: &Option>, impt: &str) -> Option { + if let Some(map) = map.as_ref() { + for (key, mapping) in map { + if key == impt { + return Some(mapping.into()); + } + if let Some(wildcard_idx) = key.find('*') { + let lhs = &key[0..wildcard_idx]; + let rhs = &key[wildcard_idx + 1..]; + if impt.starts_with(lhs) && impt.ends_with(rhs) { + let matched = + &impt[wildcard_idx..wildcard_idx + impt.len() - lhs.len() - rhs.len()]; + return Some(mapping.replace('*', matched)); + } + } + } + if let Some(mapping) = map.get(impt) { + return Some(mapping.into()); + } + } + None +} + +pub fn parse_world_key<'a>(name: &'a str) -> Option<(&'a str, &'a str, &'a str)> { + let registry_idx = match name.find(':') { + Some(idx) => idx, + None => return None, + }; + let ns = &name[0..registry_idx]; + match name.rfind('/') { + Some(sep_idx) => Some((ns, &name[registry_idx + 1..sep_idx], &name[sep_idx + 1..])), + // interface is a namespace, function is a default export + None => Some((ns, &name[registry_idx + 1..], "".as_ref())), + } +} + fn core_file_name(name: &str, idx: u32) -> String { let i_str = if idx == 0 { String::from("") diff --git a/crates/js-component-bindgen/src/ts_bindgen.rs b/crates/js-component-bindgen/src/ts_bindgen.rs index 71a2ce8a2..5969a0d9a 100644 --- a/crates/js-component-bindgen/src/ts_bindgen.rs +++ b/crates/js-component-bindgen/src/ts_bindgen.rs @@ -1,9 +1,12 @@ use crate::files::Files; use crate::function_bindgen::{array_ty, as_nullable, maybe_null}; +use crate::identifier::maybe_quote_id; use crate::source::Source; -use crate::transpile_bindgen::TranspileOpts; +use crate::transpile_bindgen::{parse_world_key, TranspileOpts}; use crate::uwriteln; use heck::*; +use std::collections::btree_map::Entry; +use std::collections::{BTreeMap, HashSet}; use std::fmt::Write; use std::mem; use wit_parser::abi::AbiVariant; @@ -51,26 +54,80 @@ pub fn ts_bindgen( let world = &resolve.worlds[id]; bindgen.preprocess(resolve, &world.name); - let mut funcs = Vec::new(); - - for (name, import) in world.imports.iter() { - match import { - WorldItem::Function(f) => funcs.push((name.as_str(), f)), - WorldItem::Interface(id) => bindgen.import_interface(resolve, name, *id, files), - WorldItem::Type(_) => unimplemented!("type imports"), + { + let mut funcs = Vec::new(); + let mut imports = BTreeMap::new(); + for (name, import) in world.imports.iter() { + match import { + WorldItem::Function(f) => funcs.push(f), + WorldItem::Interface(id) => match name { + WorldKey::Name(name) => { + // kebab name -> direct ns namespace import + bindgen.import_interface(resolve, name, *id, files); + } + // namespaced ns:pkg/iface + // -> group by pkg import by convention + WorldKey::Interface(id) => { + let import_name = resolve.id_of(*id).unwrap(); + let (_, pkg, iface) = parse_world_key(&import_name).unwrap(); + let specifier_id = pkg.to_string(); + match imports.entry(specifier_id) { + Entry::Vacant(gap) => { + gap.insert(vec![(format!("{}-{}", pkg, iface), id)]); + } + Entry::Occupied(ref mut entry) => { + entry.get_mut().push((format!("{}-{}", pkg, iface), id)); + } + } + } + }, + WorldItem::Type(_) => {} + } + } + // kebab import funcs (always default imports) + if !funcs.is_empty() { + bindgen.import_funcs(resolve, id, &funcs, files); + } + // namespace imports are grouped by namespace / kebab name + // kebab name imports are direct + for (name, import_interfaces) in imports { + bindgen.import_interfaces(resolve, name.as_ref(), import_interfaces, files); } } - if !funcs.is_empty() { - bindgen.import_funcs(resolve, id, &funcs, files); - } - funcs.clear(); + + let mut funcs = Vec::new(); + let mut seen_names = HashSet::new(); + let mut export_aliases: Vec<(String, String)> = Vec::new(); for (name, export) in world.exports.iter() { + let name = match name { + WorldKey::Name(name) => name.to_string(), + WorldKey::Interface(interface) => { + let export_name = resolve.id_of(*interface).unwrap(); + let (_, pkg, iface) = parse_world_key(&export_name).unwrap(); + let out_name = format!("{}-{}", pkg, iface); + export_aliases.push((iface.to_string(), out_name.to_string())); + out_name + } + }; + seen_names.insert(name.to_string()); match export { - WorldItem::Function(f) => funcs.push((name.as_str(), f)), - WorldItem::Interface(id) => bindgen.export_interface(resolve, name, *id, files), + WorldItem::Function(f) => funcs.push((name, f)), + WorldItem::Interface(id) => { + bindgen.export_interface(resolve, &name, *id, files); + } WorldItem::Type(_) => unimplemented!("type exports"), } } + for (alias, name) in export_aliases { + if !seen_names.contains(&alias) { + uwriteln!( + bindgen.export_object, + "{}: typeof {}Exports", + alias, + name.to_upper_camel_case() + ); + } + } if !funcs.is_empty() { let end_character = if opts.instantiation { "," } else { ";" }; bindgen.export_funcs(resolve, id, &funcs, files, end_character); @@ -163,19 +220,50 @@ impl TsBindgen { AbiVariant::GuestImport, ); let camel = name.to_upper_camel_case(); - uwriteln!(self.import_object, "'{name}': typeof {camel}Imports,"); + uwriteln!( + self.import_object, + "{}: typeof {camel}Imports,", + maybe_quote_id(name) + ); + } + fn import_interfaces( + &mut self, + resolve: &Resolve, + import_name: &str, + ifaces: Vec<(String, &InterfaceId)>, + files: &mut Files, + ) { + uwriteln!(self.import_object, "{}: {{", maybe_quote_id(import_name),); + for (name, &id) in ifaces { + self.generate_interface( + &name, + resolve, + id, + "imports", + "Imports", + files, + AbiVariant::GuestImport, + ); + let camel = name.to_upper_camel_case(); + uwriteln!( + self.import_object, + "{}: typeof {camel}Imports,", + name.to_lower_camel_case() + ); + } + uwriteln!(self.import_object, "}},"); } fn import_funcs( &mut self, resolve: &Resolve, _world: WorldId, - funcs: &[(&str, &Function)], + funcs: &[&Function], _files: &mut Files, ) { let mut gen = self.js_interface(resolve); - for (_, func) in funcs { - gen.ts_func(func, AbiVariant::GuestImport, ","); + for func in funcs { + gen.ts_func(func, AbiVariant::GuestImport, true, ","); } gen.gen.import_object.push_str(&gen.src); } @@ -197,14 +285,18 @@ impl TsBindgen { AbiVariant::GuestExport, ); let camel = name.to_upper_camel_case(); - uwriteln!(self.export_object, "'{name}': typeof {camel}Exports,"); + uwriteln!( + self.export_object, + "{}: typeof {camel}Exports,", + name.to_lower_camel_case() + ); } fn export_funcs( &mut self, resolve: &Resolve, _world: WorldId, - funcs: &[(&str, &Function)], + funcs: &[(String, &Function)], _files: &mut Files, end_character: &str, ) { @@ -213,7 +305,7 @@ impl TsBindgen { if end_character == ";" { gen.src.push_str("export function "); } - gen.ts_func(func, AbiVariant::GuestExport, end_character); + gen.ts_func(func, AbiVariant::GuestExport, false, end_character); } gen.gen.export_object.push_str(&gen.src); @@ -240,7 +332,7 @@ impl TsBindgen { uwriteln!(gen.src, "export namespace {camel} {{"); for (_, func) in resolve.interfaces[id].functions.iter() { gen.src.push_str("export function "); - gen.ts_func(func, abi, ";"); + gen.ts_func(func, abi, false, ";"); } uwriteln!(gen.src, "}}"); @@ -397,10 +489,14 @@ impl<'a> TsInterface<'a> { self.src.push_str("]"); } - fn ts_func(&mut self, func: &Function, abi: AbiVariant, end_character: &str) { + fn ts_func(&mut self, func: &Function, abi: AbiVariant, default: bool, end_character: &str) { self.docs(&func.docs); - self.src.push_str(&func.item_name().to_lower_camel_case()); + if default { + self.src.push_str("default"); + } else { + self.src.push_str(&func.item_name().to_lower_camel_case()); + } self.src.push_str("("); let param_start = match &func.kind { @@ -411,7 +507,7 @@ impl<'a> TsInterface<'a> { if i > 0 { self.src.push_str(", "); } - self.src.push_str(to_js_ident(&name.to_lower_camel_case())); + self.src.push_str(&name.to_lower_camel_case()); self.src.push_str(": "); self.print_ty( ty, @@ -475,7 +571,7 @@ impl<'a> TsInterface<'a> { as_nullable(self.resolve, &field.ty).map_or(("", &field.ty), |ty| ("?", ty)); self.src.push_str(&format!( "{}{}: ", - field.name.to_lower_camel_case(), + maybe_quote_id(&field.name.to_lower_camel_case()), option_str )); self.print_ty(ty, Mode::Lift); @@ -665,11 +761,3 @@ impl<'a> TsInterface<'a> { self.src.push_str(";\n"); } } - -fn to_js_ident(name: &str) -> &str { - match name { - "in" => "in_", - "import" => "import_", - s => s, - } -} diff --git a/crates/wasm-tools-component/src/lib.rs b/crates/wasm-tools-component/src/lib.rs index 544dbad8a..96ba864f9 100644 --- a/crates/wasm-tools-component/src/lib.rs +++ b/crates/wasm-tools-component/src/lib.rs @@ -2,16 +2,14 @@ use std::collections::VecDeque; use std::path::PathBuf; use wasm_encoder::{Encode, Section}; use wasm_metadata::Producers; -use wit_component::{ComponentEncoder, DecodedWasm, DocumentPrinter, StringEncoding}; +use wit_component::{ComponentEncoder, DecodedWasm, WitPrinter}; use wit_parser::{Resolve, UnresolvedPackage}; wit_bindgen::generate!("wasm-tools"); struct WasmToolsJs; -export_wasm_tools_js!(WasmToolsJs); - -use crate::exports::*; +export_wasm_tools!(WasmToolsJs); // fn init() { // static INIT: Once = Once::new(); @@ -24,7 +22,7 @@ use crate::exports::*; // }); // } -impl exports::Exports for WasmToolsJs { +impl WasmTools for WasmToolsJs { fn parse(wat: String) -> Result, String> { // init(); @@ -63,27 +61,27 @@ impl exports::Exports for WasmToolsJs { Ok(bytes) } - fn component_wit(binary: Vec, name: Option) -> Result { + fn component_wit(binary: Vec) -> Result { // init(); - let decoded = wit_component::decode(&name.unwrap_or(String::from("component")), &binary) + let decoded = wit_component::decode(&binary) .map_err(|e| format!("Failed to decode wit component\n{:?}", e))?; // let world = decode_world("component", &binary); let doc = match &decoded { DecodedWasm::WitPackage(_resolve, _pkg) => panic!("Unexpected wit package"), - DecodedWasm::Component(resolve, world) => resolve.worlds[*world].document, + DecodedWasm::Component(resolve, world) => resolve.worlds[*world].package.unwrap(), }; - let output = DocumentPrinter::default() + let output = WitPrinter::default() .print(decoded.resolve(), doc) .map_err(|e| format!("Unable to print wit\n${:?}", e))?; Ok(output) } - fn component_embed(embed_opts: exports::EmbedOpts) -> Result, String> { + fn component_embed(embed_opts: EmbedOpts) -> Result, String> { let binary = &embed_opts.binary; let mut resolve = Resolve::default(); @@ -96,9 +94,7 @@ impl exports::Exports for WasmToolsJs { UnresolvedPackage::parse_file(&PathBuf::from(wit_path)).map_err(|e| e.to_string())? }; - let id = resolve - .push(pkg, &Default::default()) - .map_err(|e| e.to_string())?; + let id = resolve.push(pkg).map_err(|e| e.to_string())?; let world_string = match &embed_opts.world { Some(world) => Some(world.to_string()), @@ -109,14 +105,14 @@ impl exports::Exports for WasmToolsJs { .map_err(|e| e.to_string())?; let string_encoding = match &embed_opts.string_encoding { - None | Some(exports::StringEncoding::Utf8) => StringEncoding::UTF8, - Some(exports::StringEncoding::Utf16) => StringEncoding::UTF16, - Some(exports::StringEncoding::CompactUtf16) => StringEncoding::CompactUTF16, + None | Some(StringEncoding::Utf8) => wit_component::StringEncoding::UTF8, + Some(StringEncoding::Utf16) => wit_component::StringEncoding::UTF16, + Some(StringEncoding::CompactUtf16) => wit_component::StringEncoding::CompactUTF16, }; let mut core_binary = if matches!( &embed_opts, - exports::EmbedOpts { + EmbedOpts { dummy: Some(true), .. } diff --git a/crates/wasm-tools-component/wit/wasm-tools.wit b/crates/wasm-tools-component/wit/wasm-tools.wit index b37fb1d81..8eeb2d011 100644 --- a/crates/wasm-tools-component/wit/wasm-tools.wit +++ b/crates/wasm-tools-component/wit/wasm-tools.wit @@ -1,58 +1,58 @@ -default world wasm-tools-js { - export exports: interface { - /// Translate the WebAssembly text format to binary - parse: func(wat: string) -> result, string> - - /// Translate the WebAssembly binary format to text - print: func(binary: list) -> result - - enum string-encoding { - utf8, - utf16, - compact-utf16 - } - - /// Create a component from a core wasm binary that implements and embeds a component type - component-new: func(binary: list, adapters: option>>>) -> result, string> - - /// Extract a *.wit interface from a component, optionally providing a document name to extract - component-wit: func(binary: list, document: option) -> result - - type producers-fields = list>>> - - /// Embed a WIT type into a component. - /// Only a singular WIT document without use resolutions is supported for this API. - record embed-opts { - binary: option>, - /// Pass an inline WIT source - wit-source: option, - /// Pass the file system path to WIT file - wit-path: option, - string-encoding: option, - dummy: option, - %world: option, - metadata: option - } - - component-embed: func(embed-opts: embed-opts) -> result, string> - - variant module-meta-type { - module, - // the number of nested modules - component(u32), - } - - record module-metadata { - name: option, - meta-type: module-meta-type, - range: tuple, - producers: producers-fields, - } - - /// Extract the metadata for a component - metadata-show: func(binary: list) -> result, string> - - /// Append producer metadata to a component - metadata-add: func(binary: list, metadata: producers-fields) -> result, string> +package local:wasm-tools + +world wasm-tools { + /// Translate the WebAssembly text format to binary + export parse: func(wat: string) -> result, string> + + /// Translate the WebAssembly binary format to text + export print: func(binary: list) -> result + + enum string-encoding { + utf8, + utf16, + compact-utf16 + } + + /// Create a component from a core wasm binary that implements and embeds a component type + export component-new: func(binary: list, adapters: option>>>) -> result, string> + + /// Extract a *.wit interface from a component, optionally providing a document name to extract + export component-wit: func(binary: list) -> result + + type producers-fields = list>>> + + /// Embed a WIT type into a component. + /// Only a singular WIT document without use resolutions is supported for this API. + record embed-opts { + binary: option>, + /// Pass an inline WIT source + wit-source: option, + /// Pass the file system path to WIT file + wit-path: option, + string-encoding: option, + dummy: option, + %world: option, + metadata: option + } + + export component-embed: func(embed-opts: embed-opts) -> result, string> + + variant module-meta-type { + module, + // the number of nested modules + component(u32), + } + + record module-metadata { + name: option, + meta-type: module-meta-type, + range: tuple, + producers: producers-fields, } + + /// Extract the metadata for a component + export metadata-show: func(binary: list) -> result, string> + + /// Append producer metadata to a component + export metadata-add: func(binary: list, metadata: producers-fields) -> result, string> } diff --git a/lib/wasi_preview1_component_adapter.command.wasm b/lib/wasi_preview1_component_adapter.command.wasm deleted file mode 100644 index 956ba52fde1d54559ac02e8c459be1fd34b12083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88115 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 diff --git a/lib/wasi_preview1_component_adapter.reactor.wasm b/lib/wasi_preview1_component_adapter.reactor.wasm deleted file mode 100644 index c85dbc7e35297de55269c14564f98ca3502a55ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87996 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∨x}9O4!d~q9&~)vorIOmeso>ekWgUg#G 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 |aV}2&z!d{qY-IF{#^QwIr*t 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 literal 0 HcmV?d00001 diff --git a/lib/wasi_snapshot_preview1.reactor.wasm b/lib/wasi_snapshot_preview1.reactor.wasm new file mode 100644 index 0000000000000000000000000000000000000000..5d4017c752afa427a92f5d8e957b245ba408740c GIT binary patch literal 105973 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 literal 0 HcmV?d00001 diff --git a/package.json b/package.json index 8bf7b5f67..3efa0ea56 100644 --- a/package.json +++ b/package.json @@ -24,18 +24,8 @@ "typescript": "^4.3.2" }, "scripts": { - "build": "npm run build:set-last && npm run build:dev && ./build-dist.sh", - "build:self": "npm run build:set-self && npm run build:dev && ./build-dist.sh", - "build:set-last": "echo './node_modules/.bin/jco $@' > jco.sh && chmod +x jco.sh", - "build:set-self": "echo './src/jco.js $@' > jco.sh && chmod +x jco.sh", - "build:dev": "npm run build:wasm && mkdir -p obj && npm run build:js-component-bindgen-component && npm run build:wasm-tools", - "build:wasm": "cargo build --workspace --target wasm32-wasi --release", - "build:js-component-bindgen-component": "npm run build:component:js-component-bindgen-component && npm run build:transpile:js-component-bindgen-component", - "build:wasm-tools": "npm run build:component:wasm-tools && npm run build:transpile:wasm-tools", - "build:component:js-component-bindgen-component": "./jco.sh new target/wasm32-wasi/release/js_component_bindgen_component.wasm -o obj/js-component-bindgen-component.wasm --adapt wasi_snapshot_preview1=lib/wasi_preview1_component_adapter.reactor.wasm", - "build:component:wasm-tools": "./jco.sh new target/wasm32-wasi/release/wasm_tools_js.wasm -o obj/wasm-tools.wasm --adapt wasi_snapshot_preview1=lib/wasi_preview1_component_adapter.reactor.wasm", - "build:transpile:js-component-bindgen-component": "./jco.sh transpile obj/js-component-bindgen-component.wasm --out-dir obj", - "build:transpile:wasm-tools": "./jco.sh transpile obj/wasm-tools.wasm --out-dir obj", + "build": "npm run build:dev && ./build-dist.sh", + "build:dev": "cargo build --workspace --target wasm32-wasi --release && cargo run", "build:types:preview2-shim": "PREVIEW2_SHIM_TYPES=* cargo build -p jco", "lint": "eslint -c eslintrc.cjs lib/**/*.js packages/*/lib/**/*.js", "test": "mocha -u tdd test/test.js --timeout 120000", diff --git a/packages/preview2-shim/lib/nodejs/preopens.js b/packages/preview2-shim/lib/nodejs/cli-base.js similarity index 66% rename from packages/preview2-shim/lib/nodejs/preopens.js rename to packages/preview2-shim/lib/nodejs/cli-base.js index 737b9beab..a1960b1a9 100644 --- a/packages/preview2-shim/lib/nodejs/preopens.js +++ b/packages/preview2-shim/lib/nodejs/cli-base.js @@ -56,14 +56,46 @@ export function _setPreopens (preopens) { } } -export function getStdio () { - return { - stdin: 0, - stdout: 1, - stderr: 2, - }; +let _env; +export function _setEnv (envObj) { + _env = Object.entries(envObj); } -export function getDirectories () { - return directories; +export const cliBaseEnvironment = { + getEnvironment () { + if (!_env) _setEnv(process.env); + return _env; + } +}; + +export const cliBaseExit = { + exit (status) { + process.exit(status.tag === 'err' ? 1 : 0); + } +}; + +export const cliBasePreopens = { + getDirectories () { + return directories; + } } + +export const cliBaseStdin = { + getStdin () { + return 0; + } +}; + +export const cliBaseStdout = { + getStdout () { + return 1; + } +}; + +export const cliBaseStderr = { + getStderr () { + return 2; + } +}; + +export { cliBaseEnvironment as environment, cliBaseExit as exit, cliBasePreopens as preopens, cliBaseStdin as stdin, cliBaseStdout as stdout, cliBaseStderr as stderr } diff --git a/packages/preview2-shim/lib/nodejs/environment.js b/packages/preview2-shim/lib/nodejs/environment.js deleted file mode 100644 index 28e5b8cda..000000000 --- a/packages/preview2-shim/lib/nodejs/environment.js +++ /dev/null @@ -1,19 +0,0 @@ -import process from 'node:process'; - -let _env; -export function _setEnv (envObj) { - _env = Object.entries(envObj); -} - -export function getEnvironment () { - if (!_env) _setEnv(process.env); - return _env; -} - -export function preopens () { - return []; -} - -export function getArguments () { - return []; -} \ No newline at end of file diff --git a/packages/preview2-shim/lib/nodejs/exit.js b/packages/preview2-shim/lib/nodejs/exit.js deleted file mode 100644 index a65f5f5a8..000000000 --- a/packages/preview2-shim/lib/nodejs/exit.js +++ /dev/null @@ -1,3 +0,0 @@ -export function exit(status) { - process.exit(status.tag === 'err' ? 1 : 0); -} diff --git a/packages/preview2-shim/lib/nodejs/filesystem.js b/packages/preview2-shim/lib/nodejs/filesystem.js index 9fa6c6c96..ad876365c 100644 --- a/packages/preview2-shim/lib/nodejs/filesystem.js +++ b/packages/preview2-shim/lib/nodejs/filesystem.js @@ -1,81 +1,8 @@ import { openSync, constants, statSync, lstatSync, fstatSync, closeSync, readdirSync } from 'node:fs'; -import { _descriptors, _addOpenedDescriptor, _removeOpenedDescriptor, _getDescriptorType, _setSubdescriptorType, _setDescriptorType, _getFullPath } from './preopens.js'; -import { _createFileStream } from './streams.js'; - -export function readViaStream(fd, offset) { - return _createFileStream(fd, offset); -} - -export function writeViaStream(fd, offset) { - console.log(`[filesystem] WRITE STREAM ${fd} ${offset}`); -} - -export function appendViaStream(fd) { - console.log(`[filesystem] APPEND STREAM ${fd}`); -} - -export function advise(fd, offset, length, advice) { - console.log(`[filesystem] ADVISE`, fd, offset, length, advice); -} - -export function syncData(fd) { - console.log(`[filesystem] SYNC DATA ${fd}`); -} - -export function getFlags(fd) { - console.log(`[filesystem] FLAGS FOR ${fd}`); -} - -export function getType(fd) { - let type = _getDescriptorType(fd); - if (type === null) { - stat(fd); - type = _getDescriptorType(fd); - } - return type; -} - -export function setFlags(fd, flags) { - console.log(`[filesystem] SET FLAGS ${fd} ${JSON.stringify(flags)}`); -} - -export function setSize(fd, size) { - console.log(`[filesystem] SET SIZE`, fd, size); -} - -export function setTimes(fd, dataAccessTimestamp, dataModificationTimestamp) { - console.log(`[filesystem] SET TIMES`, fd, dataAccessTimestamp, dataModificationTimestamp); -} - -export function read(fd, length, offset) { - console.log(`[filesystem] READ`, fd, length, offset); -} - -export function write(fd, buffer, offset) { - console.log(`[filesystem] WRITE`, fd, buffer, offset); -} +import { _descriptors, _addOpenedDescriptor, _removeOpenedDescriptor, _getDescriptorType, _setSubdescriptorType, _setDescriptorType, _getFullPath } from './cli-base.js'; +import { _createFileStream } from './io.js'; let _dirStreams = []; -export function readDirectory(fd) { - const fullPath = _getFullPath(fd); - let dirs; - try { - dirs = readdirSync(fullPath, { withFileTypes: true }); - } - catch (e) { - _convertFsError(e); - } - _dirStreams.push({ fd, dirs, cursor: 0 }); - return _dirStreams.length - 1; -} - -export function sync(fd) { - console.log(`[filesystem] SYNC`, fd); -} - -export function createDirectoryAt(fd, path) { - console.log(`[filesystem] CREATE DIRECTORY`, fd, path); -} const nsMagnitude = 1_000_000_000_000n; function nsToDateTime (ns) { @@ -84,7 +11,7 @@ function nsToDateTime (ns) { return { seconds, nanoseconds }; } -export function _convertFsError (e) { +function _convertFsError (e) { switch (e.code) { case 'EACCES': throw 'access'; case 'EAGAIN': @@ -146,163 +73,241 @@ function _lookupType (obj) { return 'unknown'; } -export function stat(fd) { - let stats; - try { - stats = fstatSync(fd, { bigint: true }); +export const filesystem = { + readViaStream(fd, offset) { + return _createFileStream(fd, offset); + }, + + writeViaStream(fd, offset) { + console.log(`[filesystem] WRITE STREAM ${fd} ${offset}`); + }, + + appendViaStream(fd) { + console.log(`[filesystem] APPEND STREAM ${fd}`); + }, + + advise(fd, offset, length, advice) { + console.log(`[filesystem] ADVISE`, fd, offset, length, advice); + }, + + syncData(fd) { + console.log(`[filesystem] SYNC DATA ${fd}`); + }, + + getFlags(fd) { + console.log(`[filesystem] FLAGS FOR ${fd}`); + }, + + getType(fd) { + let type = _getDescriptorType(fd); + if (type === null) { + filesystem.stat(fd); + type = _getDescriptorType(fd); + } + return type; + }, + + setFlags(fd, flags) { + console.log(`[filesystem] SET FLAGS ${fd} ${JSON.stringify(flags)}`); + }, + + setSize(fd, size) { + console.log(`[filesystem] SET SIZE`, fd, size); + }, + + setTimes(fd, dataAccessTimestamp, dataModificationTimestamp) { + console.log(`[filesystem] SET TIMES`, fd, dataAccessTimestamp, dataModificationTimestamp); + }, + + read(fd, length, offset) { + console.log(`[filesystem] READ`, fd, length, offset); + }, + + write(fd, buffer, offset) { + console.log(`[filesystem] WRITE`, fd, buffer, offset); + }, + + readDirectory(fd) { + const fullPath = _getFullPath(fd); + let dirs; + try { + dirs = readdirSync(fullPath, { withFileTypes: true }); + } + catch (e) { + _convertFsError(e); + } + _dirStreams.push({ fd, dirs, cursor: 0 }); + return _dirStreams.length - 1; + }, + + sync(fd) { + console.log(`[filesystem] SYNC`, fd); + }, + + createDirectoryAt(fd, path) { + console.log(`[filesystem] CREATE DIRECTORY`, fd, path); + }, + + stat(fd) { + let stats; + try { + stats = fstatSync(fd, { bigint: true }); + } + catch (e) { + _convertFsError(e); + } + const type = _lookupType(stats); + _setDescriptorType(fd, type); + return { + device: stats.dev, + inode: stats.ino, + type, + linkCount: stats.nlink, + size: stats.size, + dataAccessTimestamp: nsToDateTime(stats.atimeNs), + dataModificationTimestamp: nsToDateTime(stats.mtimeNs), + statusChangeTimestamp: nsToDateTime(stats.ctimeNs), + }; + }, + + statAt(fd, { symlinkFollow }, path) { + const fullPath = _descriptors[fd].path + path; + let stats; + try { + stats = (symlinkFollow ? statSync : lstatSync)(fullPath, { bigint: true }); + } + catch (e) { + _convertFsError(e); + } + const type = _lookupType(stats); + _setSubdescriptorType(fd, path, type); + return { + device: stats.dev, + inode: stats.ino, + type, + linkCount: stats.nlink, + size: stats.size, + dataAccessTimestamp: nsToDateTime(stats.atimeNs), + dataModificationTimestamp: nsToDateTime(stats.mtimeNs), + statusChangeTimestamp: nsToDateTime(stats.ctimeNs), + }; + }, + + setTimesAt(fd) { + console.log(`[filesystem] SET TIMES AT`, fd); + }, + + linkAt(fd) { + console.log(`[filesystem] LINK AT`, fd); + }, + + openAt(fd, pathFlags, path, openFlags, descriptorFlags, modes) { + // TODO + // if (pathFlags.symlinkFollow) { + // // resolve symlink + // } + const fullPath = _descriptors[fd].path + path; + let fsOpenFlags = 0x0; + if (openFlags.create) + fsOpenFlags |= constants.O_CREAT; + if (openFlags.directory) + fsOpenFlags |= constants.O_DIRECTORY; + if (openFlags.exclusive) + fsOpenFlags |= constants.O_EXCL; + if (openFlags.truncate) + fsOpenFlags |= constants.O_TRUNC; + if (descriptorFlags.read && descriptorFlags.write) + fsOpenFlags |= constants.O_RDWR; + else if (descriptorFlags.write) + fsOpenFlags |= constants.O_WRONLY; + // if (descriptorFlags.fileIntegritySync) + // if (descriptorFlags.dataIntegritySync) + // if (descriptorFlags.requestedWriteSync) + // if (descriptorFlags.mutateDirectory) + let fsMode = 0x0; + if (modes.readable) + fsMode |= 0o444; + if (modes.writeable) + fsMode |= 0o222; + if (modes.executable) + fsMode |= 0o111; + let localFd; + try { + localFd = openSync(fullPath, fsOpenFlags, fsMode); + } + catch (e) { + _convertFsError(e); + } + _addOpenedDescriptor(localFd, path, fd); + return localFd; + }, + + readlinkAt(fd) { + console.log(`[filesystem] READLINK AT`, fd); + }, + + removeDirectoryAt(fd) { + console.log(`[filesystem] REMOVE DIR AT`, fd); + }, + + renameAt(fd) { + console.log(`[filesystem] RENAME AT`, fd); + }, + + symlinkAt(fd) { + console.log(`[filesystem] SYMLINK AT`, fd); + }, + + unlinkFileAt(fd) { + console.log(`[filesystem] UNLINK FILE AT`, fd); + }, + + changeFilePermissionsAt(fd) { + console.log(`[filesystem] CHANGE FILE PERMISSIONS AT`, fd); + }, + + changeDirectoryPermissionsAt(fd) { + console.log(`[filesystem] CHANGE DIR PERMISSIONS AT`, fd); + }, + + lockShared(fd) { + console.log(`[filesystem] LOCK SHARED`, fd); + }, + + lockExclusive(fd) { + console.log(`[filesystem] LOCK EXCLUSIVE`, fd); + }, + + tryLockShared(fd) { + console.log(`[filesystem] TRY LOCK SHARED`, fd); + }, + + tryLockExclusive(fd) { + console.log(`[filesystem] TRY LOCK EXCLUSIVE`, fd); + }, + + unlock(fd) { + console.log(`[filesystem] UNLOCK`, fd); + }, + + dropDescriptor(fd) { + _removeOpenedDescriptor(fd); + closeSync(fd); + }, + + readDirectoryEntry(stream) { + const streamValue = _dirStreams[stream]; + if (streamValue.cursor === streamValue.dirs.length) + return null; + const dir = streamValue.dirs[streamValue.cursor++]; + const type = _lookupType(dir); + _setSubdescriptorType(streamValue.fd, '/' + dir.name, type); + return { inode: null, type, name: dir.name }; + }, + + dropDirectoryEntryStream(stream) { + _dirStreams.splice(stream, 1); } - catch (e) { - _convertFsError(e); - } - const type = _lookupType(stats); - _setDescriptorType(fd, type); - return { - device: stats.dev, - inode: stats.ino, - type, - linkCount: stats.nlink, - size: stats.size, - dataAccessTimestamp: nsToDateTime(stats.atimeNs), - dataModificationTimestamp: nsToDateTime(stats.mtimeNs), - statusChangeTimestamp: nsToDateTime(stats.ctimeNs), - }; -} - -export function statAt(fd, { symlinkFollow }, path) { - const fullPath = _descriptors[fd].path + path; - let stats; - try { - stats = (symlinkFollow ? statSync : lstatSync)(fullPath, { bigint: true }); - } - catch (e) { - _convertFsError(e); - } - const type = _lookupType(stats); - _setSubdescriptorType(fd, path, type); - return { - device: stats.dev, - inode: stats.ino, - type, - linkCount: stats.nlink, - size: stats.size, - dataAccessTimestamp: nsToDateTime(stats.atimeNs), - dataModificationTimestamp: nsToDateTime(stats.mtimeNs), - statusChangeTimestamp: nsToDateTime(stats.ctimeNs), - }; -} +}; -export function setTimesAt(fd) { - console.log(`[filesystem] SET TIMES AT`, fd); -} - -export function linkAt(fd) { - console.log(`[filesystem] LINK AT`, fd); -} - -export function openAt(fd, pathFlags, path, openFlags, descriptorFlags, modes) { - // TODO - // if (pathFlags.symlinkFollow) { - // // resolve symlink - // } - const fullPath = _descriptors[fd].path + path; - let fsOpenFlags = 0x0; - if (openFlags.create) - fsOpenFlags |= constants.O_CREAT; - if (openFlags.directory) - fsOpenFlags |= constants.O_DIRECTORY; - if (openFlags.exclusive) - fsOpenFlags |= constants.O_EXCL; - if (openFlags.truncate) - fsOpenFlags |= constants.O_TRUNC; - if (descriptorFlags.read && descriptorFlags.write) - fsOpenFlags |= constants.O_RDWR; - else if (descriptorFlags.write) - fsOpenFlags |= constants.O_WRONLY; - // if (descriptorFlags.fileIntegritySync) - // if (descriptorFlags.dataIntegritySync) - // if (descriptorFlags.requestedWriteSync) - // if (descriptorFlags.mutateDirectory) - let fsMode = 0x0; - if (modes.readable) - fsMode |= 0o444; - if (modes.writeable) - fsMode |= 0o222; - if (modes.executable) - fsMode |= 0o111; - let localFd; - try { - localFd = openSync(fullPath, fsOpenFlags, fsMode); - } - catch (e) { - _convertFsError(e); - } - _addOpenedDescriptor(localFd, path, fd); - return localFd; -} - -export function readlinkAt(fd) { - console.log(`[filesystem] READLINK AT`, fd); -} - -export function removeDirectoryAt(fd) { - console.log(`[filesystem] REMOVE DIR AT`, fd); -} - -export function renameAt(fd) { - console.log(`[filesystem] RENAME AT`, fd); -} - -export function symlinkAt(fd) { - console.log(`[filesystem] SYMLINK AT`, fd); -} - -export function unlinkFileAt(fd) { - console.log(`[filesystem] UNLINK FILE AT`, fd); -} - -export function changeFilePermissionsAt(fd) { - console.log(`[filesystem] CHANGE FILE PERMISSIONS AT`, fd); -} - -export function changeDirectoryPermissionsAt(fd) { - console.log(`[filesystem] CHANGE DIR PERMISSIONS AT`, fd); -} - -export function lockShared(fd) { - console.log(`[filesystem] LOCK SHARED`, fd); -} - -export function lockExclusive(fd) { - console.log(`[filesystem] LOCK EXCLUSIVE`, fd); -} - -export function tryLockShared(fd) { - console.log(`[filesystem] TRY LOCK SHARED`, fd); -} - -export function tryLockExclusive(fd) { - console.log(`[filesystem] TRY LOCK EXCLUSIVE`, fd); -} - -export function unlock(fd) { - console.log(`[filesystem] UNLOCK`, fd); -} - -export function dropDescriptor(fd) { - _removeOpenedDescriptor(fd); - closeSync(fd); -} - -export function readDirectoryEntry(stream) { - const streamValue = _dirStreams[stream]; - if (streamValue.cursor === streamValue.dirs.length) - return null; - const dir = streamValue.dirs[streamValue.cursor++]; - const type = _lookupType(dir); - _setSubdescriptorType(streamValue.fd, '/' + dir.name, type); - return { inode: null, type, name: dir.name }; -} - -export function dropDirectoryEntryStream(stream) { - _dirStreams.splice(stream, 1); -} +export { filesystem as filesystemFilesystem } diff --git a/packages/preview2-shim/lib/nodejs/io.js b/packages/preview2-shim/lib/nodejs/io.js index a1babc9e0..e008f8833 100644 --- a/packages/preview2-shim/lib/nodejs/io.js +++ b/packages/preview2-shim/lib/nodejs/io.js @@ -1,25 +1,144 @@ -export function read(s, len) { - switch (s) { - case 0: - return [process.stdin.read(len), true]; - default: - throw new Error(`TODO: write ${s}`); +import { readSync as fsReadSync } from 'node:fs'; + +function _convertFsError (e) { + switch (e.code) { + case 'EACCES': throw 'access'; + case 'EAGAIN': + case 'EWOULDBLOCK': throw 'would-block'; + case 'EALREADY': throw 'already'; + case 'EBADF': throw 'bad-descriptor'; + case 'EBUSY': throw 'busy'; + case 'EDEADLK': throw 'deadlock'; + case 'EDQUOT': throw 'quota'; + case 'EEXIST': throw 'exist'; + case 'EFBIG': throw 'file-too-large'; + case 'EILSEQ': throw 'illegal-byte-sequence'; + case 'EINPROGRESS': throw 'in-progress'; + case 'EINTR': throw 'interrupted'; + case 'EINVAL': throw 'invalid'; + case 'EIO': throw 'io'; + case 'EISDIR': throw 'is-directory'; + case 'ELOOP': throw 'loop'; + case 'EMLINK': throw 'too-many-links'; + case 'EMSGSIZE': throw 'message-size'; + case 'ENAMETOOLONG': throw 'name-too-long' + case 'ENODEV': throw 'no-device'; + case 'ENOENT': throw 'no-entry'; + case 'ENOLCK': throw 'no-lock'; + case 'ENOMEM': throw 'insufficient-memory'; + case 'ENOSPC': throw 'insufficient-space'; + case 'ENOTDIR': throw 'not-directory'; + case 'ENOTEMPTY': throw 'not-empty'; + case 'ENOTRECOVERABLE': throw 'not-recoverable'; + case 'ENOTSUP': throw 'unsupported'; + case 'ENOTTY': throw 'no-tty'; + case 'ENXIO': throw 'no-such-device'; + case 'EOVERFLOW': throw 'overflow'; + case 'EPERM': throw 'not-permitted'; + case 'EPIPE': throw 'pipe'; + case 'EROFS': throw 'read-only'; + case 'ESPIPE': throw 'invalid-seek'; + case 'ETXTBSY': throw 'text-file-busy'; + case 'EXDEV': throw 'cross-device'; + default: throw e; } } -export function write(s, buf) { - switch (s) { - case 0: - throw new Error(`TODO: write stdin`); - case 1: { - process.stdout.write(buf); - return BigInt(buf.byteLength); +export let _streams = {}; +let streamCnt = 0; +export function _createFileStream(fd, offset) { + // note we only support offset 0 + if (Number(offset) === 0) + _streams[streamCnt] = { + type: 'file', + fd: fd + }; + return streamCnt++; +} + +export const ioStreams = { + read(s, len) { + switch (s) { + case 0: + return [process.stdin.read(len), true]; + default: + throw new Error(`TODO: write ${s}`); } - case 2: { - process.stderr.write(buf); - return BigInt(buf.byteLength); + }, + blockingRead(s, len) { + len = Number(len); + const stream = _streams[s]; + if (!stream) throw null; + switch (stream.type) { + case 'file': { + const buf = Buffer.alloc(Number(len)); + try { + const readBytes = fsReadSync(stream.fd, buf, 0, Number(len)); + if (readBytes < Number(len)) + return [new Uint8Array(), true]; + return [new Uint8Array(buf.buffer, 0, readBytes), false]; + } + catch (e) { + _convertFsError(e); + } + break; + } + default: throw null; } - default: - throw new Error(`TODO: write ${s}`); + }, + skip(s, _len) { + console.log(`[streams] Skip ${s}`); + }, + blockingSkip(s, _len) { + console.log(`[streams] Blocking skip ${s}`); + }, + subscribeToInputStream(s) { + console.log(`[streams] Subscribe to input stream ${s}`); + }, + dropInputStream(s) { + delete _streams[s]; + + }, + write(s, buf) { + switch (s) { + case 0: + throw new Error(`TODO: write stdin`); + case 1: { + process.stdout.write(buf); + return BigInt(buf.byteLength); + } + case 2: { + process.stderr.write(buf); + return BigInt(buf.byteLength); + } + default: + throw new Error(`TODO: write ${s}`); + } + }, + blockingWrite(s, _buf) { + console.log(`[streams] Blocking write ${s}`); + }, + writeZeroes(s, _len) { + console.log(`[streams] Write zeroes ${s}`); + }, + blockingWriteZeroes(s, _len) { + console.log(`[streams] Blocking write zeroes ${s}`); + }, + splice(s, _src, _len) { + console.log(`[streams] Splice ${s}`); + }, + blockingSplice(s, _src, _len) { + console.log(`[streams] Blocking splice ${s}`); + }, + forward(s, _src) { + console.log(`[streams] Forward ${s}`); + }, + subscribeToOutputStream(s) { + console.log(`[streams] Subscribe to output stream ${s}`); + }, + dropOutputStream(s) { + console.log(`[streams] Drop output stream ${s}`); } -} +}; + +export { ioStreams as streams } diff --git a/packages/preview2-shim/lib/nodejs/poll.js b/packages/preview2-shim/lib/nodejs/poll.js deleted file mode 100644 index 60dc3c05c..000000000 --- a/packages/preview2-shim/lib/nodejs/poll.js +++ /dev/null @@ -1,7 +0,0 @@ -export function dropPollable(p) { - console.log(`[poll] Drop pollable ${p}`); -} - -export function pollOneoff(f) { - console.log(`[poll] Poll oneoff ${f}`); -} diff --git a/packages/preview2-shim/lib/nodejs/random.js b/packages/preview2-shim/lib/nodejs/random.js index 66aa9dddc..4a1e60c17 100644 --- a/packages/preview2-shim/lib/nodejs/random.js +++ b/packages/preview2-shim/lib/nodejs/random.js @@ -1,18 +1,23 @@ import { randomBytes } from "node:crypto"; -export function getRandomBytes(len) { - return randomBytes(Number(len)); -} +let insecureRandomValue1, insecureRandomValue2; -export function getRandomU64 () { - return new BigUint64Array(randomBytes(8).buffer)[0]; -} +export const randomRandom = { + getRandomBytes(len) { + return randomBytes(Number(len)); + }, -let insecureRandomValue1, insecureRandomValue2; -export function insecureRandom () { - if (insecureRandomValue1 === undefined) { - insecureRandomValue1 = getRandomU64(); - insecureRandomValue2 = getRandomU64(); + getRandomU64 () { + return new BigUint64Array(randomBytes(8).buffer)[0]; + }, + + insecureRandom () { + if (insecureRandomValue1 === undefined) { + insecureRandomValue1 = randomRandom.getRandomU64(); + insecureRandomValue2 = randomRandom.getRandomU64(); + } + return [insecureRandomValue1, insecureRandomValue2]; } - return [insecureRandomValue1, insecureRandomValue2]; -} +}; + +export { randomRandom as random } diff --git a/packages/preview2-shim/lib/nodejs/stderr.js b/packages/preview2-shim/lib/nodejs/stderr.js deleted file mode 100644 index 84153c2d3..000000000 --- a/packages/preview2-shim/lib/nodejs/stderr.js +++ /dev/null @@ -1,4 +0,0 @@ -// TODO: remove -export function print(message) { - process.stderr.write(message); -} diff --git a/packages/preview2-shim/lib/nodejs/streams.js b/packages/preview2-shim/lib/nodejs/streams.js deleted file mode 100644 index 312e33b11..000000000 --- a/packages/preview2-shim/lib/nodejs/streams.js +++ /dev/null @@ -1,97 +0,0 @@ -import { readSync as fsReadSync } from 'node:fs'; -import { _convertFsError } from './filesystem.js'; - -export let _streams = {}; -let streamCnt = 0; -export function _createFileStream(fd, offset) { - // note we only support offset 0 - if (Number(offset) === 0) - _streams[streamCnt] = { - type: 'file', - fd: fd - }; - return streamCnt++; -} - -export function read(s, len) { - switch (s) { - case 0: - return [process.stdin.read(len), true]; - default: - throw new Error(`TODO: write ${s}`); - } -} -export function blockingRead(s, len) { - len = Number(len); - const stream = _streams[s]; - if (!stream) throw null; - switch (stream.type) { - case 'file': { - const buf = Buffer.alloc(Number(len)); - try { - const readBytes = fsReadSync(stream.fd, buf, 0, Number(len)); - if (readBytes < Number(len)) - return [new Uint8Array(), true]; - return [new Uint8Array(buf.buffer, 0, readBytes), false]; - } - catch (e) { - _convertFsError(e); - } - break; - } - default: throw null; - } -} -export function skip(s, _len) { - console.log(`[streams] Skip ${s}`); -} -export function blockingSkip(s, _len) { - console.log(`[streams] Blocking skip ${s}`); -} -export function subscribeToInputStream(s) { - console.log(`[streams] Subscribe to input stream ${s}`); -} -export function dropInputStream(s) { - delete _streams[s]; - -} -export function write(s, buf) { - switch (s) { - case 0: - throw new Error(`TODO: write stdin`); - case 1: { - process.stdout.write(buf); - return BigInt(buf.byteLength); - } - case 2: { - process.stderr.write(buf); - return BigInt(buf.byteLength); - } - default: - throw new Error(`TODO: write ${s}`); - } -} -export function blockingWrite(s, _buf) { - console.log(`[streams] Blocking write ${s}`); -} -export function writeZeroes(s, _len) { - console.log(`[streams] Write zeroes ${s}`); -} -export function blockingWriteZeroes(s, _len) { - console.log(`[streams] Blocking write zeroes ${s}`); -} -export function splice(s, _src, _len) { - console.log(`[streams] Splice ${s}`); -} -export function blockingSplice(s, _src, _len) { - console.log(`[streams] Blocking splice ${s}`); -} -export function forward(s, _src) { - console.log(`[streams] Forward ${s}`); -} -export function subscribeToOutputStream(s) { - console.log(`[streams] Subscribe to output stream ${s}`); -} -export function dropOutputStream(s) { - console.log(`[streams] Drop output stream ${s}`); -} diff --git a/src/api.js b/src/api.js index 324b60daa..1925fe6f5 100644 --- a/src/api.js +++ b/src/api.js @@ -1,10 +1,9 @@ export { optimizeComponent as opt } from './cmd/opt.js'; export { transpileComponent as transpile } from './cmd/transpile.js'; -import { exports } from '../obj/wasm-tools.js'; -export const { parse, print, componentNew, componentWit, componentEmbed, metadataAdd, metadataShow } = exports; +export * from '../obj/wasm-tools.js'; export function preview1AdapterCommandPath () { - return new URL('../lib/wasi_preview1_component_adapter.command.wasm', import.meta.url); + return new URL('../lib/wasi_snapshot_preview1.command.wasm', import.meta.url); } export function preview1AdapterReactorPath () { - return new URL('../lib/wasi_preview1_component_adapter.reactor.wasm', import.meta.url); + return new URL('../lib/wasi_snapshot_preview1.reactor.wasm', import.meta.url); } diff --git a/src/cmd/componentize.js b/src/cmd/componentize.js index d2a056a89..54f4ba250 100644 --- a/src/cmd/componentize.js +++ b/src/cmd/componentize.js @@ -7,7 +7,7 @@ export async function componentize (jsSource, opts) { try { ({ componentize: componentizeFn } = await eval('import("@bytecodealliance/componentize-js")')); } catch (e) { - if (e?.code === 'ERR_MODULE_NOT_FOUND') + if (e?.code === 'ERR_MODULE_NOT_FOUND' && e?.message?.includes('\'@bytecodealliance/componentize-js\'')) throw new Error(`componentize-js must first be installed separately via "npm install @bytecodealliance/componentize-js".`); throw e; } diff --git a/src/cmd/opt.js b/src/cmd/opt.js index aa1d2cf3f..00f71fe4d 100644 --- a/src/cmd/opt.js +++ b/src/cmd/opt.js @@ -1,12 +1,10 @@ -import { exports } from '../../obj/wasm-tools.js'; +import { metadataShow, print } from '../../obj/wasm-tools.js'; import { writeFile } from 'fs/promises'; import { fileURLToPath } from 'url'; import c from 'chalk-template'; import { readFile, sizeStr, fixedDigitDisplay, table, spawnIOTmp, setShowSpinner, getShowSpinner } from '../common.js'; import ora from 'ora'; -const { metadataShow, print } = exports; - let WASM_OPT; try { WASM_OPT = fileURLToPath(new URL('../../node_modules/binaryen/bin/wasm-opt', import.meta.url)); diff --git a/src/cmd/transpile.js b/src/cmd/transpile.js index 7c21f0c6d..925e6c758 100644 --- a/src/cmd/transpile.js +++ b/src/cmd/transpile.js @@ -1,4 +1,4 @@ -import { exports } from '../../obj/js-component-bindgen-component.js'; +import { generate } from '../../obj/js-component-bindgen-component.js'; import { writeFile } from 'fs/promises'; import { mkdir } from 'fs/promises'; import { dirname, extname, basename } from 'path'; @@ -9,9 +9,6 @@ import { minify } from 'terser'; import { fileURLToPath } from 'url'; import ora from 'ora'; -// pending default export support -const { generate } = exports; - export async function transpile (componentPath, opts, program) { const varIdx = program.parent.rawArgs.indexOf('--'); if (varIdx !== -1) @@ -92,36 +89,7 @@ export async function transpileComponent (component, opts = {}) { if (opts.wasiShim !== false) { opts.map = Object.assign({ - // Deprecated - 'environment-preopens': '@bytecodealliance/preview2-shim/environment-preopens', - 'console': '@bytecodealliance/preview2-shim/console', - 'default-outgoing-HTTP': '@bytecodealliance/preview2-shim/default-outgoing-HTTP', - 'environment': '@bytecodealliance/preview2-shim/environment', - // Deprecated - 'environment-preopens': '@bytecodealliance/preview2-shim/environment-preopens', - 'exit': '@bytecodealliance/preview2-shim/exit', - 'filesystem': '@bytecodealliance/preview2-shim/filesystem', - // Deprecated - 'instance-monotonic-clock': '@bytecodealliance/preview2-shim/instance-monotonic-clock', - // Deprecated - 'instance-wall-clock': '@bytecodealliance/preview2-shim/instance-wall-clock', - 'instance-network': '@bytecodealliance/preview2-shim/instance-network', - 'ip-name-lookup': '@bytecodealliance/preview2-shim/ip-name-lookup', - 'monotonic-clock': '@bytecodealliance/preview2-shim/monotonic-clock', - 'network': '@bytecodealliance/preview2-shim/network', - 'poll': '@bytecodealliance/preview2-shim/poll', - 'preopens': '@bytecodealliance/preview2-shim/preopens', - 'random': '@bytecodealliance/preview2-shim/random', - // Deprecated - 'stderr': '@bytecodealliance/preview2-shim/stderr', - 'streams': '@bytecodealliance/preview2-shim/streams', - 'tcp-create-socket': '@bytecodealliance/preview2-shim/tcp-create-socket', - 'tcp': '@bytecodealliance/preview2-shim/tcp', - 'timezone': '@bytecodealliance/preview2-shim/timezone', - 'types': '@bytecodealliance/preview2-shim/types', - 'udp-create-socket': '@bytecodealliance/preview2-shim/udp-create-socket', - 'udp': '@bytecodealliance/preview2-shim/udp', - 'wall-clock': '@bytecodealliance/preview2-shim/wall-clock' + 'wasi:*': '@bytecodealliance/preview2-shim/*' }, opts.map || {}); } @@ -181,10 +149,6 @@ export async function transpileComponent (component, opts = {}) { .replace(/export var ([^ ]+) = ([^. ]+)\.([^ ]+);/g, '') .replace(/var retasmFunc = [\s\S]*$/, '') .replace(/var memasmFunc = new ArrayBuffer\(0\);/g, '') - // "imports" as the name of the imports clashes with internal asm.js naming - // so we have to do a late patchup - .replace('var imports = imports.imports;', 'var imports_ = imports.imports;') - .replace(/([^$]) = imports\["/g, '$1 = imports_["') .replace('memory.grow = __wasm_memory_grow;', '') .trim() }`).join(',\n'); diff --git a/src/cmd/wasm-tools.js b/src/cmd/wasm-tools.js index e60674b81..1392063e4 100644 --- a/src/cmd/wasm-tools.js +++ b/src/cmd/wasm-tools.js @@ -1,19 +1,9 @@ import { writeFile } from "node:fs/promises"; import { readFile } from '../common.js'; -import { exports } from "../../obj/wasm-tools.js"; +import { print as printFn, parse as parseFn, componentWit as componentWitFn, componentNew as componentNewFn, componentEmbed as componentEmbedFn, metadataAdd as metadataAddFn, metadataShow as metadataShowFn } from "../../obj/wasm-tools.js"; import { resolve, basename, extname } from 'node:path'; import c from 'chalk-template'; -const { - print: printFn, - parse: parseFn, - componentWit: componentWitFn, - componentNew: componentNewFn, - componentEmbed: componentEmbedFn, - metadataAdd: metadataAddFn, - metadataShow: metadataShowFn, -} = exports; - export async function parse(file, opts) { const source = (await readFile(file)).toString(); const output = parseFn(source); diff --git a/test/api.js b/test/api.js index 54141d15d..c8136fa37 100644 --- a/test/api.js +++ b/test/api.js @@ -10,8 +10,8 @@ export async function apiTest (fixtures) { const component = await readFile(`test/fixtures/components/${name}.component.wasm`); const { files, imports, exports } = await transpile(component, { name }); strictEqual(imports.length, 2); - strictEqual(exports.length, 2); - deepStrictEqual(exports[0], ['exports', 'instance']); + strictEqual(exports.length, 3); + deepStrictEqual(exports[0], ['flavorfulTest', 'instance']); ok(files[name + '.js']); }); @@ -27,8 +27,8 @@ export async function apiTest (fixtures) { base64Cutoff: 0, }); strictEqual(imports.length, 2); - strictEqual(exports.length, 2); - deepStrictEqual(exports[0], ['exports', 'instance']); + strictEqual(exports.length, 3); + deepStrictEqual(exports[0], ['flavorfulTest', 'instance']); ok(files[name + '.js'].length < 11_000); }); @@ -46,9 +46,10 @@ export async function apiTest (fixtures) { js: true, }); strictEqual(imports.length, 2); - strictEqual(exports.length, 2); - deepStrictEqual(exports[0], ['exports', 'instance']); - deepStrictEqual(exports[1], ['testImports', 'function']); + strictEqual(exports.length, 3); + deepStrictEqual(exports[0], ['flavorfulTest', 'instance']); + deepStrictEqual(exports[1], ['test', 'instance']); + deepStrictEqual(exports[2], ['testImports', 'function']); const source = Buffer.from(files[name + '.js']).toString(); ok(source.includes('./wasi.js')); ok(source.includes('testwasi')); @@ -73,10 +74,7 @@ export async function apiTest (fixtures) { }); test('Wit & New', async () => { - const component = await readFile(`test/fixtures/components/flavorful.component.wasm`); - const wit = await componentWit(component); - - strictEqual(wit.slice(0, 19), 'interface imports {'); + const wit = await readFile(`test/fixtures/wit/flavorful.wit`, 'utf8'); const generatedComponent = await componentEmbed({ witSource: wit, @@ -99,7 +97,7 @@ export async function apiTest (fixtures) { tag: 'component', val: 4 }); - deepStrictEqual(meta[1].producers, [['processed-by', [['wit-component', '0.9.0'], ['dummy-gen', 'test']]], ['language', [['javascript', '']]]]); + deepStrictEqual(meta[1].producers, [['processed-by', [['wit-component', '0.11.0'], ['dummy-gen', 'test']]], ['language', [['javascript', '']]]]); }); test('Multi-file WIT', async () => { @@ -124,7 +122,7 @@ export async function apiTest (fixtures) { tag: 'component', val: 1 }); - deepStrictEqual(meta[1].producers, [['processed-by', [['wit-component', '0.9.0'], ['dummy-gen', 'test']]], ['language', [['javascript', '']]]]); + deepStrictEqual(meta[1].producers, [['processed-by', [['wit-component', '0.11.0'], ['dummy-gen', 'test']]], ['language', [['javascript', '']]]]); }); test('Component new adapt', async () => { diff --git a/test/cli.js b/test/cli.js index b9c31f371..0efb3770a 100644 --- a/test/cli.js +++ b/test/cli.js @@ -24,7 +24,7 @@ export async function cliTest (fixtures) { const { stderr } = await exec(jcoPath, 'transpile', `test/fixtures/components/${name}.component.wasm`, '--no-wasi-shim', '--name', name, '-o', outDir); strictEqual(stderr, ''); const source = await readFile(`${outDir}/${name}.js`); - ok(source.toString().includes('export { exports')); + ok(source.toString().includes('export { flavorfulTest')); } finally { await cleanup(); @@ -37,7 +37,7 @@ export async function cliTest (fixtures) { const { stderr } = await exec(jcoPath, 'transpile', `test/fixtures/components/${name}.component.wasm`, '--name', name, '--valid-lifting-optimization', '--tla-compat', '--optimize', '--minify', '--base64-cutoff=0', '-o', outDir); strictEqual(stderr, ''); const source = await readFile(`${outDir}/${name}.js`); - ok(source.toString().includes('as exports,')); + ok(source.toString().includes('as flavorfulTest,')); } finally { await cleanup(); @@ -100,16 +100,10 @@ export async function cliTest (fixtures) { try { const { stderr, stdout } = await exec(jcoPath, 'wit', `test/fixtures/components/flavorful.component.wasm`); strictEqual(stderr, ''); - ok(stdout.includes('world component {')); + ok(stdout.includes('world root {')); { - const { stderr, stdout } = await exec(jcoPath, 'wit', `test/fixtures/components/flavorful.component.wasm`, '-o', outFile); - strictEqual(stderr, ''); - strictEqual(stdout, ''); - } - - { - const { stderr, stdout } = await exec(jcoPath, 'embed', '--dummy', '--wit', outFile, '-m', 'language=javascript', '-m', 'processed-by=dummy-gen@test', '-o', outFile); + const { stderr, stdout } = await exec(jcoPath, 'embed', '--dummy', '--wit', 'test/fixtures/wit/flavorful.wit', '-m', 'language=javascript', '-m', 'processed-by=dummy-gen@test', '-o', outFile); strictEqual(stderr, ''); strictEqual(stdout, ''); } @@ -135,7 +129,7 @@ export async function cliTest (fixtures) { const meta = JSON.parse(stdout); deepStrictEqual(meta[0].metaType, { tag: 'component', val: 4 }); deepStrictEqual(meta[1].producers, [ - ['processed-by', [['wit-component', '0.9.0'], ['dummy-gen', 'test']]], + ['processed-by', [['wit-component', '0.11.0'], ['dummy-gen', 'test']]], ['language', [['javascript', '']]], ]); } @@ -151,7 +145,7 @@ export async function cliTest (fixtures) { 'new', 'test/fixtures/modules/exitcode.wasm', '--adapt', - 'wasi_snapshot_preview1=lib/wasi_preview1_component_adapter.reactor.wasm', + 'wasi_snapshot_preview1=lib/wasi_snapshot_preview1.reactor.wasm', '-o', outFile); strictEqual(stderr, ''); { @@ -187,7 +181,7 @@ export async function cliTest (fixtures) { } }); - test('Componentize', async () => { + test.skip('Componentize', async () => { try { const { stdout, stderr } = await exec(jcoPath, 'componentize', diff --git a/test/codegen.js b/test/codegen.js index f5ebcbb51..a5106e245 100644 --- a/test/codegen.js +++ b/test/codegen.js @@ -43,7 +43,7 @@ export async function codegenTest (fixtures) { }); suite(`Typescript compilation`, () => { - // TypeScript tests _must_ run after codegen to complete successfully + // TypeScript tests _must_ run after codegen to complete successfully // This is due to type checking against generated bindings test('TypeScript Compilation', async () => { var { stderr } = await exec(tscPath, '-p', 'test/tsconfig.json'); diff --git a/test/fixtures/componentize/source.wit b/test/fixtures/componentize/source.wit index 362626a8f..6bfa11861 100644 --- a/test/fixtures/componentize/source.wit +++ b/test/fixtures/componentize/source.wit @@ -1,3 +1,5 @@ -default world test { +package local:test + +world test { export hello: func() -> string } \ No newline at end of file diff --git a/test/fixtures/components/dummy_proxy.component.wasm b/test/fixtures/components/dummy_proxy.component.wasm deleted file mode 100644 index 05f8a9a5adf0cac8947b2d3dd8ef589bdb905ab6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11502 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 ~Cg{PDd6i_pUVoq7)?6J7aBSraohBDk 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 VnLfym3VnzlRr;_6^9N1s`@cRKVzU4M diff --git a/test/fixtures/components/flavorful.component.wasm b/test/fixtures/components/flavorful.component.wasm index 71bc5bf8bf0fe444626d7725754aab20daf6f631..f7afa18d3509150ee7f87208fc556ffea8087086 100644 GIT binary patch delta 21397 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 delta 19218 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 JD4^No{{fAaw*&wH delta 24410 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 NO1VWCF@leo0RZ5yWnKUP delta 358 zcmbQUglWMNCRT>T;#{7Itf7&7xrup|x`{>UrManjCB+Pk3|u^n?3uX*`9(k;D+9Y4 zqZ12bK`o;LBWqG+Nh(MM3v*6>Is+pk3p<;Sx*HjVEiFuql1$Cb zjExh`Q%#c7Tp4{P&+Ac{%+o7wkY}Fpm|~f1WN2w_?#jfGmJ(lHlv$F>%noD~ zr>17JfP|8B@{3a$LMI3J>T9YR>X{qp8R+I^re~DoR4Qm#8W5s^p-Q~PIl;1XM;F)a$esF##NKe`{PlBHy`a6VuFasKooL|Fk%ED GGXntVlx3*^ diff --git a/test/fixtures/components/many_arguments.component.wasm b/test/fixtures/components/many_arguments.component.wasm deleted file mode 100644 index 3cbe400240fda1b4e85d88cb20f9cc61036b5096..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37979 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 O-n25;eFgVHgZ~TZ1aE}^ diff --git a/test/fixtures/components/numbers.component.wasm b/test/fixtures/components/numbers.component.wasm index 0152237ddb0989030d95c8a2c6efba087f7c3574..ebd2698398cc8022ca149fb09efb561e09d8b492 100644 GIT binary patch delta 12491 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 GkpBmZafWpO delta 11060 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 delta 10752 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 diff --git a/test/fixtures/components/smoke.component.wasm b/test/fixtures/components/smoke.component.wasm index 268d8539fffcc1cb69ce1c24a151ecbce7cde8c7..c41358244b861b617764e96b23f3bde35c3562ee 100644 GIT binary patch delta 602 zcmbQy%yg}psh%ORIG2}!k%3)^(TR!C0mx!uU@gfg&C6zF;1XwK6e>wAF0m@k&CgEN z&&(~zFDfZ!WnfohbYfvFsAY6uWKF6BssL+a&dE<_WMpAr2hzH!MMaE^T)d1793TVA z6N@uh85vi9VFcO7$iTqB7|ff%Twg!2#Gii!BP+5k8;^g?XD~EQYB*XmeKmusnEg;3h5Cb9^A-cFHs}w3(J_bp1s)59rCQ3vg^F%;; z7&%Wv<*B3f4uAF?JP=oRC=9h&_nHV2W?k}0c4f01kI3VITYnR$FsxD?@ z$xF;lW&FZw#Gn=*5A9WL!77s&a}Xn}MF8p`HP6 zd1i@jQf6LCdTO3-GOK}}`DC>!JH~~ZQ>#ue3M8ADnx_J>g;|QZagw3YWZN1$0Yh`s zWK%OE%al};WRo;A!^zz>dW^o4ch%@C$QtUI8|WD*XjmE~o0=F}7?>#-85kMs8kp)D z8)`CMoh(?pg;8&ET%$TW)c(nzYrimVpZuaO8%?~iK7lc1@}qh)9^?qxEZN|~2vI2l NHG!L(5teG0834?hqeuV% delta 486 zcmccC%rv8!DTN`iIG2Zkk%3)^(TR!C0mx!uEy*a&%LcNzco^9;a|`l|N{Sg+8Q9er zomdzPY8f3ES(7SDQj0-qnRD{f85kK^*nzZeYEco8&Be>e0hBE%Pb|)4U}a?d_jzJ| zJj+~0R`!i|KjyP%FfuU6GCZApwZOzM1jy!KXV+q2U{C^L_P5MHl41TDAi>DOa0^JW zFvNgJMu;wk$yS9*j5j977D_XopIiwfk4>Ih=)gE{^0h(@#vPmg7cON2N-r&$v)QNA zj!~+YjU_KJHI~S7pbza`W7(6O6)^7N$l?rsihG#);;sCdp~8j6Rd~YE&lA zsSy`QGf6YHNKH*lu}n5Hv@|!Le7HuBF?8~$8hs5_Lp^f?JpR;l~KxNWjzeKxA_=K2K2$jKrN ZW{m8c!x~%|A+o{{P2Aj!uoz@!001prh>idN diff --git a/test/fixtures/components/strings.component.wasm b/test/fixtures/components/strings.component.wasm index 87fee853b5904bd89c396a453a933335b38b0b1b..78975ccf95ba3cd012f6523b9a69000c9cf72ebf 100644 GIT binary patch delta 934 zcmaiz&rcIk5XawayDU^&2>1h%BA^1rxb1F#QjFLbAu+)am2gqBw%dhN>9*NjLV(h0 zVqzl(ZAMy6^q}4`X1Ng#dPNUQ~6mnC1;WffTIK8kfG?t zkZx!xbz1kO(%Fn==q%y-EL?Vp64}H&jiC~DVw=fK1K0^izr3Pp0Id!ukVcC!N3~dh z_1C3)w*qUx?hD>k7TF_ha&UEl8@3VBNxoowF~~4BtSo+Hj@WQ2HwL3<=7eS+{SU1i zLx__pkyH31_r_h#$x+`H5b}-rC&?L=LG`D*0?!>j>*!o&FcalWHL@QR{WaL%O+K9N$B(~8Q=tl*6xmWE-= z{9L*VZE`RaQK$`ulaW9|^pC(8y744Fz@sfXU(r&IJs z1h3HTi3)NkC=LqYZol9U@Ir_Why$=<$FA}KUU^ahFRqmPn`*U?=h(689{B?t@z9D= znj#y#b&9>a%J16r)B8u_&DBlVz+>f1RfDT(;6`}{Cec&rbJcIFIbK-=7ddF@@wI{A m*QXb$t9FN!bk>+h-?PywvAsq-&bTIvt?-uZPg$_6iG}x7*A-Lek{03x6SSn!Z&)V zA%v7yKunCm#K^^vxSWg{^Kfq)-)s`H(-gub#&t=fO4c@#8K#jePj^95Ra4~x2leV>lukGfVEax!f4Q~*+%9a- z?Y!ZKa}#`{jgSWN2y>Gm<{N`gCSNnZ8C;k;15tcGC6-rFKUDAxAuc9QJXkxuS#N0) z)V2+TJfq7Tt#mOwewl7JK~*ys!He3=!8$J`F#8f_i1Qaq$On30e-QiaUfi6SfKPa6 z_7L6QFTVgwee49I7gcwf`@! zvf8Lw>|@|QUdR{rbD}@u54gr;T}a4EYFJW)B(-&6$x=7GF-|XC1y3~68Ax=7yMn=Z zSPCVF@Evx0hC)NZh$O{R(PW?_8V+Mi!3*cHztG;+)Zq_{eo;{5;Y<3+xUV%TipgMS zNDTS{Vjw7pbVl0Xrwun&+VE*%6$EtMY;jn*kZYJ*u0Q-0cyfSdB&$+V7mCji{`DMq z|2pye@?&_2_f|3%$Zmn~8hM1;2?hhShnh- diff --git a/test/fixtures/components/variants.component.wasm b/test/fixtures/components/variants.component.wasm index a7263ba8f3698aeb7877ffbf62d3528364133bcb..a2099559970cc5608e44bce67a3d00c280af8f7d 100644 GIT binary patch delta 20083 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 D`J(qP delta 18592 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 diff --git a/test/fixtures/wit/flavorful.wit b/test/fixtures/wit/flavorful.wit new file mode 100644 index 000000000..5d601a6ed --- /dev/null +++ b/test/fixtures/wit/flavorful.wit @@ -0,0 +1,43 @@ +package test:flavorful + +interface test { + record list-in-record1 { a: string } + record list-in-record2 { a: string } + record list-in-record3 { a: string } + record list-in-record4 { a: string } + type list-in-alias = list-in-record4 + + f-list-in-record1: func(a: list-in-record1) + f-list-in-record2: func() -> list-in-record2 + f-list-in-record3: func(a: list-in-record3) -> list-in-record3 + f-list-in-record4: func(a: list-in-alias) -> list-in-alias + + type list-in-variant1-v1 = option + type list-in-variant1-v2 = result<_, string> + union list-in-variant1-v3 { string, float32 } + f-list-in-variant1: func(a: list-in-variant1-v1, b: list-in-variant1-v2, c: list-in-variant1-v3) + + type list-in-variant2 = option + f-list-in-variant2: func() -> list-in-variant2 + + type list-in-variant3 = option + f-list-in-variant3: func(a: list-in-variant3) -> list-in-variant3 + + enum my-errno { success, a, b } + errno-result: func() -> result<_, my-errno> + + type list-typedef = string + type list-typedef2 = list + type list-typedef3 = list + list-typedefs: func(a: list-typedef, c: list-typedef3) -> (a: list-typedef2, b: list-typedef3) + + list-of-variants: func(a: list, b: list, c: list) + -> (a: list, b: list, c: list) +} + +world flavorful { + import test + export test + + export test-imports: func() +} diff --git a/test/runtime/dummy_proxy.ts b/test/runtime/dummy_proxy.ts index 51c14e5c5..b44df741b 100644 --- a/test/runtime/dummy_proxy.ts +++ b/test/runtime/dummy_proxy.ts @@ -2,6 +2,8 @@ import * as assert from "node:assert"; import { importObject } from "@bytecodealliance/preview2-shim"; + +// @ts-ignore import { instantiate } from "../output/dummy_proxy/dummy_proxy.js"; import * as helpers from "./helpers.js"; diff --git a/test/runtime/flavorful.ts b/test/runtime/flavorful.ts index a8d633f06..538fab21f 100644 --- a/test/runtime/flavorful.ts +++ b/test/runtime/flavorful.ts @@ -1,76 +1,78 @@ -// Flags: --map testwasi=../helpers.js --map imports=../flavorful.js --js +// Flags: --map testwasi=../helpers.js --map test:flavorful=../flavorful.js --js // @ts-nocheck import * as assert from 'assert'; // Imports -export function fListInRecord1(x: any) {} -export function fListInRecord2() { return { a: 'list_in_record2' }; } -export function fListInRecord3(x: any) { - assert.strictEqual(x.a, 'list_in_record3 input'); - return { a: 'list_in_record3 output' }; -} -export function fListInRecord4(x: any) { - assert.strictEqual(x.a, 'input4'); - return { a: 'result4' }; -} -export function fListInVariant1(a: any, b: any, c: any) { - assert.strictEqual(a, 'foo'); - assert.deepStrictEqual(b, { tag: 'err', val: 'bar' }); - assert.deepStrictEqual(c, { tag: 0, val: 'baz' }); -} -export function fListInVariant2() { return 'list_in_variant2'; } -export function fListInVariant3(x: any) { - assert.strictEqual(x, 'input3'); - return 'output3'; -} let firstErr = true; -export function errnoResult() { - if (firstErr) { - firstErr = false; - throw 'b'; +export const flavorfulTest = { + fListInRecord1(x: any) {}, + fListInRecord2() { return { a: 'list_in_record2' }; }, + fListInRecord3(x: any) { + assert.strictEqual(x.a, 'list_in_record3 input'); + return { a: 'list_in_record3 output' }; + }, + fListInRecord4(x: any) { + assert.strictEqual(x.a, 'input4'); + return { a: 'result4' }; + }, + fListInVariant1(a: any, b: any, c: any) { + assert.strictEqual(a, 'foo'); + assert.deepStrictEqual(b, { tag: 'err', val: 'bar' }); + assert.deepStrictEqual(c, { tag: 0, val: 'baz' }); + }, + fListInVariant2() { return 'list_in_variant2'; }, + fListInVariant3(x: any) { + assert.strictEqual(x, 'input3'); + return 'output3'; + }, + errnoResult() { + if (firstErr) { + firstErr = false; + throw 'b'; + } + }, + listTypedefs(x: any, y: any) { + assert.strictEqual(x, 'typedef1'); + assert.deepStrictEqual(y, ['typedef2']); + return [(new TextEncoder).encode('typedef3'), ['typedef4']]; + }, + listOfVariants(bools: any, results: any, enums: any) { + assert.deepStrictEqual(bools, [true, false]); + assert.deepStrictEqual(results, [{ tag: 'ok', val: undefined }, { tag: 'err', val: undefined }]); + assert.deepStrictEqual(enums, ["success", "a"]); + return [ + [false, true], + [{ tag: 'err', val: undefined }, { tag: 'ok', val: undefined }], + ["a", "b"], + ]; } -} -export function listTypedefs(x: any, y: any) { - assert.strictEqual(x, 'typedef1'); - assert.deepStrictEqual(y, ['typedef2']); - return [(new TextEncoder).encode('typedef3'), ['typedef4']]; -} -export function listOfVariants(bools: any, results: any, enums: any) { - assert.deepStrictEqual(bools, [true, false]); - assert.deepStrictEqual(results, [{ tag: 'ok', val: undefined }, { tag: 'err', val: undefined }]); - assert.deepStrictEqual(enums, ["success", "a"]); - return [ - [false, true], - [{ tag: 'err', val: undefined }, { tag: 'ok', val: undefined }], - ["a", "b"], - ]; -} +}; export async function run () { const wasm = await import('../output/flavorful/flavorful.js'); wasm.testImports(); - wasm.exports.fListInRecord1({ a: "list_in_record1" }); - assert.deepStrictEqual(wasm.exports.fListInRecord2(), { a: "list_in_record2" }); + wasm.test.fListInRecord1({ a: "list_in_record1" }); + assert.deepStrictEqual(wasm.test.fListInRecord2(), { a: "list_in_record2" }); assert.deepStrictEqual( - wasm.exports.fListInRecord3({ a: "list_in_record3 input" }), + wasm.test.fListInRecord3({ a: "list_in_record3 input" }), { a: "list_in_record3 output" }, ); assert.deepStrictEqual( - wasm.exports.fListInRecord4({ a: "input4" }), + wasm.test.fListInRecord4({ a: "input4" }), { a: "result4" }, ); - wasm.exports.fListInVariant1("foo", { tag: 'err', val: 'bar' }, { tag: 0, val: 'baz' }); + wasm.test.fListInVariant1("foo", { tag: 'err', val: 'bar' }, { tag: 0, val: 'baz' }); - assert.deepStrictEqual(wasm.exports.fListInVariant2(), "list_in_variant2"); - assert.deepStrictEqual(wasm.exports.fListInVariant3("input3"), "output3"); + assert.deepStrictEqual(wasm.test.fListInVariant2(), "list_in_variant2"); + assert.deepStrictEqual(wasm.test.fListInVariant3("input3"), "output3"); try { - wasm.exports.errnoResult(); + wasm.test.errnoResult(); assert.ok(false); } catch (e: any) { @@ -79,7 +81,7 @@ export async function run () { assert.strictEqual(e.payload, 'b'); } - const [r1, r2] = wasm.exports.listTypedefs("typedef1", ["typedef2"]); + const [r1, r2] = wasm.test.listTypedefs("typedef1", ["typedef2"]); assert.deepStrictEqual(r1, (new TextEncoder()).encode('typedef3')); assert.deepStrictEqual(r2, ['typedef4']); } diff --git a/test/runtime/lists.ts b/test/runtime/lists.ts index 7820bac3c..ade4a44e6 100644 --- a/test/runtime/lists.ts +++ b/test/runtime/lists.ts @@ -9,122 +9,125 @@ import * as assert from 'assert'; async function run() { const wasm = await instantiate(helpers.loadWasm, { testwasi: helpers, - imports: { - emptyListParam(a) { - assert.deepStrictEqual(Array.from(a), []); - }, - emptyStringParam(a) { - assert.strictEqual(a, ''); - }, - emptyListResult() { - return new Uint8Array([]); - }, - emptyStringResult() { return ''; }, - listParam(a) { - assert.deepStrictEqual(Array.from(a), [1, 2, 3, 4]); - }, - listParam2(a) { - assert.strictEqual(a, 'foo'); - }, - listParam3(a) { - assert.deepStrictEqual(a, ['foo', 'bar', 'baz']); - }, - listParam4(a) { - assert.deepStrictEqual(a, [['foo', 'bar'], ['baz']]); - }, - listResult() { - return new Uint8Array([1, 2, 3, 4, 5]); - }, - listResult2() { return 'hello!'; }, - listResult3() { return ['hello,', 'world!']; }, - listRoundtrip(x) { return x; }, - stringRoundtrip(x) { return x; }, - - listMinmax8(u, s) { - assert.deepEqual(u.length, 2); - assert.deepEqual(u[0], 0); - assert.deepEqual(u[1], (1 << 8) - 1); - assert.deepEqual(s.length, 2); - assert.deepEqual(s[0], -(1 << 7)); - assert.deepEqual(s[1], (1 << 7) - 1); - - return [u, s]; - }, - - listMinmax16(u, s) { - assert.deepEqual(u.length, 2); - assert.deepEqual(u[0], 0); - assert.deepEqual(u[1], (1 << 16) - 1); - assert.deepEqual(s.length, 2); - assert.deepEqual(s[0], -(1 << 15)); - assert.deepEqual(s[1], (1 << 15) - 1); - - return [u, s]; - }, - - listMinmax32(u, s) { - assert.deepEqual(u.length, 2); - assert.deepEqual(u[0], 0); - assert.deepEqual(u[1], ~0 >>> 0); - assert.deepEqual(s.length, 2); - assert.deepEqual(s[0], 1 << 31); - assert.deepEqual(s[1], ((1 << 31) - 1) >>> 0); - - return [u, s]; - }, - - listMinmax64(u, s) { - assert.deepEqual(u.length, 2); - assert.deepEqual(u[0], 0n); - assert.deepEqual(u[1], (2n ** 64n) - 1n); - assert.deepEqual(s.length, 2); - assert.deepEqual(s[0], -(2n ** 63n)); - assert.deepEqual(s[1], (2n ** 63n) - 1n); - - return [u, s]; - }, - - listMinmaxFloat(f, d) { - assert.deepEqual(f.length, 4); - assert.deepEqual(f[0], -3.4028234663852886e+38); - assert.deepEqual(f[1], 3.4028234663852886e+38); - assert.deepEqual(f[2], Number.NEGATIVE_INFINITY); - assert.deepEqual(f[3], Number.POSITIVE_INFINITY); - - assert.deepEqual(d.length, 4); - assert.deepEqual(d[0], -Number.MAX_VALUE); - assert.deepEqual(d[1], Number.MAX_VALUE); - assert.deepEqual(d[2], Number.NEGATIVE_INFINITY); - assert.deepEqual(d[3], Number.POSITIVE_INFINITY); - - return [f, d]; - }, - }, + lists: { + listsTest: { + emptyListParam(a) { + assert.deepStrictEqual(Array.from(a), []); + }, + emptyStringParam(a) { + assert.strictEqual(a, ''); + }, + emptyListResult() { + return new Uint8Array([]); + }, + emptyStringResult() { return ''; }, + listParam(a) { + assert.deepStrictEqual(Array.from(a), [1, 2, 3, 4]); + }, + listParam2(a) { + assert.strictEqual(a, 'foo'); + }, + listParam3(a) { + assert.deepStrictEqual(a, ['foo', 'bar', 'baz']); + }, + listParam4(a) { + assert.deepStrictEqual(a, [['foo', 'bar'], ['baz']]); + }, + listResult() { + return new Uint8Array([1, 2, 3, 4, 5]); + }, + listResult2() { return 'hello!'; }, + listResult3() { return ['hello,', 'world!']; }, + listRoundtrip(x) { return x; }, + stringRoundtrip(x) { return x; }, + + listMinmax8(u, s) { + assert.deepEqual(u.length, 2); + assert.deepEqual(u[0], 0); + assert.deepEqual(u[1], (1 << 8) - 1); + assert.deepEqual(s.length, 2); + assert.deepEqual(s[0], -(1 << 7)); + assert.deepEqual(s[1], (1 << 7) - 1); + + return [u, s]; + }, + + listMinmax16(u, s) { + assert.deepEqual(u.length, 2); + assert.deepEqual(u[0], 0); + assert.deepEqual(u[1], (1 << 16) - 1); + assert.deepEqual(s.length, 2); + assert.deepEqual(s[0], -(1 << 15)); + assert.deepEqual(s[1], (1 << 15) - 1); + + return [u, s]; + }, + + listMinmax32(u, s) { + assert.deepEqual(u.length, 2); + assert.deepEqual(u[0], 0); + assert.deepEqual(u[1], ~0 >>> 0); + assert.deepEqual(s.length, 2); + assert.deepEqual(s[0], 1 << 31); + assert.deepEqual(s[1], ((1 << 31) - 1) >>> 0); + + return [u, s]; + }, + + listMinmax64(u, s) { + assert.deepEqual(u.length, 2); + assert.deepEqual(u[0], 0n); + assert.deepEqual(u[1], (2n ** 64n) - 1n); + assert.deepEqual(s.length, 2); + assert.deepEqual(s[0], -(2n ** 63n)); + assert.deepEqual(s[1], (2n ** 63n) - 1n); + + return [u, s]; + }, + + listMinmaxFloat(f, d) { + assert.deepEqual(f.length, 4); + assert.deepEqual(f[0], -3.4028234663852886e+38); + assert.deepEqual(f[1], 3.4028234663852886e+38); + assert.deepEqual(f[2], Number.NEGATIVE_INFINITY); + assert.deepEqual(f[3], Number.POSITIVE_INFINITY); + + assert.deepEqual(d.length, 4); + assert.deepEqual(d[0], -Number.MAX_VALUE); + assert.deepEqual(d[1], Number.MAX_VALUE); + assert.deepEqual(d[2], Number.NEGATIVE_INFINITY); + assert.deepEqual(d[3], Number.POSITIVE_INFINITY); + + return [f, d]; + } + } + } }); const bytes = wasm.allocatedBytes(); + assert.strictEqual(wasm.test, wasm.listsTest); wasm.testImports(); - wasm.exports.emptyListParam(new Uint8Array([])); - wasm.exports.emptyStringParam(''); - wasm.exports.listParam(new Uint8Array([1, 2, 3, 4]).buffer); - wasm.exports.listParam2("foo"); - wasm.exports.listParam3(["foo", "bar", "baz"]); - wasm.exports.listParam4([["foo", "bar"], ["baz"]]); - assert.deepStrictEqual(Array.from(wasm.exports.emptyListResult()), []); - assert.deepStrictEqual(wasm.exports.emptyStringResult(), ""); - assert.deepStrictEqual(Array.from(wasm.exports.listResult()), [1, 2, 3, 4, 5]); - assert.deepStrictEqual(wasm.exports.listResult2(), "hello!"); - assert.deepStrictEqual(wasm.exports.listResult3(), ["hello,", "world!"]); + wasm.test.emptyListParam(new Uint8Array([])); + wasm.test.emptyStringParam(''); + wasm.test.listParam(new Uint8Array([1, 2, 3, 4]).buffer); + wasm.test.listParam2("foo"); + wasm.test.listParam3(["foo", "bar", "baz"]); + wasm.test.listParam4([["foo", "bar"], ["baz"]]); + assert.deepStrictEqual(Array.from(wasm.test.emptyListResult()), []); + assert.deepStrictEqual(wasm.test.emptyStringResult(), ""); + assert.deepStrictEqual(Array.from(wasm.test.listResult()), [1, 2, 3, 4, 5]); + assert.deepStrictEqual(wasm.test.listResult2(), "hello!"); + assert.deepStrictEqual(wasm.test.listResult3(), ["hello,", "world!"]); const buffer = new ArrayBuffer(8); (new Uint8Array(buffer)).set(new Uint8Array([1, 2, 3, 4]), 2); // Create a view of the four bytes in the middle of the buffer const view = new Uint8Array(buffer, 2, 4); - assert.deepStrictEqual(Array.from(wasm.exports.listRoundtrip(view)), [1, 2, 3, 4]); + assert.deepStrictEqual(Array.from(wasm.test.listRoundtrip(view)), [1, 2, 3, 4]); - assert.deepStrictEqual(wasm.exports.stringRoundtrip("x"), "x"); - assert.deepStrictEqual(wasm.exports.stringRoundtrip(""), ""); - assert.deepStrictEqual(wasm.exports.stringRoundtrip("hello ⚑ world"), "hello ⚑ world"); + assert.deepStrictEqual(wasm.test.stringRoundtrip("x"), "x"); + assert.deepStrictEqual(wasm.test.stringRoundtrip(""), ""); + assert.deepStrictEqual(wasm.test.stringRoundtrip("hello ⚑ world"), "hello ⚑ world"); // Ensure that we properly called `free` everywhere in all the glue that we // needed to. diff --git a/test/runtime/many_arguments.ts b/test/runtime/many-arguments.ts similarity index 95% rename from test/runtime/many_arguments.ts rename to test/runtime/many-arguments.ts index c526450d0..a7f0d4c47 100644 --- a/test/runtime/many_arguments.ts +++ b/test/runtime/many-arguments.ts @@ -1,6 +1,6 @@ // Flags: --instantiation -import { instantiate } from "../output/many_arguments/many_arguments.js"; +import { instantiate } from "../output/many-arguments/many-arguments.js"; import * as helpers from "./helpers.js"; function assertEq(x: any, y: any) { diff --git a/test/runtime/numbers.ts b/test/runtime/numbers.ts index 25b95b1fb..7544811a2 100644 --- a/test/runtime/numbers.ts +++ b/test/runtime/numbers.ts @@ -2,12 +2,7 @@ import * as helpers from "./helpers.js"; import { instantiate } from "../output/numbers/numbers.js"; - -function assertEq(x: any, y: any) { - if (x !== y) - throw new Error(`${x} != ${y}`); -} - +import { strictEqual } from 'node:assert'; function assert(x: boolean) { if (!x) throw new Error("assert failed"); @@ -17,71 +12,75 @@ async function run() { let scalar = 0; const wasm = await instantiate(helpers.loadWasm, { testwasi: helpers, - imports: { - roundtripU8(x) { return x; }, - roundtripS8(x) { return x; }, - roundtripU16(x) { return x; }, - roundtripS16(x) { return x; }, - roundtripU32(x) { return x; }, - roundtripS32(x) { return x; }, - roundtripU64(x) { return x; }, - roundtripS64(x) { return x; }, - roundtripFloat32(x) { return x; }, - roundtripFloat64(x) { return x; }, - roundtripChar(x) { return x; }, - setScalar(x) { scalar = x; }, - getScalar() { return scalar; }, - }, + numbers: { + numbersTest: { + roundtripU8(x) { return x; }, + roundtripS8(x) { return x; }, + roundtripU16(x) { return x; }, + roundtripS16(x) { return x; }, + roundtripU32(x) { return x; }, + roundtripS32(x) { return x; }, + roundtripU64(x) { return x; }, + roundtripS64(x) { return x; }, + roundtripFloat32(x) { return x; }, + roundtripFloat64(x) { return x; }, + roundtripChar(x) { return x; }, + setScalar(x) { scalar = x; }, + getScalar() { return scalar; }, + } + } }); wasm.testImports(); - assertEq(wasm.exports.roundtripU8(1), 1); - assertEq(wasm.exports.roundtripU8((1 << 8) - 1), (1 << 8) - 1); + strictEqual(wasm.numbersTest, wasm.test); + + strictEqual(wasm.test.roundtripU8(1), 1); + strictEqual(wasm.test.roundtripU8((1 << 8) - 1), (1 << 8) - 1); - assertEq(wasm.exports.roundtripS8(1), 1); - assertEq(wasm.exports.roundtripS8((1 << 7) - 1), (1 << 7) - 1); - assertEq(wasm.exports.roundtripS8(-(1 << 7)), -(1 << 7)); + strictEqual(wasm.test.roundtripS8(1), 1); + strictEqual(wasm.test.roundtripS8((1 << 7) - 1), (1 << 7) - 1); + strictEqual(wasm.test.roundtripS8(-(1 << 7)), -(1 << 7)); - assertEq(wasm.exports.roundtripU16(1), 1); - assertEq(wasm.exports.roundtripU16((1 << 16) - 1), (1 << 16) - 1); + strictEqual(wasm.test.roundtripU16(1), 1); + strictEqual(wasm.test.roundtripU16((1 << 16) - 1), (1 << 16) - 1); - assertEq(wasm.exports.roundtripS16(1), 1); - assertEq(wasm.exports.roundtripS16((1 << 15) - 1), (1 << 15) - 1); - assertEq(wasm.exports.roundtripS16(-(1 << 15)), -(1 << 15)); + strictEqual(wasm.test.roundtripS16(1), 1); + strictEqual(wasm.test.roundtripS16((1 << 15) - 1), (1 << 15) - 1); + strictEqual(wasm.test.roundtripS16(-(1 << 15)), -(1 << 15)); - assertEq(wasm.exports.roundtripU32(1), 1); - assertEq(wasm.exports.roundtripU32(~0 >>> 0), ~0 >>> 0); + strictEqual(wasm.test.roundtripU32(1), 1); + strictEqual(wasm.test.roundtripU32(~0 >>> 0), ~0 >>> 0); - assertEq(wasm.exports.roundtripS32(1), 1); - assertEq(wasm.exports.roundtripS32(((1 << 31) - 1) >>> 0), ((1 << 31) - 1) >>> 0); - assertEq(wasm.exports.roundtripS32(1 << 31), 1 << 31); + strictEqual(wasm.test.roundtripS32(1), 1); + strictEqual(wasm.test.roundtripS32(((1 << 31) - 1) >>> 0), ((1 << 31) - 1) >>> 0); + strictEqual(wasm.test.roundtripS32(1 << 31), 1 << 31); - assertEq(wasm.exports.roundtripU64(1n), 1n); - assertEq(wasm.exports.roundtripU64((1n << 64n) - 1n), (1n << 64n) - 1n); + strictEqual(wasm.test.roundtripU64(1n), 1n); + strictEqual(wasm.test.roundtripU64((1n << 64n) - 1n), (1n << 64n) - 1n); - assertEq(wasm.exports.roundtripS64(1n), 1n); - assertEq(wasm.exports.roundtripS64((1n << 63n) - 1n), (1n << 63n) - 1n); - assertEq(wasm.exports.roundtripS64(-(1n << 63n)), -(1n << 63n)); + strictEqual(wasm.test.roundtripS64(1n), 1n); + strictEqual(wasm.test.roundtripS64((1n << 63n) - 1n), (1n << 63n) - 1n); + strictEqual(wasm.test.roundtripS64(-(1n << 63n)), -(1n << 63n)); - assertEq(wasm.exports.roundtripFloat32(1), 1); - assertEq(wasm.exports.roundtripFloat32(Infinity), Infinity); - assertEq(wasm.exports.roundtripFloat32(-Infinity), -Infinity); - assert(Number.isNaN(wasm.exports.roundtripFloat32(NaN))); + strictEqual(wasm.test.roundtripFloat32(1), 1); + strictEqual(wasm.test.roundtripFloat32(Infinity), Infinity); + strictEqual(wasm.test.roundtripFloat32(-Infinity), -Infinity); + assert(Number.isNaN(wasm.test.roundtripFloat32(NaN))); - assertEq(wasm.exports.roundtripFloat64(1), 1); - assertEq(wasm.exports.roundtripFloat64(Infinity), Infinity); - assertEq(wasm.exports.roundtripFloat64(-Infinity), -Infinity); - assert(Number.isNaN(wasm.exports.roundtripFloat64(NaN))); + strictEqual(wasm.test.roundtripFloat64(1), 1); + strictEqual(wasm.test.roundtripFloat64(Infinity), Infinity); + strictEqual(wasm.test.roundtripFloat64(-Infinity), -Infinity); + assert(Number.isNaN(wasm.test.roundtripFloat64(NaN))); - assertEq(wasm.exports.roundtripChar('a'), 'a'); - assertEq(wasm.exports.roundtripChar(' '), ' '); - assertEq(wasm.exports.roundtripChar('🚩'), '🚩'); + strictEqual(wasm.test.roundtripChar('a'), 'a'); + strictEqual(wasm.test.roundtripChar(' '), ' '); + strictEqual(wasm.test.roundtripChar('🚩'), '🚩'); - wasm.exports.setScalar(2); - assertEq(wasm.exports.getScalar(), 2); - wasm.exports.setScalar(4); - assertEq(wasm.exports.getScalar(), 4); + wasm.test.setScalar(2); + strictEqual(wasm.test.getScalar(), 2); + wasm.test.setScalar(4); + strictEqual(wasm.test.getScalar(), 4); } await run() diff --git a/test/runtime/records.ts b/test/runtime/records.ts index 393c3d7ce..638cbec5c 100644 --- a/test/runtime/records.ts +++ b/test/runtime/records.ts @@ -8,44 +8,47 @@ import * as assert from 'node:assert'; async function run() { const wasm = await instantiate(helpers.loadWasm, { testwasi: helpers, - imports: { - multipleResults() { return [4, 5]; }, - swapTuple([a, b]) { return [b, a]; }, - roundtripFlags1(x) { return x; }, - roundtripFlags2(x) { return x; }, - roundtripFlags3(r0, r1, r2, r3) { return [r0, r1, r2, r3]; }, - roundtripRecord1(x) { return x; }, - tuple0([]) { return []; }, - tuple1([x]) { return [x]; }, - }, + records: { + recordsTest: { + multipleResults() { return [4, 5]; }, + swapTuple([a, b]) { return [b, a]; }, + roundtripFlags1(x) { return x; }, + roundtripFlags2(x) { return x; }, + roundtripFlags3(r0, r1, r2, r3) { return [r0, r1, r2, r3]; }, + roundtripRecord1(x) { return x; }, + tuple0([]) { return []; }, + tuple1([x]) { return [x]; }, + } + } }); wasm.testImports(); - assert.deepEqual(wasm.exports.multipleResults(), [100, 200]); - assert.deepStrictEqual(wasm.exports.swapTuple([1, 2]), [2, 1]); - assert.deepEqual(wasm.exports.roundtripFlags1({ a: true }), { a: true, b: false }); - assert.deepEqual(wasm.exports.roundtripFlags1({}), { a: false, b: false }); - assert.deepEqual(wasm.exports.roundtripFlags1({ a: true, b: true }), { a: true, b: true }); - - assert.deepEqual(wasm.exports.roundtripFlags2({ c: true }), { c: true, d: false, e: false }); - assert.deepEqual(wasm.exports.roundtripFlags2({}), { c: false, d: false, e: false }); - assert.deepEqual(wasm.exports.roundtripFlags2({ d: true }), { c: false, d: true, e: false }); - assert.deepEqual(wasm.exports.roundtripFlags2({ c: true, e: true }), { c: true, d: false, e: true }); + assert.deepStrictEqual(wasm.test, wasm.recordsTest); + assert.deepEqual(wasm.test.multipleResults(), [100, 200]); + assert.deepStrictEqual(wasm.test.swapTuple([1, 2]), [2, 1]); + assert.deepEqual(wasm.test.roundtripFlags1({ a: true }), { a: true, b: false }); + assert.deepEqual(wasm.test.roundtripFlags1({}), { a: false, b: false }); + assert.deepEqual(wasm.test.roundtripFlags1({ a: true, b: true }), { a: true, b: true }); + + assert.deepEqual(wasm.test.roundtripFlags2({ c: true }), { c: true, d: false, e: false }); + assert.deepEqual(wasm.test.roundtripFlags2({}), { c: false, d: false, e: false }); + assert.deepEqual(wasm.test.roundtripFlags2({ d: true }), { c: false, d: true, e: false }); + assert.deepEqual(wasm.test.roundtripFlags2({ c: true, e: true }), { c: true, d: false, e: true }); { - const { a, b } = wasm.exports.roundtripRecord1({ a: 8, b: {} }); + const { a, b } = wasm.test.roundtripRecord1({ a: 8, b: {} }); assert.deepEqual(a, 8); assert.deepEqual(b, { a: false, b: false }); } { - const { a, b } = wasm.exports.roundtripRecord1({ a: 0, b: { a: true, b: true } }); + const { a, b } = wasm.test.roundtripRecord1({ a: 0, b: { a: true, b: true } }); assert.deepEqual(a, 0); assert.deepEqual(b, { a: true, b: true }); } - assert.deepStrictEqual(wasm.exports.tuple0([]), []); - assert.deepStrictEqual(wasm.exports.tuple1([1]), [1]); + assert.deepStrictEqual(wasm.test.tuple0([]), []); + assert.deepStrictEqual(wasm.test.tuple1([1]), [1]); } await run() diff --git a/test/runtime/smoke.ts b/test/runtime/smoke.ts index 42580bdb2..9e1c0677b 100644 --- a/test/runtime/smoke.ts +++ b/test/runtime/smoke.ts @@ -1,4 +1,4 @@ -// Flags: --tla-compat --map testwasi=../helpers.js --map imports=../smoke.js --base64-cutoff=2500 +// Flags: --tla-compat --map testwasi=../helpers.js --map test:smoke=../smoke.js --base64-cutoff=2500 function assert(x: boolean, msg: string) { if (!x) throw new Error(msg); @@ -6,9 +6,11 @@ function assert(x: boolean, msg: string) { let hit = false; -export function thunk () { - hit = true; -} +export const smokeImports = { + thunk () { + hit = true; + } +}; async function run() { const wasm = await import('../output/smoke/smoke.js'); diff --git a/test/runtime/strings.ts b/test/runtime/strings.ts index 2c7d8866a..229d374c5 100644 --- a/test/runtime/strings.ts +++ b/test/runtime/strings.ts @@ -9,12 +9,14 @@ import * as assert from 'assert'; async function run() { const wasm = await instantiate(helpers.loadWasm, { testwasi: helpers, - imports: { - takeBasic(s: string) { - assert.strictEqual(s, 'latin utf16'); - }, - returnUnicode() { - return '🚀🚀🚀 𠈄𓀀'; + strings: { + stringsImports: { + takeBasic(s: string) { + assert.strictEqual(s, 'latin utf16'); + }, + returnUnicode() { + return '🚀🚀🚀 𠈄𓀀'; + } } } }); diff --git a/test/runtime/variants.ts b/test/runtime/variants.ts index 9ee9070ba..44fe0b8d8 100644 --- a/test/runtime/variants.ts +++ b/test/runtime/variants.ts @@ -8,47 +8,50 @@ import * as assert from 'assert'; async function run() { const wasm = await instantiate(helpers.loadWasm, { testwasi: helpers, - imports: { - roundtripOption(x) { return x; }, - roundtripResult(x) { - if (x.tag == 'ok') { - return x.val; - } else { - throw Object.assign(new Error(''), { payload: Math.round(x.val) }); + variants: { + variantsTest: { + roundtripOption(x) { return x; }, + roundtripResult(x) { + if (x.tag == 'ok') { + return x.val; + } else { + throw Object.assign(new Error(''), { payload: Math.round(x.val) }); + } + }, + roundtripEnum(x) { return x; }, + invertBool(x) { return !x; }, + variantCasts(x) { return x; }, + variantZeros(x) { return x; }, + variantTypedefs(x, y, z) {}, + variantEnums(a, b, c) { + assert.deepStrictEqual(a, true); + assert.deepStrictEqual(b, { tag: 'ok', val: undefined }); + assert.deepStrictEqual(c, "success"); + return [ + false, + { tag: 'err', val: undefined }, + "a", + ]; } - }, - roundtripEnum(x) { return x; }, - invertBool(x) { return !x; }, - variantCasts(x) { return x; }, - variantZeros(x) { return x; }, - variantTypedefs(x, y, z) {}, - variantEnums(a, b, c) { - assert.deepStrictEqual(a, true); - assert.deepStrictEqual(b, { tag: 'ok', val: undefined }); - assert.deepStrictEqual(c, "success"); - return [ - false, - { tag: 'err', val: undefined }, - "a", - ]; - }, - }, + } + } }); wasm.testImports(); - assert.deepStrictEqual(wasm.exports.roundtripOption(1), 1); - assert.deepStrictEqual(wasm.exports.roundtripOption(null), null); + assert.strictEqual(wasm.test, wasm.variantsTest); + assert.deepStrictEqual(wasm.test.roundtripOption(1), 1); + assert.deepStrictEqual(wasm.test.roundtripOption(null), null); // @ts-ignore - assert.deepStrictEqual(wasm.exports.roundtripOption(undefined), null); + assert.deepStrictEqual(wasm.test.roundtripOption(undefined), null); // @ts-ignore - assert.deepStrictEqual(wasm.exports.roundtripOption(), null); - assert.deepStrictEqual(wasm.exports.roundtripOption(2), 2); - assert.deepStrictEqual(wasm.exports.roundtripResult({ tag: 'ok', val: 2 }), 2); - assert.deepStrictEqual(wasm.exports.roundtripResult({ tag: 'ok', val: 4 }), 4); + assert.deepStrictEqual(wasm.test.roundtripOption(), null); + assert.deepStrictEqual(wasm.test.roundtripOption(2), 2); + assert.deepStrictEqual(wasm.test.roundtripResult({ tag: 'ok', val: 2 }), 2); + assert.deepStrictEqual(wasm.test.roundtripResult({ tag: 'ok', val: 4 }), 4); const f = Math.fround(5.2); try { - wasm.exports.roundtripResult({ tag: 'err', val: f }); + wasm.test.roundtripResult({ tag: 'err', val: f }); assert.fail('Expected an error'); } catch (e: any) { assert.strictEqual(e.constructor.name, 'ComponentError'); @@ -56,14 +59,14 @@ async function run() { assert.strictEqual(e.payload, 5); } - assert.deepStrictEqual(wasm.exports.roundtripEnum("a"), "a"); - assert.deepStrictEqual(wasm.exports.roundtripEnum("b"), "b"); + assert.deepStrictEqual(wasm.test.roundtripEnum("a"), "a"); + assert.deepStrictEqual(wasm.test.roundtripEnum("b"), "b"); - assert.deepStrictEqual(wasm.exports.invertBool(true), false); - assert.deepStrictEqual(wasm.exports.invertBool(false), true); + assert.deepStrictEqual(wasm.test.invertBool(true), false); + assert.deepStrictEqual(wasm.test.invertBool(false), true); { - const [a1, a2, a3, a4, a5, a6] = wasm.exports.variantCasts([ + const [a1, a2, a3, a4, a5, a6] = wasm.test.variantCasts([ { tag: 'a', val: 1 }, { tag: 'a', val: 2 }, { tag: 'a', val: 3 }, @@ -79,7 +82,7 @@ async function run() { assert.deepStrictEqual(a6, { tag: 'a', val: 6 }); } { - const [b1, b2, b3, b4, b5, b6] = wasm.exports.variantCasts([ + const [b1, b2, b3, b4, b5, b6] = wasm.test.variantCasts([ { tag: 'b', val: 1n }, { tag: 'b', val: 2 }, { tag: 'b', val: 3 }, @@ -96,7 +99,7 @@ async function run() { } { - const [a1, a2, a3, a4] = wasm.exports.variantZeros([ + const [a1, a2, a3, a4] = wasm.test.variantZeros([ { tag: 'a', val: 1 }, { tag: 'a', val: 2n }, { tag: 'a', val: 3 }, @@ -108,7 +111,7 @@ async function run() { assert.deepStrictEqual(a4, { tag: 'a', val: 4 }); } - wasm.exports.variantTypedefs(null, false, { tag: 'err', val: undefined }); + wasm.test.variantTypedefs(null, false, { tag: 'err', val: undefined }); } await run() diff --git a/update-preview2.sh b/update-preview2.sh index 340275c11..916c879c2 100755 --- a/update-preview2.sh +++ b/update-preview2.sh @@ -1,5 +1,6 @@ cd lib rm wasi_preview1_component_adapter.command.wasm rm wasi_preview1_component_adapter.reactor.wasm -wget https://github.com/bytecodealliance/preview2-prototyping/releases/download/latest/wasi_preview1_component_adapter.command.wasm -wget https://github.com/bytecodealliance/preview2-prototyping/releases/download/latest/wasi_preview1_component_adapter.reactor.wasm +wget https://github.com/bytecodealliance/wasmtime/releases/download/dev/wasi_snapshot_preview1.command.wasm +wget https://github.com/bytecodealliance/wasmtime/releases/download/dev/wasi_snapshot_preview1.reactor.wasm + diff --git a/update-tests.sh b/update-tests.sh index 7ef97e7be..e321efd48 100755 --- a/update-tests.sh +++ b/update-tests.sh @@ -2,12 +2,15 @@ git submodule init git submodule update cd wit-bindgen git pull origin main -cargo test --workspace --test runtime +cargo test -p wit-bindgen-cli --no-default-features -F c for t in target/runtime-tests/*/c-*/*.component.wasm do name="$(basename $(dirname $t))" name=${name:2} - echo "cp $t ../test/fixtures/${name}.component.wasm" - cp $t ../test/fixtures/${name}.component.wasm + echo "cp $t ../test/fixtures/components/${name}.component.wasm" + cp $t ../test/fixtures/components/${name}.component.wasm done + +# copy flavorful wit case +cp tests/runtime/flavorful/world.wit ../test/fixtures/wit/flavorful.wit diff --git a/wit-bindgen b/wit-bindgen index ca6692e9c..e69cf5db8 160000 --- a/wit-bindgen +++ b/wit-bindgen @@ -1 +1 @@ -Subproject commit ca6692e9cfbb4368a2603e91a7dc035deb23a118 +Subproject commit e69cf5db8754f829637e25491c560ec0d9728852 From 54ef413f284335aff9755c5820a707c1f6b59bc0 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 12 Jun 2023 23:46:41 -0700 Subject: [PATCH 2/3] types --- Cargo.toml | 1 + build.rs | 52 +- .../js-component-bindgen-component/src/lib.rs | 52 +- .../wit/js-component-bindgen.wit | 25 +- crates/js-component-bindgen/src/lib.rs | 22 +- crates/js-component-bindgen/src/ts_bindgen.rs | 44 +- packages/preview2-shim/README.md | 1 + packages/preview2-shim/lib/nodejs/index.js | 41 +- .../{HTTP.d.ts => http-incoming-handler.d.ts} | 2 +- .../types/imports/cli-base-environment.d.ts | 17 + .../types/imports/cli-base-exit.d.ts | 7 + .../{preopens.d.ts => cli-base-preopens.d.ts} | 17 +- .../types/imports/cli-base-stderr.d.ts | 5 + .../types/imports/cli-base-stdin.d.ts | 5 + .../types/imports/cli-base-stdout.d.ts | 5 + .../types/imports/clocks-monotonic-clock.d.ts | 24 + .../types/imports/clocks-timezone.d.ts | 71 ++ .../types/imports/clocks-wall-clock.d.ts | 31 + .../preview2-shim/types/imports/console.d.ts | 17 - .../types/imports/environment.d.ts | 4 - .../preview2-shim/types/imports/exit.d.ts | 4 - .../types/imports/filesystem-filesystem.d.ts | 857 ++++++++++++++++++ .../types/imports/filesystem.d.ts | 210 ----- ...g-HTTP.d.ts => http-outgoing-handler.d.ts} | 2 +- .../imports/{types.d.ts => http-types.d.ts} | 56 +- .../types/imports/instance-network.d.ts | 5 - .../types/imports/io-streams.d.ts | 180 ++++ .../types/imports/ip-name-lookup.d.ts | 19 - .../types/imports/logging-handler.d.ts | 40 + .../types/imports/monotonic-clock.d.ts | 8 - .../preview2-shim/types/imports/network.d.ts | 50 - .../types/imports/poll-poll.d.ts | 41 + .../preview2-shim/types/imports/poll.d.ts | 5 - .../types/imports/random-insecure-seed.d.ts | 22 + .../types/imports/random-insecure.d.ts | 20 + .../types/imports/random-random.d.ts | 22 + .../preview2-shim/types/imports/random.d.ts | 5 - .../imports/sockets-instance-network.d.ts | 8 + .../types/imports/sockets-ip-name-lookup.d.ts | 76 ++ .../types/imports/sockets-network.d.ts | 212 +++++ .../imports/sockets-tcp-create-socket.d.ts | 33 + .../types/imports/sockets-tcp.d.ts | 285 ++++++ .../imports/sockets-udp-create-socket.d.ts | 33 + .../types/imports/sockets-udp.d.ts | 219 +++++ .../preview2-shim/types/imports/streams.d.ts | 23 - .../types/imports/tcp-create-socket.d.ts | 9 - packages/preview2-shim/types/imports/tcp.d.ts | 52 -- .../preview2-shim/types/imports/timezone.d.ts | 13 - .../types/imports/udp-create-socket.d.ts | 9 - packages/preview2-shim/types/imports/udp.d.ts | 36 - .../types/imports/wall-clock.d.ts | 8 - packages/preview2-shim/types/index.d.ts | 8 - .../preview2-shim/types/wasi-command.d.ts | 23 + packages/preview2-shim/types/wasi-proxy.d.ts | 55 +- .../preview2-shim/types/wasi-reactor.d.ts | 93 +- test/fixtures/wit/wasi/command-extended.wit | 36 + test/fixtures/wit/wasi/command.wit | 26 + .../wit/wasi/deps/clocks/monotonic-clock.wit | 34 + .../wit/wasi/deps/clocks/timezone.wit | 63 ++ .../wit/wasi/deps/clocks/wall-clock.wit | 43 + .../wit/wasi/deps/filesystem/filesystem.wit | 782 ++++++++++++++++ .../wit/wasi/deps/http/incoming-handler.wit | 24 + .../wit/wasi/deps/http/outgoing-handler.wit | 18 + test/fixtures/wit/wasi/deps/http/types.wit | 159 ++++ test/fixtures/wit/wasi/deps/io/streams.wit | 215 +++++ .../wit/wasi/deps/logging/handler.wit | 34 + test/fixtures/wit/wasi/deps/poll/poll.wit | 41 + .../wit/wasi/deps/random/insecure-seed.wit | 24 + .../wit/wasi/deps/random/insecure.wit | 21 + test/fixtures/wit/wasi/deps/random/random.wit | 25 + .../wasi/deps/sockets/instance-network.wit | 9 + .../wit/wasi/deps/sockets/ip-name-lookup.wit | 69 ++ .../wit/wasi/deps/sockets/network.wit | 187 ++++ .../wasi/deps/sockets/tcp-create-socket.wit | 27 + test/fixtures/wit/wasi/deps/sockets/tcp.wit | 255 ++++++ .../wasi/deps/sockets/udp-create-socket.wit | 27 + test/fixtures/wit/wasi/deps/sockets/udp.wit | 211 +++++ .../wasi/deps/wasi-cli-base/environment.wit | 16 + .../wit/wasi/deps/wasi-cli-base/exit.wit | 4 + .../wit/wasi/deps/wasi-cli-base/preopens.wit | 7 + .../wit/wasi/deps/wasi-cli-base/stdio.wit | 17 + test/fixtures/wit/wasi/proxy.wit | 9 + test/fixtures/wit/wasi/reactor.wit | 24 + test/runtime/dummy_proxy.ts | 1 + 84 files changed, 4881 insertions(+), 711 deletions(-) rename packages/preview2-shim/types/exports/{HTTP.d.ts => http-incoming-handler.d.ts} (87%) create mode 100644 packages/preview2-shim/types/imports/cli-base-environment.d.ts create mode 100644 packages/preview2-shim/types/imports/cli-base-exit.d.ts rename packages/preview2-shim/types/imports/{preopens.d.ts => cli-base-preopens.d.ts} (50%) create mode 100644 packages/preview2-shim/types/imports/cli-base-stderr.d.ts create mode 100644 packages/preview2-shim/types/imports/cli-base-stdin.d.ts create mode 100644 packages/preview2-shim/types/imports/cli-base-stdout.d.ts create mode 100644 packages/preview2-shim/types/imports/clocks-monotonic-clock.d.ts create mode 100644 packages/preview2-shim/types/imports/clocks-timezone.d.ts create mode 100644 packages/preview2-shim/types/imports/clocks-wall-clock.d.ts delete mode 100644 packages/preview2-shim/types/imports/console.d.ts delete mode 100644 packages/preview2-shim/types/imports/environment.d.ts delete mode 100644 packages/preview2-shim/types/imports/exit.d.ts create mode 100644 packages/preview2-shim/types/imports/filesystem-filesystem.d.ts delete mode 100644 packages/preview2-shim/types/imports/filesystem.d.ts rename packages/preview2-shim/types/imports/{default-outgoing-HTTP.d.ts => http-outgoing-handler.d.ts} (90%) rename packages/preview2-shim/types/imports/{types.d.ts => http-types.d.ts} (99%) delete mode 100644 packages/preview2-shim/types/imports/instance-network.d.ts create mode 100644 packages/preview2-shim/types/imports/io-streams.d.ts delete mode 100644 packages/preview2-shim/types/imports/ip-name-lookup.d.ts create mode 100644 packages/preview2-shim/types/imports/logging-handler.d.ts delete mode 100644 packages/preview2-shim/types/imports/monotonic-clock.d.ts delete mode 100644 packages/preview2-shim/types/imports/network.d.ts create mode 100644 packages/preview2-shim/types/imports/poll-poll.d.ts delete mode 100644 packages/preview2-shim/types/imports/poll.d.ts create mode 100644 packages/preview2-shim/types/imports/random-insecure-seed.d.ts create mode 100644 packages/preview2-shim/types/imports/random-insecure.d.ts create mode 100644 packages/preview2-shim/types/imports/random-random.d.ts delete mode 100644 packages/preview2-shim/types/imports/random.d.ts create mode 100644 packages/preview2-shim/types/imports/sockets-instance-network.d.ts create mode 100644 packages/preview2-shim/types/imports/sockets-ip-name-lookup.d.ts create mode 100644 packages/preview2-shim/types/imports/sockets-network.d.ts create mode 100644 packages/preview2-shim/types/imports/sockets-tcp-create-socket.d.ts create mode 100644 packages/preview2-shim/types/imports/sockets-tcp.d.ts create mode 100644 packages/preview2-shim/types/imports/sockets-udp-create-socket.d.ts create mode 100644 packages/preview2-shim/types/imports/sockets-udp.d.ts delete mode 100644 packages/preview2-shim/types/imports/streams.d.ts delete mode 100644 packages/preview2-shim/types/imports/tcp-create-socket.d.ts delete mode 100644 packages/preview2-shim/types/imports/tcp.d.ts delete mode 100644 packages/preview2-shim/types/imports/timezone.d.ts delete mode 100644 packages/preview2-shim/types/imports/udp-create-socket.d.ts delete mode 100644 packages/preview2-shim/types/imports/udp.d.ts delete mode 100644 packages/preview2-shim/types/imports/wall-clock.d.ts delete mode 100644 packages/preview2-shim/types/index.d.ts create mode 100644 packages/preview2-shim/types/wasi-command.d.ts create mode 100644 test/fixtures/wit/wasi/command-extended.wit create mode 100644 test/fixtures/wit/wasi/command.wit create mode 100644 test/fixtures/wit/wasi/deps/clocks/monotonic-clock.wit create mode 100644 test/fixtures/wit/wasi/deps/clocks/timezone.wit create mode 100644 test/fixtures/wit/wasi/deps/clocks/wall-clock.wit create mode 100644 test/fixtures/wit/wasi/deps/filesystem/filesystem.wit create mode 100644 test/fixtures/wit/wasi/deps/http/incoming-handler.wit create mode 100644 test/fixtures/wit/wasi/deps/http/outgoing-handler.wit create mode 100644 test/fixtures/wit/wasi/deps/http/types.wit create mode 100644 test/fixtures/wit/wasi/deps/io/streams.wit create mode 100644 test/fixtures/wit/wasi/deps/logging/handler.wit create mode 100644 test/fixtures/wit/wasi/deps/poll/poll.wit create mode 100644 test/fixtures/wit/wasi/deps/random/insecure-seed.wit create mode 100644 test/fixtures/wit/wasi/deps/random/insecure.wit create mode 100644 test/fixtures/wit/wasi/deps/random/random.wit create mode 100644 test/fixtures/wit/wasi/deps/sockets/instance-network.wit create mode 100644 test/fixtures/wit/wasi/deps/sockets/ip-name-lookup.wit create mode 100644 test/fixtures/wit/wasi/deps/sockets/network.wit create mode 100644 test/fixtures/wit/wasi/deps/sockets/tcp-create-socket.wit create mode 100644 test/fixtures/wit/wasi/deps/sockets/tcp.wit create mode 100644 test/fixtures/wit/wasi/deps/sockets/udp-create-socket.wit create mode 100644 test/fixtures/wit/wasi/deps/sockets/udp.wit create mode 100644 test/fixtures/wit/wasi/deps/wasi-cli-base/environment.wit create mode 100644 test/fixtures/wit/wasi/deps/wasi-cli-base/exit.wit create mode 100644 test/fixtures/wit/wasi/deps/wasi-cli-base/preopens.wit create mode 100644 test/fixtures/wit/wasi/deps/wasi-cli-base/stdio.wit create mode 100644 test/fixtures/wit/wasi/proxy.wit create mode 100644 test/fixtures/wit/wasi/reactor.wit diff --git a/Cargo.toml b/Cargo.toml index 8a423493c..63e4c00c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ js-component-bindgen = { path = "./crates/js-component-bindgen" } wit-component = { workspace = true } [build-dependencies] +anyhow = "1.0.71" js-component-bindgen = { path = "./crates/js-component-bindgen" } wit-component = { workspace = true } diff --git a/build.rs b/build.rs index 63a7b2452..6d1b1890e 100644 --- a/build.rs +++ b/build.rs @@ -1,39 +1,41 @@ -use std::{collections::HashMap, env, fs, io::Write, path::PathBuf}; +use anyhow::Result; +use std::{env, fs, io::Write, path::PathBuf}; -fn main() { +use js_component_bindgen::{generate_types, source::wit_parser::Resolve}; + +fn main() -> Result<()> { if env::var("PREVIEW2_SHIM_TYPES").is_ok() { - let current_dir = std::env::current_dir().unwrap(); - let fixtures_dir = current_dir.join("./test/fixtures/components"); - for world in ["reactor", "proxy"] { - let component_path = fixtures_dir.join(format!("dummy_{}.component.wasm", world)); - let component = fs::read(&component_path).expect("component to be read from file"); + for world in ["proxy", "command", "reactor"] { + let name = format!("wasi-{}", world); + let preview2_wit_path = "./test/fixtures/wit/wasi"; + + let mut resolve = Resolve::default(); + let (id, _) = resolve.push_dir(&PathBuf::from(preview2_wit_path))?; + + let world = resolve.select_world(id, Some(world))?; - let import_map = HashMap::from([]); let opts = js_component_bindgen::TranspileOpts { - name: format!("wasi-{}", world), + name: "component".to_string(), no_typescript: false, - instantiation: true, - map: Some(import_map), - no_nodejs_compat: true, - base64_cutoff: 5000_usize, + no_nodejs_compat: false, + instantiation: false, + map: None, tla_compat: false, valid_lifting_optimization: false, + base64_cutoff: 0, }; - let transpiled = js_component_bindgen::transpile(component, opts) - .map_err(|e| format!("{:?}", e)) - .unwrap(); + let files = generate_types(name, resolve, world, opts)?; - for (filename, contents) in transpiled.files.iter() { - if filename.ends_with(".d.ts") { - let outfile = PathBuf::from("./packages/preview2-shim/types").join(filename); - fs::create_dir_all(outfile.parent().unwrap()).unwrap(); - let mut file = fs::File::create(outfile).unwrap(); - file.write_all(contents).unwrap(); - } + for (filename, contents) in files.iter() { + let outfile = PathBuf::from("./packages/preview2-shim/types").join(filename); + fs::create_dir_all(outfile.parent().unwrap()).unwrap(); + let mut file = fs::File::create(outfile).unwrap(); + file.write_all(contents).unwrap(); } - println!("cargo:rerun-if-changed={:?}", component_path); + // println!("cargo:rerun-if-changed={:?}", preview2_wit_path); } } - println!("cargo:rerun-if-changed=build.rs"); + // println!("cargo:rerun-if-changed=build.rs"); + Ok(()) } diff --git a/crates/js-component-bindgen-component/src/lib.rs b/crates/js-component-bindgen-component/src/lib.rs index b88758d8c..760b4247c 100644 --- a/crates/js-component-bindgen-component/src/lib.rs +++ b/crates/js-component-bindgen-component/src/lib.rs @@ -1,5 +1,11 @@ +use std::path::PathBuf; + use anyhow::Result; -use js_component_bindgen::transpile; +use js_component_bindgen::{ + generate_types, + source::wit_parser::{Resolve, UnresolvedPackage}, + transpile, +}; /// Calls [`write!`] with the passed arguments and unwraps the result. /// @@ -81,4 +87,48 @@ impl JsComponentBindgen for JsComponentBindgenComponent { .collect(), }) } + + fn generate_types( + name: String, + opts: TypeGenerationOptions, + ) -> Result)>, String> { + let mut resolve = Resolve::default(); + let pkg = match opts.wit { + Wit::Source(source) => { + UnresolvedPackage::parse(&PathBuf::from(format!("{name}.wit")), &source) + .map_err(|e| e.to_string())? + } + Wit::Path(path) => { + UnresolvedPackage::parse_file(&PathBuf::from(path)).map_err(|e| e.to_string())? + } + Wit::Binary(_) => todo!(), + }; + let id = resolve.push(pkg).map_err(|e| e.to_string())?; + + let world_string = match &opts.world { + Some(world) => Some(world.to_string()), + None => None, + }; + let world = resolve + .select_world(id, world_string.as_deref()) + .map_err(|e| e.to_string())?; + + let opts = js_component_bindgen::TranspileOpts { + name: "component".to_string(), + no_typescript: false, + no_nodejs_compat: false, + instantiation: opts.instantiation.unwrap_or(false), + map: match opts.map { + Some(map) => Some(map.into_iter().collect()), + None => None, + }, + tla_compat: opts.tla_compat.unwrap_or(false), + valid_lifting_optimization: false, + base64_cutoff: 0, + }; + + let files = generate_types(name, resolve, world, opts).map_err(|e| e.to_string())?; + + Ok(files) + } } diff --git a/crates/js-component-bindgen-component/wit/js-component-bindgen.wit b/crates/js-component-bindgen-component/wit/js-component-bindgen.wit index d24c15a71..032f56289 100644 --- a/crates/js-component-bindgen-component/wit/js-component-bindgen.wit +++ b/crates/js-component-bindgen-component/wit/js-component-bindgen.wit @@ -38,6 +38,25 @@ world js-component-bindgen { valid-lifting-optimization: option, } + variant wit { + /// wit is provided as an inline WIT string + source(string), + /// wit is provided from a component binary + binary(list), + /// wit is provided from a filesystem path + path(string), + } + + record type-generation-options { + /// wit to generate typing from + wit: wit, + /// world to generate typing for + %world: option, + tla-compat: option, + instantiation: option, + map: option, + } + enum export-type { function, instance, @@ -49,8 +68,10 @@ world js-component-bindgen { exports: list> } - /// Generate the file structure for the transpilation of a component + /// Generate the file structure for the transpiled of a component /// into a JS embedding, returns the file list and imports and exports of the - /// output JS transpiled component + /// output JS generation component export generate: func(component: list, options: generate-options) -> result + + export generate-types: func(name: string, options: type-generation-options) -> result } diff --git a/crates/js-component-bindgen/src/lib.rs b/crates/js-component-bindgen/src/lib.rs index 9f27163c3..104528ed6 100644 --- a/crates/js-component-bindgen/src/lib.rs +++ b/crates/js-component-bindgen/src/lib.rs @@ -21,6 +21,7 @@ use wasmtime_environ::{ScopeVec, Tunables}; use wit_component::DecodedWasm; use ts_bindgen::ts_bindgen; +use wit_parser::{Resolve, WorldId}; /// Calls [`write!`] with the passed arguments and unwraps the result. /// @@ -59,8 +60,25 @@ pub struct ComponentInfo { pub exports: Vec<(String, wasmtime_environ::component::Export)>, } +pub fn generate_types( + name: String, + resolve: Resolve, + world_id: WorldId, + opts: TranspileOpts, +) -> Result)>, anyhow::Error> { + let mut files = files::Files::default(); + + ts_bindgen(&name, &resolve, world_id, &opts, &mut files); + + let mut files_out: Vec<(String, Vec)> = Vec::new(); + for (name, source) in files.iter() { + files_out.push((name.to_string(), source.to_vec())); + } + Ok(files_out) +} + /// Generate the JS transpilation bindgen for a given Wasm component binary -/// Outputs the file map and import and export metadata for the generation +/// Outputs the file map and import and export metadata for the Transpilation #[cfg(feature = "transpile-bindgen")] pub fn transpile(component: Vec, opts: TranspileOpts) -> Result { let name = opts.name.clone(); @@ -71,7 +89,7 @@ pub fn transpile(component: Vec, opts: TranspileOpts) -> Result funcs.push((name, f)), WorldItem::Interface(id) => { - bindgen.export_interface(resolve, &name, *id, files); + bindgen.export_interface(resolve, &name, *id, files, opts.instantiation); } WorldItem::Type(_) => unimplemented!("type exports"), } } for (alias, name) in export_aliases { if !seen_names.contains(&alias) { - uwriteln!( - bindgen.export_object, - "{}: typeof {}Exports", - alias, - name.to_upper_camel_case() - ); + if opts.instantiation { + uwriteln!( + bindgen.export_object, + "{}: typeof {}Exports,", + alias, + name.to_upper_camel_case() + ); + } else { + uwriteln!( + bindgen.export_object, + "export const {}: typeof {}Exports;", + alias, + name.to_upper_camel_case() + ); + } } } if !funcs.is_empty() { @@ -274,6 +283,7 @@ impl TsBindgen { name: &str, id: InterfaceId, files: &mut Files, + instantiation: bool, ) { self.generate_interface( name, @@ -285,11 +295,19 @@ impl TsBindgen { AbiVariant::GuestExport, ); let camel = name.to_upper_camel_case(); - uwriteln!( - self.export_object, - "{}: typeof {camel}Exports,", - name.to_lower_camel_case() - ); + if instantiation { + uwriteln!( + self.export_object, + "{}: typeof {camel}Exports,", + name.to_lower_camel_case() + ); + } else { + uwriteln!( + self.export_object, + "export const {}: typeof {camel}Exports;", + name.to_lower_camel_case() + ); + } } fn export_funcs( diff --git a/packages/preview2-shim/README.md b/packages/preview2-shim/README.md index 564c8e14c..9eb0e1ec7 100644 --- a/packages/preview2-shim/README.md +++ b/packages/preview2-shim/README.md @@ -16,6 +16,7 @@ Currently supports Node.js and browser versions, but alternative implementations | Poll | :x: | :x: | | Random | :heavy_check_mark: | :heavy_check_mark: | | Sockets | :x: | :x: | +| CLI Base | :heavy_check_mark: | :heavy_check_mark: | # License diff --git a/packages/preview2-shim/lib/nodejs/index.js b/packages/preview2-shim/lib/nodejs/index.js index c7bc2cd6f..298f8f1dd 100644 --- a/packages/preview2-shim/lib/nodejs/index.js +++ b/packages/preview2-shim/lib/nodejs/index.js @@ -1,16 +1,10 @@ -import * as console from "./console.js"; -import * as defaultOutgoingHttp from "./default-outgoing-HTTP.js"; +import * as clocks from "./clocks.js"; import * as environment from "./environment.js"; import * as exit from "./exit.js"; import * as filesystem from "./filesystem.js"; -import * as instanceNetwork from "./instance-network.js"; -import * as ipNameLookup from "./ip-name-lookup.js"; -import * as monotonicClock from "./monotonic-clock.js"; -import * as network from "./network.js"; -import * as poll from "./poll.js"; -import * as preopens from "./preopens.js"; +import * as monotonicClock from "./clocks.js"; import * as random from "./random.js"; -import * as streams from "./streams.js"; +import * as streams from "./io.js"; import * as tcpCreateSocket from "./tcp-create-socket.js"; import * as tcp from "./tcp.js"; import * as timezone from "./timezone.js"; @@ -20,26 +14,15 @@ import * as udp from "./udp.js"; import * as wallClock from "./wall-clock.js"; export const importObject = { - 'console': console, - 'default-outgoing-HTTP': defaultOutgoingHttp, - 'environment': environment, - 'exit': exit, - 'filesystem': filesystem, - 'instance-network': instanceNetwork, - 'ip-name-lookup': ipNameLookup, - 'monotonic-clock': monotonicClock, - 'network': network, - 'poll': poll, - 'preopens': preopens, - 'random': random, - 'streams': streams, - 'tcp-create-socket': tcpCreateSocket, - 'tcp': tcp, - 'timezone': timezone, - 'types': types, - 'udp-create-socket': udpCreateSocket, - 'udp': udp, - 'wall-clock': wallClock + clocks, + filesystem, + http, + io, + logging, + poll, + random, + sockets, + cliBase, }; export { WasiHttp } from "../http/wasi-http.js"; diff --git a/packages/preview2-shim/types/exports/HTTP.d.ts b/packages/preview2-shim/types/exports/http-incoming-handler.d.ts similarity index 87% rename from packages/preview2-shim/types/exports/HTTP.d.ts rename to packages/preview2-shim/types/exports/http-incoming-handler.d.ts index 844504d8a..68472899b 100644 --- a/packages/preview2-shim/types/exports/HTTP.d.ts +++ b/packages/preview2-shim/types/exports/http-incoming-handler.d.ts @@ -1,4 +1,4 @@ -export namespace Http { +export namespace HttpIncomingHandler { export function handle(request: IncomingRequest, responseOut: ResponseOutparam): void; } import type { IncomingRequest } from '../imports/types'; diff --git a/packages/preview2-shim/types/imports/cli-base-environment.d.ts b/packages/preview2-shim/types/imports/cli-base-environment.d.ts new file mode 100644 index 000000000..e47855763 --- /dev/null +++ b/packages/preview2-shim/types/imports/cli-base-environment.d.ts @@ -0,0 +1,17 @@ +export namespace CliBaseEnvironment { + export function /** + * Get the POSIX-style environment variables. + * + * Each environment variable is provided as a pair of string variable names + * and string value. + * + * Morally, these are a value import, but until value imports are available + * in the component model, this import function should return the same + * values each time it is called. + */ + getEnvironment(): [string, string][]; + export function /** + * Get the POSIX-style arguments to the program. + */ + getArguments(): string[]; +} diff --git a/packages/preview2-shim/types/imports/cli-base-exit.d.ts b/packages/preview2-shim/types/imports/cli-base-exit.d.ts new file mode 100644 index 000000000..9841ba8a8 --- /dev/null +++ b/packages/preview2-shim/types/imports/cli-base-exit.d.ts @@ -0,0 +1,7 @@ +export namespace CliBaseExit { + export function /** + * Exit the curerent instance and any linked instances. + */ + exit(status: Result): void; +} +export type Result = { tag: 'ok', val: T } | { tag: 'err', val: E }; diff --git a/packages/preview2-shim/types/imports/preopens.d.ts b/packages/preview2-shim/types/imports/cli-base-preopens.d.ts similarity index 50% rename from packages/preview2-shim/types/imports/preopens.d.ts rename to packages/preview2-shim/types/imports/cli-base-preopens.d.ts index 6a4a8c23c..23f2b3e5d 100644 --- a/packages/preview2-shim/types/imports/preopens.d.ts +++ b/packages/preview2-shim/types/imports/cli-base-preopens.d.ts @@ -1,15 +1,12 @@ -export namespace Preopens { - export function getStdio(): StdioPreopens; - export function getDirectories(): [Descriptor, string][]; +export namespace CliBasePreopens { + export function /** + * Return the set of of preopened directories, and their path. + */ + getDirectories(): [Descriptor, string][]; } +import type { Descriptor } from '../imports/filesystem'; +export { Descriptor }; import type { InputStream } from '../imports/streams'; export { InputStream }; import type { OutputStream } from '../imports/streams'; export { OutputStream }; -export interface StdioPreopens { - stdin: InputStream, - stdout: OutputStream, - stderr: OutputStream, -} -import type { Descriptor } from '../imports/filesystem'; -export { Descriptor }; diff --git a/packages/preview2-shim/types/imports/cli-base-stderr.d.ts b/packages/preview2-shim/types/imports/cli-base-stderr.d.ts new file mode 100644 index 000000000..66381437f --- /dev/null +++ b/packages/preview2-shim/types/imports/cli-base-stderr.d.ts @@ -0,0 +1,5 @@ +export namespace CliBaseStderr { + export function getStderr(): OutputStream; +} +import type { OutputStream } from '../imports/streams'; +export { OutputStream }; diff --git a/packages/preview2-shim/types/imports/cli-base-stdin.d.ts b/packages/preview2-shim/types/imports/cli-base-stdin.d.ts new file mode 100644 index 000000000..871840e94 --- /dev/null +++ b/packages/preview2-shim/types/imports/cli-base-stdin.d.ts @@ -0,0 +1,5 @@ +export namespace CliBaseStdin { + export function getStdin(): InputStream; +} +import type { InputStream } from '../imports/streams'; +export { InputStream }; diff --git a/packages/preview2-shim/types/imports/cli-base-stdout.d.ts b/packages/preview2-shim/types/imports/cli-base-stdout.d.ts new file mode 100644 index 000000000..85e63971e --- /dev/null +++ b/packages/preview2-shim/types/imports/cli-base-stdout.d.ts @@ -0,0 +1,5 @@ +export namespace CliBaseStdout { + export function getStdout(): OutputStream; +} +import type { OutputStream } from '../imports/streams'; +export { OutputStream }; diff --git a/packages/preview2-shim/types/imports/clocks-monotonic-clock.d.ts b/packages/preview2-shim/types/imports/clocks-monotonic-clock.d.ts new file mode 100644 index 000000000..f5491d5bf --- /dev/null +++ b/packages/preview2-shim/types/imports/clocks-monotonic-clock.d.ts @@ -0,0 +1,24 @@ +export namespace ClocksMonotonicClock { + export function /** + * Read the current value of the clock. + * + * The clock is monotonic, therefore calling this function repeatedly will + * produce a sequence of non-decreasing values. + */ + now(): Instant; + export function /** + * Query the resolution of the clock. + */ + resolution(): Instant; + export function /** + * Create a `pollable` which will resolve once the specified time has been + * reached. + */ + subscribe(when: Instant, absolute: boolean): Pollable; +} +import type { Pollable } from '../imports/poll'; +export { Pollable }; +/** + * A timestamp in nanoseconds. + */ +export type Instant = bigint; diff --git a/packages/preview2-shim/types/imports/clocks-timezone.d.ts b/packages/preview2-shim/types/imports/clocks-timezone.d.ts new file mode 100644 index 000000000..083612e6e --- /dev/null +++ b/packages/preview2-shim/types/imports/clocks-timezone.d.ts @@ -0,0 +1,71 @@ +export namespace ClocksTimezone { + export function /** + * Return information needed to display the given `datetime`. This includes + * the UTC offset, the time zone name, and a flag indicating whether + * daylight saving time is active. + * + * If the timezone cannot be determined for the given `datetime`, return a + * `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight + * saving time. + */ + display(this: Timezone, when: Datetime): TimezoneDisplay; + export function /** + * The same as `display`, but only return the UTC offset. + */ + utcOffset(this: Timezone, when: Datetime): number; + export function /** + * Dispose of the specified input-stream, after which it may no longer + * be used. + */ + dropTimezone(this: Timezone): void; +} +import type { Datetime } from '../imports/wall-clock'; +export { Datetime }; +/** + * Information useful for displaying the timezone of a specific `datetime`. + * + * This information may vary within a single `timezone` to reflect daylight + * saving time adjustments. + */ +export interface TimezoneDisplay { + /** + * The number of seconds difference between UTC time and the local + * time of the timezone. + * + * The returned value will always be less than 86400 which is the + * number of seconds in a day (24*60*60). + * + * In implementations that do not expose an actual time zone, this + * should return 0. + */ + utcOffset: number, + /** + * The abbreviated name of the timezone to display to a user. The name + * `UTC` indicates Coordinated Universal Time. Otherwise, this should + * reference local standards for the name of the time zone. + * + * In implementations that do not expose an actual time zone, this + * should be the string `UTC`. + * + * In time zones that do not have an applicable name, a formatted + * representation of the UTC offset may be returned, such as `-04:00`. + */ + name: string, + /** + * Whether daylight saving time is active. + * + * In implementations that do not expose an actual time zone, this + * should return false. + */ + inDaylightSavingTime: boolean, +} +/** + * A timezone. + * + * In timezones that recognize daylight saving time, also known as daylight + * time and summer time, the information returned from the functions varies + * over time to reflect these adjustments. + * + * This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + */ +export type Timezone = number; diff --git a/packages/preview2-shim/types/imports/clocks-wall-clock.d.ts b/packages/preview2-shim/types/imports/clocks-wall-clock.d.ts new file mode 100644 index 000000000..9d16dd14c --- /dev/null +++ b/packages/preview2-shim/types/imports/clocks-wall-clock.d.ts @@ -0,0 +1,31 @@ +export namespace ClocksWallClock { + export function /** + * Read the current value of the clock. + * + * This clock is not monotonic, therefore calling this function repeatedly + * will not necessarily produce a sequence of non-decreasing values. + * + * The returned timestamps represent the number of seconds since + * 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + * also known as [Unix Time]. + * + * The nanoseconds field of the output is always less than 1000000000. + * + * [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + * [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + */ + now(): Datetime; + export function /** + * Query the resolution of the clock. + * + * The nanoseconds field of the output is always less than 1000000000. + */ + resolution(): Datetime; +} +/** + * A time and date in seconds plus nanoseconds. + */ +export interface Datetime { + seconds: bigint, + nanoseconds: number, +} diff --git a/packages/preview2-shim/types/imports/console.d.ts b/packages/preview2-shim/types/imports/console.d.ts deleted file mode 100644 index 836dc6288..000000000 --- a/packages/preview2-shim/types/imports/console.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -export namespace Console { - export function log(level: Level, context: string, message: string): void; -} -/** - * # Variants - * - * ## `"trace"` - * - * ## `"debug"` - * - * ## `"info"` - * - * ## `"warn"` - * - * ## `"error"` - */ -export type Level = 'trace' | 'debug' | 'info' | 'warn' | 'error'; diff --git a/packages/preview2-shim/types/imports/environment.d.ts b/packages/preview2-shim/types/imports/environment.d.ts deleted file mode 100644 index dd76cc3ce..000000000 --- a/packages/preview2-shim/types/imports/environment.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export namespace Environment { - export function getEnvironment(): [string, string][]; - export function getArguments(): string[]; -} diff --git a/packages/preview2-shim/types/imports/exit.d.ts b/packages/preview2-shim/types/imports/exit.d.ts deleted file mode 100644 index 6bbe89687..000000000 --- a/packages/preview2-shim/types/imports/exit.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export namespace Exit { - export function exit(status: Result): void; -} -export type Result = { tag: 'ok', val: T } | { tag: 'err', val: E }; diff --git a/packages/preview2-shim/types/imports/filesystem-filesystem.d.ts b/packages/preview2-shim/types/imports/filesystem-filesystem.d.ts new file mode 100644 index 000000000..8f4b750a6 --- /dev/null +++ b/packages/preview2-shim/types/imports/filesystem-filesystem.d.ts @@ -0,0 +1,857 @@ +export namespace FilesystemFilesystem { + export function /** + * Return a stream for reading from a file. + * + * Multiple read, write, and append streams may be active on the same open + * file and they do not interfere with each other. + * + * Note: This allows using `wasi:io/streams.read`, which is similar to `read` in POSIX. + */ + readViaStream(this: Descriptor, offset: Filesize): InputStream; + export function /** + * Return a stream for writing to a file. + * + * Note: This allows using `wasi:io/streams.write`, which is similar to `write` in + * POSIX. + */ + writeViaStream(this: Descriptor, offset: Filesize): OutputStream; + export function /** + * Return a stream for appending to a file. + * + * Note: This allows using `wasi:io/streams.write`, which is similar to `write` with + * `O_APPEND` in in POSIX. + */ + appendViaStream(this: Descriptor): OutputStream; + export function /** + * Provide file advisory information on a descriptor. + * + * This is similar to `posix_fadvise` in POSIX. + */ + advise(this: Descriptor, offset: Filesize, length: Filesize, advice: Advice): void; + export function /** + * Synchronize the data of a file to disk. + * + * This function succeeds with no effect if the file descriptor is not + * opened for writing. + * + * Note: This is similar to `fdatasync` in POSIX. + */ + syncData(this: Descriptor): void; + export function /** + * Get flags associated with a descriptor. + * + * Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + * + * Note: This returns the value that was the `fs_flags` value returned + * from `fdstat_get` in earlier versions of WASI. + */ + getFlags(this: Descriptor): DescriptorFlags; + export function /** + * Get the dynamic type of a descriptor. + * + * Note: This returns the same value as the `type` field of the `fd-stat` + * returned by `stat`, `stat-at` and similar. + * + * Note: This returns similar flags to the `st_mode & S_IFMT` value provided + * by `fstat` in POSIX. + * + * Note: This returns the value that was the `fs_filetype` value returned + * from `fdstat_get` in earlier versions of WASI. + */ + getType(this: Descriptor): DescriptorType; + export function /** + * Adjust the size of an open file. If this increases the file's size, the + * extra bytes are filled with zeros. + * + * Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + */ + setSize(this: Descriptor, size: Filesize): void; + export function /** + * Adjust the timestamps of an open file or directory. + * + * Note: This is similar to `futimens` in POSIX. + * + * Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + */ + setTimes(this: Descriptor, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; + export function /** + * Read from a descriptor, without using and updating the descriptor's offset. + * + * This function returns a list of bytes containing the data that was + * read, along with a bool which, when true, indicates that the end of the + * file was reached. The returned list will contain up to `length` bytes; it + * may return fewer than requested, if the end of the file is reached or + * if the I/O operation is interrupted. + * + * In the future, this may change to return a `stream`. + * + * Note: This is similar to `pread` in POSIX. + */ + read(this: Descriptor, length: Filesize, offset: Filesize): [Uint8Array | ArrayBuffer, boolean]; + export function /** + * Write to a descriptor, without using and updating the descriptor's offset. + * + * It is valid to write past the end of a file; the file is extended to the + * extent of the write, with bytes between the previous end and the start of + * the write set to zero. + * + * In the future, this may change to take a `stream`. + * + * Note: This is similar to `pwrite` in POSIX. + */ + write(this: Descriptor, buffer: Uint8Array, offset: Filesize): Filesize; + export function /** + * Read directory entries from a directory. + * + * On filesystems where directories contain entries referring to themselves + * and their parents, often named `.` and `..` respectively, these entries + * are omitted. + * + * This always returns a new stream which starts at the beginning of the + * directory. Multiple streams may be active on the same directory, and they + * do not interfere with each other. + */ + readDirectory(this: Descriptor): DirectoryEntryStream; + export function /** + * Synchronize the data and metadata of a file to disk. + * + * This function succeeds with no effect if the file descriptor is not + * opened for writing. + * + * Note: This is similar to `fsync` in POSIX. + */ + sync(this: Descriptor): void; + export function /** + * Create a directory. + * + * Note: This is similar to `mkdirat` in POSIX. + */ + createDirectoryAt(this: Descriptor, path: string): void; + export function /** + * Return the attributes of an open file or directory. + * + * Note: This is similar to `fstat` in POSIX. + * + * Note: This was called `fd_filestat_get` in earlier versions of WASI. + */ + stat(this: Descriptor): DescriptorStat; + export function /** + * Return the attributes of a file or directory. + * + * Note: This is similar to `fstatat` in POSIX. + * + * Note: This was called `path_filestat_get` in earlier versions of WASI. + */ + statAt(this: Descriptor, pathFlags: PathFlags, path: string): DescriptorStat; + export function /** + * Adjust the timestamps of a file or directory. + * + * Note: This is similar to `utimensat` in POSIX. + * + * Note: This was called `path_filestat_set_times` in earlier versions of + * WASI. + */ + setTimesAt(this: Descriptor, pathFlags: PathFlags, path: string, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; + export function /** + * Create a hard link. + * + * Note: This is similar to `linkat` in POSIX. + */ + linkAt(this: Descriptor, oldPathFlags: PathFlags, oldPath: string, newDescriptor: Descriptor, newPath: string): void; + export function /** + * Open a file or directory. + * + * The returned descriptor is not guaranteed to be the lowest-numbered + * descriptor not currently open/ it is randomized to prevent applications + * from depending on making assumptions about indexes, since this is + * error-prone in multi-threaded contexts. The returned descriptor is + * guaranteed to be less than 2**31. + * + * If `flags` contains `descriptor-flags::mutate-directory`, and the base + * descriptor doesn't have `descriptor-flags::mutate-directory` set, + * `open-at` fails with `error-code::read-only`. + * + * If `flags` contains `write` or `mutate-directory`, or `open-flags` + * contains `truncate` or `create`, and the base descriptor doesn't have + * `descriptor-flags::mutate-directory` set, `open-at` fails with + * `error-code::read-only`. + * + * Note: This is similar to `openat` in POSIX. + */ + openAt(this: Descriptor, pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: DescriptorFlags, modes: Modes): Descriptor; + export function /** + * Read the contents of a symbolic link. + * + * If the contents contain an absolute or rooted path in the underlying + * filesystem, this function fails with `error-code::not-permitted`. + * + * Note: This is similar to `readlinkat` in POSIX. + */ + readlinkAt(this: Descriptor, path: string): string; + export function /** + * Remove a directory. + * + * Return `error-code::not-empty` if the directory is not empty. + * + * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + */ + removeDirectoryAt(this: Descriptor, path: string): void; + export function /** + * Rename a filesystem object. + * + * Note: This is similar to `renameat` in POSIX. + */ + renameAt(this: Descriptor, oldPath: string, newDescriptor: Descriptor, newPath: string): void; + export function /** + * Create a symbolic link (also known as a "symlink"). + * + * If `old-path` starts with `/`, the function fails with + * `error-code::not-permitted`. + * + * Note: This is similar to `symlinkat` in POSIX. + */ + symlinkAt(this: Descriptor, oldPath: string, newPath: string): void; + export function /** + * Check accessibility of a filesystem path. + * + * Check whether the given filesystem path names an object which is + * readable, writable, or executable, or whether it exists. + * + * This does not a guarantee that subsequent accesses will succeed, as + * filesystem permissions may be modified asynchronously by external + * entities. + * + * Note: This is similar to `faccessat` with the `AT_EACCESS` flag in POSIX. + */ + accessAt(this: Descriptor, pathFlags: PathFlags, path: string, type: AccessType): void; + export function /** + * Unlink a filesystem object that is not a directory. + * + * Return `error-code::is-directory` if the path refers to a directory. + * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + */ + unlinkFileAt(this: Descriptor, path: string): void; + export function /** + * Change the permissions of a filesystem object that is not a directory. + * + * Note that the ultimate meanings of these permissions is + * filesystem-specific. + * + * Note: This is similar to `fchmodat` in POSIX. + */ + changeFilePermissionsAt(this: Descriptor, pathFlags: PathFlags, path: string, modes: Modes): void; + export function /** + * Change the permissions of a directory. + * + * Note that the ultimate meanings of these permissions is + * filesystem-specific. + * + * Unlike in POSIX, the `executable` flag is not reinterpreted as a "search" + * flag. `read` on a directory implies readability and searchability, and + * `execute` is not valid for directories. + * + * Note: This is similar to `fchmodat` in POSIX. + */ + changeDirectoryPermissionsAt(this: Descriptor, pathFlags: PathFlags, path: string, modes: Modes): void; + export function /** + * Request a shared advisory lock for an open file. + * + * This requests a *shared* lock; more than one shared lock can be held for + * a file at the same time. + * + * If the open file has an exclusive lock, this function downgrades the lock + * to a shared lock. If it has a shared lock, this function has no effect. + * + * This requests an *advisory* lock, meaning that the file could be accessed + * by other programs that don't hold the lock. + * + * It is unspecified how shared locks interact with locks acquired by + * non-WASI programs. + * + * This function blocks until the lock can be acquired. + * + * Not all filesystems support locking; on filesystems which don't support + * locking, this function returns `error-code::unsupported`. + * + * Note: This is similar to `flock(fd, LOCK_SH)` in Unix. + */ + lockShared(this: Descriptor): void; + export function /** + * Request an exclusive advisory lock for an open file. + * + * This requests an *exclusive* lock; no other locks may be held for the + * file while an exclusive lock is held. + * + * If the open file has a shared lock and there are no exclusive locks held + * for the file, this function upgrades the lock to an exclusive lock. If the + * open file already has an exclusive lock, this function has no effect. + * + * This requests an *advisory* lock, meaning that the file could be accessed + * by other programs that don't hold the lock. + * + * It is unspecified whether this function succeeds if the file descriptor + * is not opened for writing. It is unspecified how exclusive locks interact + * with locks acquired by non-WASI programs. + * + * This function blocks until the lock can be acquired. + * + * Not all filesystems support locking; on filesystems which don't support + * locking, this function returns `error-code::unsupported`. + * + * Note: This is similar to `flock(fd, LOCK_EX)` in Unix. + */ + lockExclusive(this: Descriptor): void; + export function /** + * Request a shared advisory lock for an open file. + * + * This requests a *shared* lock; more than one shared lock can be held for + * a file at the same time. + * + * If the open file has an exclusive lock, this function downgrades the lock + * to a shared lock. If it has a shared lock, this function has no effect. + * + * This requests an *advisory* lock, meaning that the file could be accessed + * by other programs that don't hold the lock. + * + * It is unspecified how shared locks interact with locks acquired by + * non-WASI programs. + * + * This function returns `error-code::would-block` if the lock cannot be + * acquired. + * + * Not all filesystems support locking; on filesystems which don't support + * locking, this function returns `error-code::unsupported`. + * + * Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. + */ + tryLockShared(this: Descriptor): void; + export function /** + * Request an exclusive advisory lock for an open file. + * + * This requests an *exclusive* lock; no other locks may be held for the + * file while an exclusive lock is held. + * + * If the open file has a shared lock and there are no exclusive locks held + * for the file, this function upgrades the lock to an exclusive lock. If the + * open file already has an exclusive lock, this function has no effect. + * + * This requests an *advisory* lock, meaning that the file could be accessed + * by other programs that don't hold the lock. + * + * It is unspecified whether this function succeeds if the file descriptor + * is not opened for writing. It is unspecified how exclusive locks interact + * with locks acquired by non-WASI programs. + * + * This function returns `error-code::would-block` if the lock cannot be + * acquired. + * + * Not all filesystems support locking; on filesystems which don't support + * locking, this function returns `error-code::unsupported`. + * + * Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. + */ + tryLockExclusive(this: Descriptor): void; + export function /** + * Release a shared or exclusive lock on an open file. + * + * Note: This is similar to `flock(fd, LOCK_UN)` in Unix. + */ + unlock(this: Descriptor): void; + export function /** + * Dispose of the specified `descriptor`, after which it may no longer + * be used. + */ + dropDescriptor(this: Descriptor): void; + export function /** + * Read a single directory entry from a `directory-entry-stream`. + */ + readDirectoryEntry(this: DirectoryEntryStream): DirectoryEntry | null; + export function /** + * Dispose of the specified `directory-entry-stream`, after which it may no longer + * be used. + */ + dropDirectoryEntryStream(this: DirectoryEntryStream): void; +} +import type { InputStream } from '../imports/streams'; +export { InputStream }; +import type { OutputStream } from '../imports/streams'; +export { OutputStream }; +import type { Datetime } from '../imports/wall-clock'; +export { Datetime }; +/** + * Flags determining the method of how paths are resolved. + */ +export interface PathFlags { + /** + * As long as the resolved path corresponds to a symbolic link, it is + * expanded. + */ + symlinkFollow?: boolean, +} +/** + * Open flags used by `open-at`. + */ +export interface OpenFlags { + /** + * Create file if it does not exist, similar to `O_CREAT` in POSIX. + */ + create?: boolean, + /** + * Fail if not a directory, similar to `O_DIRECTORY` in POSIX. + */ + directory?: boolean, + /** + * Fail if file already exists, similar to `O_EXCL` in POSIX. + */ + exclusive?: boolean, + /** + * Truncate file to size 0, similar to `O_TRUNC` in POSIX. + */ + truncate?: boolean, +} +/** + * Permissions mode used by `open-at`, `change-file-permissions-at`, and + * similar. + */ +export interface Modes { + /** + * True if the resource is considered readable by the containing + * filesystem. + */ + readable?: boolean, + /** + * True if the resource is considered writable by the containing + * filesystem. + */ + writable?: boolean, + /** + * True if the resource is considered executable by the containing + * filesystem. This does not apply to directories. + */ + executable?: boolean, +} +/** + * Number of hard links to an inode. + */ +export type LinkCount = bigint; +/** + * Filesystem object serial number that is unique within its file system. + */ +export type Inode = bigint; +/** + * File size or length of a region within a file. + */ +export type Filesize = bigint; +/** + * Error codes returned by functions, similar to `errno` in POSIX. + * Not all of these error codes are returned by the functions provided by this + * API; some are used in higher-level library layers, and others are provided + * merely for alignment with POSIX. + * + * # Variants + * + * ## `"access"` + * + * Permission denied, similar to `EACCES` in POSIX. + * + * ## `"would-block"` + * + * Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. + * + * ## `"already"` + * + * Connection already in progress, similar to `EALREADY` in POSIX. + * + * ## `"bad-descriptor"` + * + * Bad descriptor, similar to `EBADF` in POSIX. + * + * ## `"busy"` + * + * Device or resource busy, similar to `EBUSY` in POSIX. + * + * ## `"deadlock"` + * + * Resource deadlock would occur, similar to `EDEADLK` in POSIX. + * + * ## `"quota"` + * + * Storage quota exceeded, similar to `EDQUOT` in POSIX. + * + * ## `"exist"` + * + * File exists, similar to `EEXIST` in POSIX. + * + * ## `"file-too-large"` + * + * File too large, similar to `EFBIG` in POSIX. + * + * ## `"illegal-byte-sequence"` + * + * Illegal byte sequence, similar to `EILSEQ` in POSIX. + * + * ## `"in-progress"` + * + * Operation in progress, similar to `EINPROGRESS` in POSIX. + * + * ## `"interrupted"` + * + * Interrupted function, similar to `EINTR` in POSIX. + * + * ## `"invalid"` + * + * Invalid argument, similar to `EINVAL` in POSIX. + * + * ## `"io"` + * + * I/O error, similar to `EIO` in POSIX. + * + * ## `"is-directory"` + * + * Is a directory, similar to `EISDIR` in POSIX. + * + * ## `"loop"` + * + * Too many levels of symbolic links, similar to `ELOOP` in POSIX. + * + * ## `"too-many-links"` + * + * Too many links, similar to `EMLINK` in POSIX. + * + * ## `"message-size"` + * + * Message too large, similar to `EMSGSIZE` in POSIX. + * + * ## `"name-too-long"` + * + * Filename too long, similar to `ENAMETOOLONG` in POSIX. + * + * ## `"no-device"` + * + * No such device, similar to `ENODEV` in POSIX. + * + * ## `"no-entry"` + * + * No such file or directory, similar to `ENOENT` in POSIX. + * + * ## `"no-lock"` + * + * No locks available, similar to `ENOLCK` in POSIX. + * + * ## `"insufficient-memory"` + * + * Not enough space, similar to `ENOMEM` in POSIX. + * + * ## `"insufficient-space"` + * + * No space left on device, similar to `ENOSPC` in POSIX. + * + * ## `"not-directory"` + * + * Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX. + * + * ## `"not-empty"` + * + * Directory not empty, similar to `ENOTEMPTY` in POSIX. + * + * ## `"not-recoverable"` + * + * State not recoverable, similar to `ENOTRECOVERABLE` in POSIX. + * + * ## `"unsupported"` + * + * Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX. + * + * ## `"no-tty"` + * + * Inappropriate I/O control operation, similar to `ENOTTY` in POSIX. + * + * ## `"no-such-device"` + * + * No such device or address, similar to `ENXIO` in POSIX. + * + * ## `"overflow"` + * + * Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX. + * + * ## `"not-permitted"` + * + * Operation not permitted, similar to `EPERM` in POSIX. + * + * ## `"pipe"` + * + * Broken pipe, similar to `EPIPE` in POSIX. + * + * ## `"read-only"` + * + * Read-only file system, similar to `EROFS` in POSIX. + * + * ## `"invalid-seek"` + * + * Invalid seek, similar to `ESPIPE` in POSIX. + * + * ## `"text-file-busy"` + * + * Text file busy, similar to `ETXTBSY` in POSIX. + * + * ## `"cross-device"` + * + * Cross-device link, similar to `EXDEV` in POSIX. + */ +export type ErrorCode = 'access' | 'would-block' | 'already' | 'bad-descriptor' | 'busy' | 'deadlock' | 'quota' | 'exist' | 'file-too-large' | 'illegal-byte-sequence' | 'in-progress' | 'interrupted' | 'invalid' | 'io' | 'is-directory' | 'loop' | 'too-many-links' | 'message-size' | 'name-too-long' | 'no-device' | 'no-entry' | 'no-lock' | 'insufficient-memory' | 'insufficient-space' | 'not-directory' | 'not-empty' | 'not-recoverable' | 'unsupported' | 'no-tty' | 'no-such-device' | 'overflow' | 'not-permitted' | 'pipe' | 'read-only' | 'invalid-seek' | 'text-file-busy' | 'cross-device'; +/** + * A stream of directory entries. + * + * This [represents a stream of `dir-entry`](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Streams). + */ +export type DirectoryEntryStream = number; +/** + * Identifier for a device containing a file system. Can be used in + * combination with `inode` to uniquely identify a file or directory in + * the filesystem. + */ +export type Device = bigint; +/** + * The type of a filesystem object referenced by a descriptor. + * + * Note: This was called `filetype` in earlier versions of WASI. + * + * # Variants + * + * ## `"unknown"` + * + * The type of the descriptor or file is unknown or is different from + * any of the other types specified. + * + * ## `"block-device"` + * + * The descriptor refers to a block device inode. + * + * ## `"character-device"` + * + * The descriptor refers to a character device inode. + * + * ## `"directory"` + * + * The descriptor refers to a directory inode. + * + * ## `"fifo"` + * + * The descriptor refers to a named pipe. + * + * ## `"symbolic-link"` + * + * The file refers to a symbolic link inode. + * + * ## `"regular-file"` + * + * The descriptor refers to a regular file inode. + * + * ## `"socket"` + * + * The descriptor refers to a socket. + */ +export type DescriptorType = 'unknown' | 'block-device' | 'character-device' | 'directory' | 'fifo' | 'symbolic-link' | 'regular-file' | 'socket'; +/** + * A directory entry. + */ +export interface DirectoryEntry { + /** + * The serial number of the object referred to by this directory entry. + * May be none if the inode value is not known. + * + * When this is none, libc implementations might do an extra `stat-at` + * call to retrieve the inode number to fill their `d_ino` fields, so + * implementations which can set this to a non-none value should do so. + */ + inode?: Inode, + /** + * The type of the file referred to by this directory entry. + */ + type: DescriptorType, + /** + * The name of the object. + */ + name: string, +} +/** + * Descriptor flags. + * + * Note: This was called `fdflags` in earlier versions of WASI. + */ +export interface DescriptorFlags { + /** + * Read mode: Data can be read. + */ + read?: boolean, + /** + * Write mode: Data can be written to. + */ + write?: boolean, + /** + * Request that writes be performed according to synchronized I/O file + * integrity completion. The data stored in the file and the file's + * metadata are synchronized. This is similar to `O_SYNC` in POSIX. + * + * The precise semantics of this operation have not yet been defined for + * WASI. At this time, it should be interpreted as a request, and not a + * requirement. + */ + fileIntegritySync?: boolean, + /** + * Request that writes be performed according to synchronized I/O data + * integrity completion. Only the data stored in the file is + * synchronized. This is similar to `O_DSYNC` in POSIX. + * + * The precise semantics of this operation have not yet been defined for + * WASI. At this time, it should be interpreted as a request, and not a + * requirement. + */ + dataIntegritySync?: boolean, + /** + * Requests that reads be performed at the same level of integrety + * requested for writes. This is similar to `O_RSYNC` in POSIX. + * + * The precise semantics of this operation have not yet been defined for + * WASI. At this time, it should be interpreted as a request, and not a + * requirement. + */ + requestedWriteSync?: boolean, + /** + * Mutating directories mode: Directory contents may be mutated. + * + * When this flag is unset on a descriptor, operations using the + * descriptor which would create, rename, delete, modify the data or + * metadata of filesystem objects, or obtain another handle which + * would permit any of those, shall fail with `error-code::read-only` if + * they would otherwise succeed. + * + * This may only be set on directories. + */ + mutateDirectory?: boolean, +} +/** + * A descriptor is a reference to a filesystem object, which may be a file, + * directory, named pipe, special file, or other object on which filesystem + * calls may be made. + * + * This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + */ +export type Descriptor = number; +/** + * When setting a timestamp, this gives the value to set it to. + */ +export type NewTimestamp = NewTimestampNoChange | NewTimestampNow | NewTimestampTimestamp; +/** + * Leave the timestamp set to its previous value. + */ +export interface NewTimestampNoChange { + tag: 'no-change', +} +/** + * Set the timestamp to the current time of the system clock associated + * with the filesystem. + */ +export interface NewTimestampNow { + tag: 'now', +} +/** + * Set the timestamp to the given value. + */ +export interface NewTimestampTimestamp { + tag: 'timestamp', + val: Datetime, +} +/** + * File attributes. + * + * Note: This was called `filestat` in earlier versions of WASI. + */ +export interface DescriptorStat { + /** + * Device ID of device containing the file. + */ + device: Device, + /** + * File serial number. + */ + inode: Inode, + /** + * File type. + */ + type: DescriptorType, + /** + * Number of hard links to the file. + */ + linkCount: LinkCount, + /** + * For regular files, the file size in bytes. For symbolic links, the + * length in bytes of the pathname contained in the symbolic link. + */ + size: Filesize, + /** + * Last data access timestamp. + */ + dataAccessTimestamp: Datetime, + /** + * Last data modification timestamp. + */ + dataModificationTimestamp: Datetime, + /** + * Last file status change timestamp. + */ + statusChangeTimestamp: Datetime, +} +/** + * File or memory access pattern advisory information. + * + * # Variants + * + * ## `"normal"` + * + * The application has no advice to give on its behavior with respect + * to the specified data. + * + * ## `"sequential"` + * + * The application expects to access the specified data sequentially + * from lower offsets to higher offsets. + * + * ## `"random"` + * + * The application expects to access the specified data in a random + * order. + * + * ## `"will-need"` + * + * The application expects to access the specified data in the near + * future. + * + * ## `"dont-need"` + * + * The application expects that it will not access the specified data + * in the near future. + * + * ## `"no-reuse"` + * + * The application expects to access the specified data once and then + * not reuse it thereafter. + */ +export type Advice = 'normal' | 'sequential' | 'random' | 'will-need' | 'dont-need' | 'no-reuse'; +/** + * Access type used by `access-at`. + */ +export type AccessType = AccessTypeAccess | AccessTypeExists; +/** + * Test for readability, writeability, or executability. + */ +export interface AccessTypeAccess { + tag: 'access', + val: Modes, +} +/** + * Test whether the path exists. + */ +export interface AccessTypeExists { + tag: 'exists', +} diff --git a/packages/preview2-shim/types/imports/filesystem.d.ts b/packages/preview2-shim/types/imports/filesystem.d.ts deleted file mode 100644 index 5c3c2d14f..000000000 --- a/packages/preview2-shim/types/imports/filesystem.d.ts +++ /dev/null @@ -1,210 +0,0 @@ -export namespace Filesystem { - export function readViaStream(this: Descriptor, offset: Filesize): InputStream; - export function writeViaStream(this: Descriptor, offset: Filesize): OutputStream; - export function appendViaStream(this: Descriptor): OutputStream; - export function advise(this: Descriptor, offset: Filesize, length: Filesize, advice: Advice): void; - export function syncData(this: Descriptor): void; - export function getFlags(this: Descriptor): DescriptorFlags; - export function getType(this: Descriptor): DescriptorType; - export function setSize(this: Descriptor, size: Filesize): void; - export function setTimes(this: Descriptor, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; - export function read(this: Descriptor, length: Filesize, offset: Filesize): [Uint8Array | ArrayBuffer, boolean]; - export function write(this: Descriptor, buffer: Uint8Array, offset: Filesize): Filesize; - export function readDirectory(this: Descriptor): DirectoryEntryStream; - export function sync(this: Descriptor): void; - export function createDirectoryAt(this: Descriptor, path: string): void; - export function stat(this: Descriptor): DescriptorStat; - export function statAt(this: Descriptor, pathFlags: PathFlags, path: string): DescriptorStat; - export function setTimesAt(this: Descriptor, pathFlags: PathFlags, path: string, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; - export function linkAt(this: Descriptor, oldPathFlags: PathFlags, oldPath: string, newDescriptor: Descriptor, newPath: string): void; - export function openAt(this: Descriptor, pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: DescriptorFlags, modes: Modes): Descriptor; - export function readlinkAt(this: Descriptor, path: string): string; - export function removeDirectoryAt(this: Descriptor, path: string): void; - export function renameAt(this: Descriptor, oldPath: string, newDescriptor: Descriptor, newPath: string): void; - export function symlinkAt(this: Descriptor, oldPath: string, newPath: string): void; - export function unlinkFileAt(this: Descriptor, path: string): void; - export function changeFilePermissionsAt(this: Descriptor, pathFlags: PathFlags, path: string, modes: Modes): void; - export function changeDirectoryPermissionsAt(this: Descriptor, pathFlags: PathFlags, path: string, modes: Modes): void; - export function lockShared(this: Descriptor): void; - export function lockExclusive(this: Descriptor): void; - export function tryLockShared(this: Descriptor): void; - export function tryLockExclusive(this: Descriptor): void; - export function unlock(this: Descriptor): void; - export function dropDescriptor(this: Descriptor): void; - export function readDirectoryEntry(this: DirectoryEntryStream): DirectoryEntry | null; - export function dropDirectoryEntryStream(this: DirectoryEntryStream): void; -} -export type Descriptor = number; -export type Filesize = bigint; -import type { InputStream } from '../imports/streams'; -export { InputStream }; -import type { OutputStream } from '../imports/streams'; -export { OutputStream }; -/** - * # Variants - * - * ## `"normal"` - * - * ## `"sequential"` - * - * ## `"random"` - * - * ## `"will-need"` - * - * ## `"dont-need"` - * - * ## `"no-reuse"` - */ -export type Advice = 'normal' | 'sequential' | 'random' | 'will-need' | 'dont-need' | 'no-reuse'; -/** - * # Variants - * - * ## `"access"` - * - * ## `"would-block"` - * - * ## `"already"` - * - * ## `"bad-descriptor"` - * - * ## `"busy"` - * - * ## `"deadlock"` - * - * ## `"quota"` - * - * ## `"exist"` - * - * ## `"file-too-large"` - * - * ## `"illegal-byte-sequence"` - * - * ## `"in-progress"` - * - * ## `"interrupted"` - * - * ## `"invalid"` - * - * ## `"io"` - * - * ## `"is-directory"` - * - * ## `"loop"` - * - * ## `"too-many-links"` - * - * ## `"message-size"` - * - * ## `"name-too-long"` - * - * ## `"no-device"` - * - * ## `"no-entry"` - * - * ## `"no-lock"` - * - * ## `"insufficient-memory"` - * - * ## `"insufficient-space"` - * - * ## `"not-directory"` - * - * ## `"not-empty"` - * - * ## `"not-recoverable"` - * - * ## `"unsupported"` - * - * ## `"no-tty"` - * - * ## `"no-such-device"` - * - * ## `"overflow"` - * - * ## `"not-permitted"` - * - * ## `"pipe"` - * - * ## `"read-only"` - * - * ## `"invalid-seek"` - * - * ## `"text-file-busy"` - * - * ## `"cross-device"` - */ -export type ErrorCode = 'access' | 'would-block' | 'already' | 'bad-descriptor' | 'busy' | 'deadlock' | 'quota' | 'exist' | 'file-too-large' | 'illegal-byte-sequence' | 'in-progress' | 'interrupted' | 'invalid' | 'io' | 'is-directory' | 'loop' | 'too-many-links' | 'message-size' | 'name-too-long' | 'no-device' | 'no-entry' | 'no-lock' | 'insufficient-memory' | 'insufficient-space' | 'not-directory' | 'not-empty' | 'not-recoverable' | 'unsupported' | 'no-tty' | 'no-such-device' | 'overflow' | 'not-permitted' | 'pipe' | 'read-only' | 'invalid-seek' | 'text-file-busy' | 'cross-device'; -export interface DescriptorFlags { - read?: boolean, - write?: boolean, - fileIntegritySync?: boolean, - dataIntegritySync?: boolean, - requestedWriteSync?: boolean, - mutateDirectory?: boolean, -} -/** - * # Variants - * - * ## `"unknown"` - * - * ## `"block-device"` - * - * ## `"character-device"` - * - * ## `"directory"` - * - * ## `"fifo"` - * - * ## `"symbolic-link"` - * - * ## `"regular-file"` - * - * ## `"socket"` - */ -export type DescriptorType = 'unknown' | 'block-device' | 'character-device' | 'directory' | 'fifo' | 'symbolic-link' | 'regular-file' | 'socket'; -import type { Datetime } from '../imports/wall-clock'; -export { Datetime }; -export type NewTimestamp = NewTimestampNoChange | NewTimestampNow | NewTimestampTimestamp; -export interface NewTimestampNoChange { - tag: 'no-change', -} -export interface NewTimestampNow { - tag: 'now', -} -export interface NewTimestampTimestamp { - tag: 'timestamp', - val: Datetime, -} -export type DirectoryEntryStream = number; -export type Device = bigint; -export type Inode = bigint; -export type LinkCount = bigint; -export interface DescriptorStat { - device: Device, - inode: Inode, - type: DescriptorType, - linkCount: LinkCount, - size: Filesize, - dataAccessTimestamp: Datetime, - dataModificationTimestamp: Datetime, - statusChangeTimestamp: Datetime, -} -export interface PathFlags { - symlinkFollow?: boolean, -} -export interface OpenFlags { - create?: boolean, - directory?: boolean, - exclusive?: boolean, - truncate?: boolean, -} -export interface Modes { - readable?: boolean, - writeable?: boolean, - executable?: boolean, -} -export interface DirectoryEntry { - inode?: Inode, - type: DescriptorType, - name: string, -} diff --git a/packages/preview2-shim/types/imports/default-outgoing-HTTP.d.ts b/packages/preview2-shim/types/imports/http-outgoing-handler.d.ts similarity index 90% rename from packages/preview2-shim/types/imports/default-outgoing-HTTP.d.ts rename to packages/preview2-shim/types/imports/http-outgoing-handler.d.ts index 8cedf49ee..54c7bd013 100644 --- a/packages/preview2-shim/types/imports/default-outgoing-HTTP.d.ts +++ b/packages/preview2-shim/types/imports/http-outgoing-handler.d.ts @@ -1,4 +1,4 @@ -export namespace DefaultOutgoingHttp { +export namespace HttpOutgoingHandler { export function handle(request: OutgoingRequest, options: RequestOptions | null): FutureIncomingResponse; } import type { OutgoingRequest } from '../imports/types'; diff --git a/packages/preview2-shim/types/imports/types.d.ts b/packages/preview2-shim/types/imports/http-types.d.ts similarity index 99% rename from packages/preview2-shim/types/imports/types.d.ts rename to packages/preview2-shim/types/imports/http-types.d.ts index c9a292570..52d189c6c 100644 --- a/packages/preview2-shim/types/imports/types.d.ts +++ b/packages/preview2-shim/types/imports/http-types.d.ts @@ -1,4 +1,4 @@ -export namespace Types { +export namespace HttpTypes { export function dropFields(fields: Fields): void; export function newFields(entries: [string, string][]): Fields; export function fieldsGet(fields: Fields, name: string): string[]; @@ -33,15 +33,32 @@ export namespace Types { export function futureIncomingResponseGet(f: FutureIncomingResponse): Result | null; export function listenToFutureIncomingResponse(f: FutureIncomingResponse): Pollable; } -export type Fields = number; import type { InputStream } from '../imports/streams'; export { InputStream }; -export type IncomingStream = InputStream; -export type Trailers = Fields; import type { OutputStream } from '../imports/streams'; export { OutputStream }; +import type { Pollable } from '../imports/poll'; +export { Pollable }; +export type StatusCode = number; +export type Scheme = SchemeHttp | SchemeHttps | SchemeOther; +export interface SchemeHttp { + tag: 'HTTP', +} +export interface SchemeHttps { + tag: 'HTTPS', +} +export interface SchemeOther { + tag: 'other', + val: string, +} +export type ResponseOutparam = number; +export interface RequestOptions { + connectTimeoutMs?: number, + firstByteTimeoutMs?: number, + betweenBytesTimeoutMs?: number, +} export type OutgoingStream = OutputStream; -export type IncomingRequest = number; +export type OutgoingResponse = number; export type OutgoingRequest = number; export type Method = MethodGet | MethodHead | MethodPost | MethodPut | MethodDelete | MethodConnect | MethodOptions | MethodTrace | MethodPatch | MethodOther; export interface MethodGet { @@ -75,20 +92,13 @@ export interface MethodOther { tag: 'other', val: string, } -export type Scheme = SchemeHttp | SchemeHttps | SchemeOther; -export interface SchemeHttp { - tag: 'HTTP', -} -export interface SchemeHttps { - tag: 'HTTPS', -} -export interface SchemeOther { - tag: 'other', - val: string, -} +export type IncomingStream = InputStream; +export type IncomingResponse = number; +export type IncomingRequest = number; +export type FutureIncomingResponse = number; +export type Fields = number; +export type Trailers = Fields; export type Headers = Fields; -export type ResponseOutparam = number; -export type OutgoingResponse = number; export type Error = ErrorInvalidUrl | ErrorTimeoutError | ErrorProtocolError | ErrorUnexpectedError; export interface ErrorInvalidUrl { tag: 'invalid-url', @@ -106,14 +116,4 @@ export interface ErrorUnexpectedError { tag: 'unexpected-error', val: string, } -export type IncomingResponse = number; -export type StatusCode = number; -export type FutureIncomingResponse = number; -import type { Pollable } from '../imports/poll'; -export { Pollable }; -export interface RequestOptions { - connectTimeoutMs?: number, - firstByteTimeoutMs?: number, - betweenBytesTimeoutMs?: number, -} export type Result = { tag: 'ok', val: T } | { tag: 'err', val: E }; diff --git a/packages/preview2-shim/types/imports/instance-network.d.ts b/packages/preview2-shim/types/imports/instance-network.d.ts deleted file mode 100644 index 5e852cf75..000000000 --- a/packages/preview2-shim/types/imports/instance-network.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export namespace InstanceNetwork { - export function instanceNetwork(): Network; -} -import type { Network } from '../imports/network'; -export { Network }; diff --git a/packages/preview2-shim/types/imports/io-streams.d.ts b/packages/preview2-shim/types/imports/io-streams.d.ts new file mode 100644 index 000000000..e083c4611 --- /dev/null +++ b/packages/preview2-shim/types/imports/io-streams.d.ts @@ -0,0 +1,180 @@ +export namespace IoStreams { + export function /** + * Read bytes from a stream. + * + * This function returns a list of bytes containing the data that was + * read, along with a bool which, when true, indicates that the end of the + * stream was reached. The returned list will contain up to `len` bytes; it + * may return fewer than requested, but not more. + * + * Once a stream has reached the end, subsequent calls to read or + * `skip` will always report end-of-stream rather than producing more + * data. + * + * If `len` is 0, it represents a request to read 0 bytes, which should + * always succeed, assuming the stream hasn't reached its end yet, and + * return an empty list. + * + * The len here is a `u64`, but some callees may not be able to allocate + * a buffer as large as that would imply. + * FIXME: describe what happens if allocation fails. + */ + read(this: InputStream, len: bigint): [Uint8Array | ArrayBuffer, boolean]; + export function /** + * Read bytes from a stream, with blocking. + * + * This is similar to `read`, except that it blocks until at least one + * byte can be read. + */ + blockingRead(this: InputStream, len: bigint): [Uint8Array | ArrayBuffer, boolean]; + export function /** + * Skip bytes from a stream. + * + * This is similar to the `read` function, but avoids copying the + * bytes into the instance. + * + * Once a stream has reached the end, subsequent calls to read or + * `skip` will always report end-of-stream rather than producing more + * data. + * + * This function returns the number of bytes skipped, along with a bool + * indicating whether the end of the stream was reached. The returned + * value will be at most `len`; it may be less. + */ + skip(this: InputStream, len: bigint): [bigint, boolean]; + export function /** + * Skip bytes from a stream, with blocking. + * + * This is similar to `skip`, except that it blocks until at least one + * byte can be consumed. + */ + blockingSkip(this: InputStream, len: bigint): [bigint, boolean]; + export function /** + * Create a `pollable` which will resolve once either the specified stream + * has bytes available to read or the other end of the stream has been + * closed. + */ + subscribeToInputStream(this: InputStream): Pollable; + export function /** + * Dispose of the specified `input-stream`, after which it may no longer + * be used. + */ + dropInputStream(this: InputStream): void; + export function /** + * Write bytes to a stream. + * + * This function returns a `u64` indicating the number of bytes from + * `buf` that were written; it may be less than the full list. + */ + write(this: OutputStream, buf: Uint8Array): bigint; + export function /** + * Write bytes to a stream, with blocking. + * + * This is similar to `write`, except that it blocks until at least one + * byte can be written. + */ + blockingWrite(this: OutputStream, buf: Uint8Array): bigint; + export function /** + * Write multiple zero bytes to a stream. + * + * This function returns a `u64` indicating the number of zero bytes + * that were written; it may be less than `len`. + */ + writeZeroes(this: OutputStream, len: bigint): bigint; + export function /** + * Write multiple zero bytes to a stream, with blocking. + * + * This is similar to `write-zeroes`, except that it blocks until at least + * one byte can be written. + */ + blockingWriteZeroes(this: OutputStream, len: bigint): bigint; + export function /** + * Read from one stream and write to another. + * + * This function returns the number of bytes transferred; it may be less + * than `len`. + * + * Unlike other I/O functions, this function blocks until all the data + * read from the input stream has been written to the output stream. + */ + splice(this: OutputStream, src: InputStream, len: bigint): [bigint, boolean]; + export function /** + * Read from one stream and write to another, with blocking. + * + * This is similar to `splice`, except that it blocks until at least + * one byte can be read. + */ + blockingSplice(this: OutputStream, src: InputStream, len: bigint): [bigint, boolean]; + export function /** + * Forward the entire contents of an input stream to an output stream. + * + * This function repeatedly reads from the input stream and writes + * the data to the output stream, until the end of the input stream + * is reached, or an error is encountered. + * + * Unlike other I/O functions, this function blocks until the end + * of the input stream is seen and all the data has been written to + * the output stream. + * + * This function returns the number of bytes transferred. + */ + forward(this: OutputStream, src: InputStream): bigint; + export function /** + * Create a `pollable` which will resolve once either the specified stream + * is ready to accept bytes or the other end of the stream has been closed. + */ + subscribeToOutputStream(this: OutputStream): Pollable; + export function /** + * Dispose of the specified `output-stream`, after which it may no longer + * be used. + */ + dropOutputStream(this: OutputStream): void; +} +import type { Pollable } from '../imports/poll'; +export { Pollable }; +/** + * An error type returned from a stream operation. Currently this + * doesn't provide any additional information. + */ +export interface StreamError { +} +/** + * An output bytestream. In the future, this will be replaced by handle + * types. + * + * This conceptually represents a `stream`. It's temporary + * scaffolding until component-model's async features are ready. + * + * `output-stream`s are *non-blocking* to the extent practical on + * underlying platforms. Except where specified otherwise, I/O operations also + * always return promptly, after the number of bytes that can be written + * promptly, which could even be zero. To wait for the stream to be ready to + * accept data, the `subscribe-to-output-stream` function to obtain a + * `pollable` which can be polled for using `wasi_poll`. + * + * And at present, it is a `u32` instead of being an actual handle, until + * the wit-bindgen implementation of handles and resources is ready. + * + * This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + */ +export type OutputStream = number; +/** + * An input bytestream. In the future, this will be replaced by handle + * types. + * + * This conceptually represents a `stream`. It's temporary + * scaffolding until component-model's async features are ready. + * + * `input-stream`s are *non-blocking* to the extent practical on underlying + * platforms. I/O operations always return promptly; if fewer bytes are + * promptly available than requested, they return the number of bytes promptly + * available, which could even be zero. To wait for data to be available, + * use the `subscribe-to-input-stream` function to obtain a `pollable` which + * can be polled for using `wasi_poll`. + * + * And at present, it is a `u32` instead of being an actual handle, until + * the wit-bindgen implementation of handles and resources is ready. + * + * This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + */ +export type InputStream = number; diff --git a/packages/preview2-shim/types/imports/ip-name-lookup.d.ts b/packages/preview2-shim/types/imports/ip-name-lookup.d.ts deleted file mode 100644 index 8f486982a..000000000 --- a/packages/preview2-shim/types/imports/ip-name-lookup.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -export namespace IpNameLookup { - export function resolveAddresses(network: Network, name: string, addressFamily: IpAddressFamily | null, includeUnavailable: boolean): ResolveAddressStream; - export function resolveNextAddress(this: ResolveAddressStream): IpAddress | null; - export function dropResolveAddressStream(this: ResolveAddressStream): void; - export function nonBlocking(this: ResolveAddressStream): boolean; - export function setNonBlocking(this: ResolveAddressStream, value: boolean): void; - export function subscribe(this: ResolveAddressStream): Pollable; -} -import type { Network } from '../imports/network'; -export { Network }; -import type { IpAddressFamily } from '../imports/network'; -export { IpAddressFamily }; -export type ResolveAddressStream = number; -import type { Error } from '../imports/network'; -export { Error }; -import type { IpAddress } from '../imports/network'; -export { IpAddress }; -import type { Pollable } from '../imports/poll'; -export { Pollable }; diff --git a/packages/preview2-shim/types/imports/logging-handler.d.ts b/packages/preview2-shim/types/imports/logging-handler.d.ts new file mode 100644 index 000000000..a5ed00d21 --- /dev/null +++ b/packages/preview2-shim/types/imports/logging-handler.d.ts @@ -0,0 +1,40 @@ +export namespace LoggingHandler { + export function /** + * Emit a log message. + * + * A log message has a `level` describing what kind of message is being + * sent, a context, which is an uninterpreted string meant to help + * consumers group similar messages, and a string containing the message + * text. + */ + log(level: Level, context: string, message: string): void; +} +/** + * A log level, describing a kind of message. + * + * # Variants + * + * ## `"trace"` + * + * Describes messages about the values of variables and the flow of + * control within a program. + * + * ## `"debug"` + * + * Describes messages likely to be of interest to someone debugging a + * program. + * + * ## `"info"` + * + * Describes messages likely to be of interest to someone monitoring a + * program. + * + * ## `"warn"` + * + * Describes messages indicating hazardous situations. + * + * ## `"error"` + * + * Describes messages indicating serious errors. + */ +export type Level = 'trace' | 'debug' | 'info' | 'warn' | 'error'; diff --git a/packages/preview2-shim/types/imports/monotonic-clock.d.ts b/packages/preview2-shim/types/imports/monotonic-clock.d.ts deleted file mode 100644 index 13877f078..000000000 --- a/packages/preview2-shim/types/imports/monotonic-clock.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export namespace MonotonicClock { - export function now(): Instant; - export function resolution(): Instant; - export function subscribe(when: Instant, absolute: boolean): Pollable; -} -export type Instant = bigint; -import type { Pollable } from '../imports/poll'; -export { Pollable }; diff --git a/packages/preview2-shim/types/imports/network.d.ts b/packages/preview2-shim/types/imports/network.d.ts deleted file mode 100644 index c7e63d259..000000000 --- a/packages/preview2-shim/types/imports/network.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -export namespace Network { - export function dropNetwork(this: Network): void; -} -export type Network = number; -export type Ipv6Address = [number, number, number, number, number, number, number, number]; -export interface Ipv6SocketAddress { - port: number, - flowInfo: number, - address: Ipv6Address, - scopeId: number, -} -export type Ipv4Address = [number, number, number, number]; -export interface Ipv4SocketAddress { - port: number, - address: Ipv4Address, -} -export type IpSocketAddress = IpSocketAddressIpv4 | IpSocketAddressIpv6; -export interface IpSocketAddressIpv4 { - tag: 'ipv4', - val: Ipv4SocketAddress, -} -export interface IpSocketAddressIpv6 { - tag: 'ipv6', - val: Ipv6SocketAddress, -} -export type IpAddress = IpAddressIpv4 | IpAddressIpv6; -export interface IpAddressIpv4 { - tag: 'ipv4', - val: Ipv4Address, -} -export interface IpAddressIpv6 { - tag: 'ipv6', - val: Ipv6Address, -} -/** - * # Variants - * - * ## `"unknown"` - * - * ## `"again"` - */ -export type Error = 'unknown' | 'again'; -/** - * # Variants - * - * ## `"ipv4"` - * - * ## `"ipv6"` - */ -export type IpAddressFamily = 'ipv4' | 'ipv6'; diff --git a/packages/preview2-shim/types/imports/poll-poll.d.ts b/packages/preview2-shim/types/imports/poll-poll.d.ts new file mode 100644 index 000000000..329891d28 --- /dev/null +++ b/packages/preview2-shim/types/imports/poll-poll.d.ts @@ -0,0 +1,41 @@ +export namespace PollPoll { + export function /** + * Dispose of the specified `pollable`, after which it may no longer + * be used. + */ + dropPollable(this: Pollable): void; + export function /** + * Poll for completion on a set of pollables. + * + * The "oneoff" in the name refers to the fact that this function must do a + * linear scan through the entire list of subscriptions, which may be + * inefficient if the number is large and the same subscriptions are used + * many times. In the future, this is expected to be obsoleted by the + * component model async proposal, which will include a scalable waiting + * facility. + * + * Note that the return type would ideally be `list`, but that would + * be more difficult to polyfill given the current state of `wit-bindgen`. + * See + * for details. For now, we use zero to mean "not ready" and non-zero to + * mean "ready". + */ + pollOneoff(in: Uint32Array): Uint8Array | ArrayBuffer; +} +/** + * A "pollable" handle. + * + * This is conceptually represents a `stream<_, _>`, or in other words, + * a stream that one can wait on, repeatedly, but which does not itself + * produce any data. It's temporary scaffolding until component-model's + * async features are ready. + * + * And at present, it is a `u32` instead of being an actual handle, until + * the wit-bindgen implementation of handles and resources is ready. + * + * `pollable` lifetimes are not automatically managed. Users must ensure + * that they do not outlive the resource they reference. + * + * This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + */ +export type Pollable = number; diff --git a/packages/preview2-shim/types/imports/poll.d.ts b/packages/preview2-shim/types/imports/poll.d.ts deleted file mode 100644 index 4182efe5b..000000000 --- a/packages/preview2-shim/types/imports/poll.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export namespace Poll { - export function dropPollable(this: Pollable): void; - export function pollOneoff(in_: Uint32Array): Uint8Array | ArrayBuffer; -} -export type Pollable = number; diff --git a/packages/preview2-shim/types/imports/random-insecure-seed.d.ts b/packages/preview2-shim/types/imports/random-insecure-seed.d.ts new file mode 100644 index 000000000..cf34f3dac --- /dev/null +++ b/packages/preview2-shim/types/imports/random-insecure-seed.d.ts @@ -0,0 +1,22 @@ +export namespace RandomInsecureSeed { + export function /** + * Return a 128-bit value that may contain a pseudo-random value. + * + * The returned value is not required to be computed from a CSPRNG, and may + * even be entirely deterministic. Host implementations are encouraged to + * provide pseudo-random values to any program exposed to + * attacker-controlled content, to enable DoS protection built into many + * languages' hash-map implementations. + * + * This function is intended to only be called once, by a source language + * to initialize Denial Of Service (DoS) protection in its hash-map + * implementation. + * + * # Expected future evolution + * + * This will likely be changed to a value import, to prevent it from being + * called multiple times and potentially used for purposes other than DoS + * protection. + */ + insecureSeed(): [bigint, bigint]; +} diff --git a/packages/preview2-shim/types/imports/random-insecure.d.ts b/packages/preview2-shim/types/imports/random-insecure.d.ts new file mode 100644 index 000000000..9e5cadb92 --- /dev/null +++ b/packages/preview2-shim/types/imports/random-insecure.d.ts @@ -0,0 +1,20 @@ +export namespace RandomInsecure { + export function /** + * Return `len` insecure pseudo-random bytes. + * + * This function is not cryptographically secure. Do not use it for + * anything related to security. + * + * There are no requirements on the values of the returned bytes, however + * implementations are encouraged to return evenly distributed values with + * a long period. + */ + getInsecureRandomBytes(len: bigint): Uint8Array | ArrayBuffer; + export function /** + * Return an insecure pseudo-random `u64` value. + * + * This function returns the same type of pseudo-random data as + * `get-insecure-random-bytes`, represented as a `u64`. + */ + getInsecureRandomU64(): bigint; +} diff --git a/packages/preview2-shim/types/imports/random-random.d.ts b/packages/preview2-shim/types/imports/random-random.d.ts new file mode 100644 index 000000000..63ef6023a --- /dev/null +++ b/packages/preview2-shim/types/imports/random-random.d.ts @@ -0,0 +1,22 @@ +export namespace RandomRandom { + export function /** + * Return `len` cryptographically-secure pseudo-random bytes. + * + * This function must produce data from an adequately seeded + * cryptographically-secure pseudo-random number generator (CSPRNG), so it + * must not block, from the perspective of the calling program, and the + * returned data is always unpredictable. + * + * This function must always return fresh pseudo-random data. Deterministic + * environments must omit this function, rather than implementing it with + * deterministic data. + */ + getRandomBytes(len: bigint): Uint8Array | ArrayBuffer; + export function /** + * Return a cryptographically-secure pseudo-random `u64` value. + * + * This function returns the same type of pseudo-random data as + * `get-random-bytes`, represented as a `u64`. + */ + getRandomU64(): bigint; +} diff --git a/packages/preview2-shim/types/imports/random.d.ts b/packages/preview2-shim/types/imports/random.d.ts deleted file mode 100644 index 5636224d8..000000000 --- a/packages/preview2-shim/types/imports/random.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export namespace Random { - export function getRandomBytes(len: bigint): Uint8Array | ArrayBuffer; - export function getRandomU64(): bigint; - export function insecureRandom(): [bigint, bigint]; -} diff --git a/packages/preview2-shim/types/imports/sockets-instance-network.d.ts b/packages/preview2-shim/types/imports/sockets-instance-network.d.ts new file mode 100644 index 000000000..c514e0d1d --- /dev/null +++ b/packages/preview2-shim/types/imports/sockets-instance-network.d.ts @@ -0,0 +1,8 @@ +export namespace SocketsInstanceNetwork { + export function /** + * Get a handle to the default network. + */ + instanceNetwork(): Network; +} +import type { Network } from '../imports/network'; +export { Network }; diff --git a/packages/preview2-shim/types/imports/sockets-ip-name-lookup.d.ts b/packages/preview2-shim/types/imports/sockets-ip-name-lookup.d.ts new file mode 100644 index 000000000..c45af8d09 --- /dev/null +++ b/packages/preview2-shim/types/imports/sockets-ip-name-lookup.d.ts @@ -0,0 +1,76 @@ +export namespace SocketsIpNameLookup { + export function /** + * Resolve an internet host name to a list of IP addresses. + * + * See the wasi-socket proposal README.md for a comparison with getaddrinfo. + * + * # Parameters + * - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted + * to ASCII using IDNA encoding. + * - `address-family`: If provided, limit the results to addresses of this specific address family. + * - `include-unavailable`: When set to true, this function will also return addresses of which the runtime + * thinks (or knows) can't be connected to at the moment. For example, this will return IPv6 addresses on + * systems without an active IPv6 interface. Notes: + * - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address. + * - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged. + * + * This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream` + * that can be used to (asynchronously) fetch the results. + * + * At the moment, the stream never completes successfully with 0 items. Ie. the first call + * to `resolve-next-address` never returns `ok(none)`. This may change in the future. + * + * # Typical errors + * - `invalid-name`: `name` is a syntactically invalid domain name. + * - `invalid-name`: `name` is an IP address. + * - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) + * + * # References: + * - + * - + * - + * - + */ + resolveAddresses(network: Network, name: string, addressFamily: IpAddressFamily | null, includeUnavailable: boolean): ResolveAddressStream; + export function /** + * Returns the next address from the resolver. + * + * This function should be called multiple times. On each call, it will + * return the next address in connection order preference. If all + * addresses have been exhausted, this function returns `none`. + * After which, you should release the stream with `drop-resolve-address-stream`. + * + * This function never returns IPv4-mapped IPv6 addresses. + * + * # Typical errors + * - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + * - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + * - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + * - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + */ + resolveNextAddress(this: ResolveAddressStream): IpAddress | null; + export function /** + * Dispose of the specified `resolve-address-stream`, after which it may no longer be used. + * + * Note: this function is scheduled to be removed when Resources are natively supported in Wit. + */ + dropResolveAddressStream(this: ResolveAddressStream): void; + export function /** + * Create a `pollable` which will resolve once the stream is ready for I/O. + * + * Note: this function is here for WASI Preview2 only. + * It's planned to be removed when `future` is natively supported in Preview3. + */ + subscribe(this: ResolveAddressStream): Pollable; +} +import type { Pollable } from '../imports/poll'; +export { Pollable }; +import type { Network } from '../imports/network'; +export { Network }; +import type { ErrorCode } from '../imports/network'; +export { ErrorCode }; +import type { IpAddress } from '../imports/network'; +export { IpAddress }; +import type { IpAddressFamily } from '../imports/network'; +export { IpAddressFamily }; +export type ResolveAddressStream = number; diff --git a/packages/preview2-shim/types/imports/sockets-network.d.ts b/packages/preview2-shim/types/imports/sockets-network.d.ts new file mode 100644 index 000000000..7288285bf --- /dev/null +++ b/packages/preview2-shim/types/imports/sockets-network.d.ts @@ -0,0 +1,212 @@ +export namespace SocketsNetwork { + export function /** + * Dispose of the specified `network`, after which it may no longer be used. + * + * Note: this function is scheduled to be removed when Resources are natively supported in Wit. + */ + dropNetwork(this: Network): void; +} +/** + * An opaque resource that represents access to (a subset of) the network. + * This enables context-based security for networking. + * There is no need for this to map 1:1 to a physical network interface. + * + * FYI, In the future this will be replaced by handle types. + */ +export type Network = number; +export type Ipv6Address = [number, number, number, number, number, number, number, number]; +export interface Ipv6SocketAddress { + port: number, + flowInfo: number, + address: Ipv6Address, + scopeId: number, +} +export type Ipv4Address = [number, number, number, number]; +export interface Ipv4SocketAddress { + port: number, + address: Ipv4Address, +} +export type IpSocketAddress = IpSocketAddressIpv4 | IpSocketAddressIpv6; +export interface IpSocketAddressIpv4 { + tag: 'ipv4', + val: Ipv4SocketAddress, +} +export interface IpSocketAddressIpv6 { + tag: 'ipv6', + val: Ipv6SocketAddress, +} +/** + * # Variants + * + * ## `"ipv4"` + * + * Similar to `AF_INET` in POSIX. + * + * ## `"ipv6"` + * + * Similar to `AF_INET6` in POSIX. + */ +export type IpAddressFamily = 'ipv4' | 'ipv6'; +export type IpAddress = IpAddressIpv4 | IpAddressIpv6; +export interface IpAddressIpv4 { + tag: 'ipv4', + val: Ipv4Address, +} +export interface IpAddressIpv6 { + tag: 'ipv6', + val: Ipv6Address, +} +/** + * Error codes. + * + * In theory, every API can return any error code. + * In practice, API's typically only return the errors documented per API + * combined with a couple of errors that are always possible: + * - `unknown` + * - `access-denied` + * - `not-supported` + * - `out-of-memory` + * + * See each individual API for what the POSIX equivalents are. They sometimes differ per API. + * + * # Variants + * + * ## `"unknown"` + * + * Unknown error + * + * ## `"access-denied"` + * + * Access denied. + * + * POSIX equivalent: EACCES, EPERM + * + * ## `"not-supported"` + * + * The operation is not supported. + * + * POSIX equivalent: EOPNOTSUPP + * + * ## `"out-of-memory"` + * + * Not enough memory to complete the operation. + * + * POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + * + * ## `"timeout"` + * + * The operation timed out before it could finish completely. + * + * ## `"concurrency-conflict"` + * + * This operation is incompatible with another asynchronous operation that is already in progress. + * + * ## `"not-in-progress"` + * + * Trying to finish an asynchronous operation that: + * - has not been started yet, or: + * - was already finished by a previous `finish-*` call. + * + * Note: this is scheduled to be removed when `future`s are natively supported. + * + * ## `"would-block"` + * + * The operation has been aborted because it could not be completed immediately. + * + * Note: this is scheduled to be removed when `future`s are natively supported. + * + * ## `"address-family-not-supported"` + * + * The specified address-family is not supported. + * + * ## `"address-family-mismatch"` + * + * An IPv4 address was passed to an IPv6 resource, or vice versa. + * + * ## `"invalid-remote-address"` + * + * The socket address is not a valid remote address. E.g. the IP address is set to INADDR_ANY, or the port is set to 0. + * + * ## `"ipv4-only-operation"` + * + * The operation is only supported on IPv4 resources. + * + * ## `"ipv6-only-operation"` + * + * The operation is only supported on IPv6 resources. + * + * ## `"new-socket-limit"` + * + * A new socket resource could not be created because of a system limit. + * + * ## `"already-attached"` + * + * The socket is already attached to another network. + * + * ## `"already-bound"` + * + * The socket is already bound. + * + * ## `"already-connected"` + * + * The socket is already in the Connection state. + * + * ## `"not-bound"` + * + * The socket is not bound to any local address. + * + * ## `"not-connected"` + * + * The socket is not in the Connection state. + * + * ## `"address-not-bindable"` + * + * A bind operation failed because the provided address is not an address that the `network` can bind to. + * + * ## `"address-in-use"` + * + * A bind operation failed because the provided address is already in use. + * + * ## `"ephemeral-ports-exhausted"` + * + * A bind operation failed because there are no ephemeral ports available. + * + * ## `"remote-unreachable"` + * + * The remote address is not reachable + * + * ## `"already-listening"` + * + * The socket is already in the Listener state. + * + * ## `"not-listening"` + * + * The socket is already in the Listener state. + * + * ## `"connection-refused"` + * + * The connection was forcefully rejected + * + * ## `"connection-reset"` + * + * The connection was reset. + * + * ## `"datagram-too-large"` + * + * ## `"invalid-name"` + * + * The provided name is a syntactically invalid domain name. + * + * ## `"name-unresolvable"` + * + * Name does not exist or has no suitable associated IP addresses. + * + * ## `"temporary-resolver-failure"` + * + * A temporary failure in name resolution occurred. + * + * ## `"permanent-resolver-failure"` + * + * A permanent failure in name resolution occurred. + */ +export type ErrorCode = 'unknown' | 'access-denied' | 'not-supported' | 'out-of-memory' | 'timeout' | 'concurrency-conflict' | 'not-in-progress' | 'would-block' | 'address-family-not-supported' | 'address-family-mismatch' | 'invalid-remote-address' | 'ipv4-only-operation' | 'ipv6-only-operation' | 'new-socket-limit' | 'already-attached' | 'already-bound' | 'already-connected' | 'not-bound' | 'not-connected' | 'address-not-bindable' | 'address-in-use' | 'ephemeral-ports-exhausted' | 'remote-unreachable' | 'already-listening' | 'not-listening' | 'connection-refused' | 'connection-reset' | 'datagram-too-large' | 'invalid-name' | 'name-unresolvable' | 'temporary-resolver-failure' | 'permanent-resolver-failure'; diff --git a/packages/preview2-shim/types/imports/sockets-tcp-create-socket.d.ts b/packages/preview2-shim/types/imports/sockets-tcp-create-socket.d.ts new file mode 100644 index 000000000..609b5477f --- /dev/null +++ b/packages/preview2-shim/types/imports/sockets-tcp-create-socket.d.ts @@ -0,0 +1,33 @@ +export namespace SocketsTcpCreateSocket { + export function /** + * Create a new TCP socket. + * + * Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. + * + * This function does not require a network capability handle. This is considered to be safe because + * at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` + * is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + * + * All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + * + * # Typical errors + * - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP) + * - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + * - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + * + * # References + * - + * - + * - + * - + */ + createTcpSocket(addressFamily: IpAddressFamily): TcpSocket; +} +import type { Network } from '../imports/network'; +export { Network }; +import type { ErrorCode } from '../imports/network'; +export { ErrorCode }; +import type { IpAddressFamily } from '../imports/network'; +export { IpAddressFamily }; +import type { TcpSocket } from '../imports/tcp'; +export { TcpSocket }; diff --git a/packages/preview2-shim/types/imports/sockets-tcp.d.ts b/packages/preview2-shim/types/imports/sockets-tcp.d.ts new file mode 100644 index 000000000..07d2ae528 --- /dev/null +++ b/packages/preview2-shim/types/imports/sockets-tcp.d.ts @@ -0,0 +1,285 @@ +export namespace SocketsTcp { + export function /** + * Bind the socket to a specific network on the provided IP address and port. + * + * If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + * network interface(s) to bind to. + * If the TCP/UDP port is zero, the socket will be bound to a random free port. + * + * When a socket is not explicitly bound, the first invocation to a listen or connect operation will + * implicitly bind the socket. + * + * Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + * + * # Typical `start` errors + * - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + * - `already-bound`: The socket is already bound. (EINVAL) + * - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + * + * # Typical `finish` errors + * - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + * - `address-in-use`: Address is already in use. (EADDRINUSE) + * - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + * - `not-in-progress`: A `bind` operation is not in progress. + * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + * + * # References + * - + * - + * - + * - + */ + startBind(this: TcpSocket, network: Network, localAddress: IpSocketAddress): void; + export function finishBind(this: TcpSocket): void; + export function /** + * Connect to a remote endpoint. + * + * On success: + * - the socket is transitioned into the Connection state + * - a pair of streams is returned that can be used to read & write to the connection + * + * # Typical `start` errors + * - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + * - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + * - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + * - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + * - `already-connected`: The socket is already in the Connection state. (EISCONN) + * - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) + * - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + * + * # Typical `finish` errors + * - `timeout`: Connection timed out. (ETIMEDOUT) + * - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + * - `connection-reset`: The connection was reset. (ECONNRESET) + * - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + * - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + * - `not-in-progress`: A `connect` operation is not in progress. + * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + * + * # References + * - + * - + * - + * - + */ + startConnect(this: TcpSocket, network: Network, remoteAddress: IpSocketAddress): void; + export function finishConnect(this: TcpSocket): [InputStream, OutputStream]; + export function /** + * Start listening for new connections. + * + * Transitions the socket into the Listener state. + * + * Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + * + * # Typical `start` errors + * - `already-attached`: The socket is already attached to a different network. The `network` passed to `listen` must be identical to the one passed to `bind`. + * - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) + * - `already-listening`: The socket is already in the Listener state. + * - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) + * + * # Typical `finish` errors + * - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + * - `not-in-progress`: A `listen` operation is not in progress. + * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + * + * # References + * - + * - + * - + * - + */ + startListen(this: TcpSocket, network: Network): void; + export function finishListen(this: TcpSocket): void; + export function /** + * Accept a new client socket. + * + * The returned socket is bound and in the Connection state. + * + * On success, this function returns the newly accepted client socket along with + * a pair of streams that can be used to read & write to the connection. + * + * # Typical errors + * - `not-listening`: Socket is not in the Listener state. (EINVAL) + * - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + * + * Host implementations must skip over transient errors returned by the native accept syscall. + * + * # References + * - + * - + * - + * - + */ + accept(this: TcpSocket): [TcpSocket, InputStream, OutputStream]; + export function /** + * Get the bound local address. + * + * # Typical errors + * - `not-bound`: The socket is not bound to any local address. + * + * # References + * - + * - + * - + * - + */ + localAddress(this: TcpSocket): IpSocketAddress; + export function /** + * Get the bound remote address. + * + * # Typical errors + * - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + * + * # References + * - + * - + * - + * - + */ + remoteAddress(this: TcpSocket): IpSocketAddress; + export function /** + * Whether this is a IPv4 or IPv6 socket. + * + * Equivalent to the SO_DOMAIN socket option. + */ + addressFamily(this: TcpSocket): IpAddressFamily; + export function /** + * Whether IPv4 compatibility (dual-stack) mode is disabled or not. + * + * Equivalent to the IPV6_V6ONLY socket option. + * + * # Typical errors + * - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + * - `already-bound`: (set) The socket is already bound. + * - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + */ + ipv6Only(this: TcpSocket): boolean; + export function setIpv6Only(this: TcpSocket, value: boolean): void; + export function /** + * Hints the desired listen queue size. Implementations are free to ignore this. + * + * # Typical errors + * - `already-connected`: (set) The socket is already in the Connection state. + * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + */ + setListenBacklogSize(this: TcpSocket, value: bigint): void; + export function /** + * Equivalent to the SO_KEEPALIVE socket option. + * + * # Typical errors + * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + */ + keepAlive(this: TcpSocket): boolean; + export function setKeepAlive(this: TcpSocket, value: boolean): void; + export function /** + * Equivalent to the TCP_NODELAY socket option. + * + * # Typical errors + * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + */ + noDelay(this: TcpSocket): boolean; + export function setNoDelay(this: TcpSocket, value: boolean): void; + export function /** + * Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + * + * # Typical errors + * - `already-connected`: (set) The socket is already in the Connection state. + * - `already-listening`: (set) The socket is already in the Listener state. + * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + */ + unicastHopLimit(this: TcpSocket): number; + export function setUnicastHopLimit(this: TcpSocket, value: number): void; + export function /** + * The kernel buffer space reserved for sends/receives on this socket. + * + * Note #1: an implementation may choose to cap or round the buffer size when setting the value. + * In other words, after setting a value, reading the same setting back may return a different value. + * + * Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of + * actual data to be sent/received by the application, because the kernel might also use the buffer space + * for internal metadata structures. + * + * Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + * + * # Typical errors + * - `already-connected`: (set) The socket is already in the Connection state. + * - `already-listening`: (set) The socket is already in the Listener state. + * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + */ + receiveBufferSize(this: TcpSocket): bigint; + export function setReceiveBufferSize(this: TcpSocket, value: bigint): void; + export function sendBufferSize(this: TcpSocket): bigint; + export function setSendBufferSize(this: TcpSocket, value: bigint): void; + export function /** + * Create a `pollable` which will resolve once the socket is ready for I/O. + * + * Note: this function is here for WASI Preview2 only. + * It's planned to be removed when `future` is natively supported in Preview3. + */ + subscribe(this: TcpSocket): Pollable; + export function /** + * Initiate a graceful shutdown. + * + * - receive: the socket is not expecting to receive any more data from the peer. All subsequent read + * operations on the `input-stream` associated with this socket will return an End Of Stream indication. + * Any data still in the receive queue at time of calling `shutdown` will be discarded. + * - send: the socket is not expecting to send any more data to the peer. All subsequent write + * operations on the `output-stream` associated with this socket will return an error. + * - both: same effect as receive & send combined. + * + * The shutdown function does not close (drop) the socket. + * + * # Typical errors + * - `not-connected`: The socket is not in the Connection state. (ENOTCONN) + * + * # References + * - + * - + * - + * - + */ + shutdown(this: TcpSocket, shutdownType: ShutdownType): void; + export function /** + * Dispose of the specified `tcp-socket`, after which it may no longer be used. + * + * Similar to the POSIX `close` function. + * + * Note: this function is scheduled to be removed when Resources are natively supported in Wit. + */ + dropTcpSocket(this: TcpSocket): void; +} +import type { InputStream } from '../imports/streams'; +export { InputStream }; +import type { OutputStream } from '../imports/streams'; +export { OutputStream }; +import type { Pollable } from '../imports/poll'; +export { Pollable }; +import type { Network } from '../imports/network'; +export { Network }; +import type { ErrorCode } from '../imports/network'; +export { ErrorCode }; +import type { IpSocketAddress } from '../imports/network'; +export { IpSocketAddress }; +import type { IpAddressFamily } from '../imports/network'; +export { IpAddressFamily }; +/** + * A TCP socket handle. + */ +export type TcpSocket = number; +/** + * # Variants + * + * ## `"receive"` + * + * Similar to `SHUT_RD` in POSIX. + * + * ## `"send"` + * + * Similar to `SHUT_WR` in POSIX. + * + * ## `"both"` + * + * Similar to `SHUT_RDWR` in POSIX. + */ +export type ShutdownType = 'receive' | 'send' | 'both'; diff --git a/packages/preview2-shim/types/imports/sockets-udp-create-socket.d.ts b/packages/preview2-shim/types/imports/sockets-udp-create-socket.d.ts new file mode 100644 index 000000000..8c8d1d0fb --- /dev/null +++ b/packages/preview2-shim/types/imports/sockets-udp-create-socket.d.ts @@ -0,0 +1,33 @@ +export namespace SocketsUdpCreateSocket { + export function /** + * Create a new UDP socket. + * + * Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. + * + * This function does not require a network capability handle. This is considered to be safe because + * at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, + * the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + * + * All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + * + * # Typical errors + * - `not-supported`: The host does not support UDP sockets. (EOPNOTSUPP) + * - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + * - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + * + * # References: + * - + * - + * - + * - + */ + createUdpSocket(addressFamily: IpAddressFamily): UdpSocket; +} +import type { Network } from '../imports/network'; +export { Network }; +import type { ErrorCode } from '../imports/network'; +export { ErrorCode }; +import type { IpAddressFamily } from '../imports/network'; +export { IpAddressFamily }; +import type { UdpSocket } from '../imports/udp'; +export { UdpSocket }; diff --git a/packages/preview2-shim/types/imports/sockets-udp.d.ts b/packages/preview2-shim/types/imports/sockets-udp.d.ts new file mode 100644 index 000000000..b421606f8 --- /dev/null +++ b/packages/preview2-shim/types/imports/sockets-udp.d.ts @@ -0,0 +1,219 @@ +export namespace SocketsUdp { + export function /** + * Bind the socket to a specific network on the provided IP address and port. + * + * If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + * network interface(s) to bind to. + * If the TCP/UDP port is zero, the socket will be bound to a random free port. + * + * When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. + * + * Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + * + * # Typical `start` errors + * - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + * - `already-bound`: The socket is already bound. (EINVAL) + * - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + * + * # Typical `finish` errors + * - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + * - `address-in-use`: Address is already in use. (EADDRINUSE) + * - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + * - `not-in-progress`: A `bind` operation is not in progress. + * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + * + * # References + * - + * - + * - + * - + */ + startBind(this: UdpSocket, network: Network, localAddress: IpSocketAddress): void; + export function finishBind(this: UdpSocket): void; + export function /** + * Set the destination address. + * + * The local-address is updated based on the best network path to `remote-address`. + * + * When a destination address is set: + * - all receive operations will only return datagrams sent from the provided `remote-address`. + * - the `send` function can only be used to send to this destination. + * + * Note that this function does not generate any network traffic and the peer is not aware of this "connection". + * + * Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + * + * # Typical `start` errors + * - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + * - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + * - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + * - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + * - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + * + * # Typical `finish` errors + * - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + * - `not-in-progress`: A `connect` operation is not in progress. + * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + * + * # References + * - + * - + * - + * - + */ + startConnect(this: UdpSocket, network: Network, remoteAddress: IpSocketAddress): void; + export function finishConnect(this: UdpSocket): void; + export function /** + * Receive a message. + * + * Returns: + * - The sender address of the datagram + * - The number of bytes read. + * + * # Typical errors + * - `not-bound`: The socket is not bound to any local address. (EINVAL) + * - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + * - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) + * + * # References + * - + * - + * - + * - + * - + * - + * - + */ + receive(this: UdpSocket): Datagram; + export function /** + * Send a message to a specific destination address. + * + * The remote address option is required. To send a message to the "connected" peer, + * call `remote-address` to get their address. + * + * # Typical errors + * - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + * - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + * - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + * - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) + * - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. + * - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + * - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + * - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) + * + * # References + * - + * - + * - + * - + * - + * - + * - + */ + send(this: UdpSocket, datagram: Datagram): void; + export function /** + * Get the current bound address. + * + * # Typical errors + * - `not-bound`: The socket is not bound to any local address. + * + * # References + * - + * - + * - + * - + */ + localAddress(this: UdpSocket): IpSocketAddress; + export function /** + * Get the address set with `connect`. + * + * # Typical errors + * - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + * + * # References + * - + * - + * - + * - + */ + remoteAddress(this: UdpSocket): IpSocketAddress; + export function /** + * Whether this is a IPv4 or IPv6 socket. + * + * Equivalent to the SO_DOMAIN socket option. + */ + addressFamily(this: UdpSocket): IpAddressFamily; + export function /** + * Whether IPv4 compatibility (dual-stack) mode is disabled or not. + * + * Equivalent to the IPV6_V6ONLY socket option. + * + * # Typical errors + * - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + * - `already-bound`: (set) The socket is already bound. + * - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + * - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + */ + ipv6Only(this: UdpSocket): boolean; + export function setIpv6Only(this: UdpSocket, value: boolean): void; + export function /** + * Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + * + * # Typical errors + * - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + */ + unicastHopLimit(this: UdpSocket): number; + export function setUnicastHopLimit(this: UdpSocket, value: number): void; + export function /** + * The kernel buffer space reserved for sends/receives on this socket. + * + * Note #1: an implementation may choose to cap or round the buffer size when setting the value. + * In other words, after setting a value, reading the same setting back may return a different value. + * + * Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of + * actual data to be sent/received by the application, because the kernel might also use the buffer space + * for internal metadata structures. + * + * Fails when this socket is in the Listening state. + * + * Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + * + * # Typical errors + * - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + */ + receiveBufferSize(this: UdpSocket): bigint; + export function setReceiveBufferSize(this: UdpSocket, value: bigint): void; + export function sendBufferSize(this: UdpSocket): bigint; + export function setSendBufferSize(this: UdpSocket, value: bigint): void; + export function /** + * Create a `pollable` which will resolve once the socket is ready for I/O. + * + * Note: this function is here for WASI Preview2 only. + * It's planned to be removed when `future` is natively supported in Preview3. + */ + subscribe(this: UdpSocket): Pollable; + export function /** + * Dispose of the specified `udp-socket`, after which it may no longer be used. + * + * Note: this function is scheduled to be removed when Resources are natively supported in Wit. + */ + dropUdpSocket(this: UdpSocket): void; +} +import type { Pollable } from '../imports/poll'; +export { Pollable }; +import type { Network } from '../imports/network'; +export { Network }; +import type { ErrorCode } from '../imports/network'; +export { ErrorCode }; +import type { IpSocketAddress } from '../imports/network'; +export { IpSocketAddress }; +import type { IpAddressFamily } from '../imports/network'; +export { IpAddressFamily }; +/** + * A UDP socket handle. + */ +export type UdpSocket = number; +export interface Datagram { + data: Uint8Array, + remoteAddress: IpSocketAddress, +} diff --git a/packages/preview2-shim/types/imports/streams.d.ts b/packages/preview2-shim/types/imports/streams.d.ts deleted file mode 100644 index ade09fd90..000000000 --- a/packages/preview2-shim/types/imports/streams.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -export namespace Streams { - export function read(this: InputStream, len: bigint): [Uint8Array | ArrayBuffer, boolean]; - export function blockingRead(this: InputStream, len: bigint): [Uint8Array | ArrayBuffer, boolean]; - export function skip(this: InputStream, len: bigint): [bigint, boolean]; - export function blockingSkip(this: InputStream, len: bigint): [bigint, boolean]; - export function subscribeToInputStream(this: InputStream): Pollable; - export function dropInputStream(this: InputStream): void; - export function write(this: OutputStream, buf: Uint8Array): bigint; - export function blockingWrite(this: OutputStream, buf: Uint8Array): bigint; - export function writeZeroes(this: OutputStream, len: bigint): bigint; - export function blockingWriteZeroes(this: OutputStream, len: bigint): bigint; - export function splice(this: OutputStream, src: InputStream, len: bigint): [bigint, boolean]; - export function blockingSplice(this: OutputStream, src: InputStream, len: bigint): [bigint, boolean]; - export function forward(this: OutputStream, src: InputStream): bigint; - export function subscribeToOutputStream(this: OutputStream): Pollable; - export function dropOutputStream(this: OutputStream): void; -} -export type InputStream = number; -export interface StreamError { -} -import type { Pollable } from '../imports/poll'; -export { Pollable }; -export type OutputStream = number; diff --git a/packages/preview2-shim/types/imports/tcp-create-socket.d.ts b/packages/preview2-shim/types/imports/tcp-create-socket.d.ts deleted file mode 100644 index b4f53f0f7..000000000 --- a/packages/preview2-shim/types/imports/tcp-create-socket.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export namespace TcpCreateSocket { - export function createTcpSocket(addressFamily: IpAddressFamily): TcpSocket; -} -import type { IpAddressFamily } from '../imports/network'; -export { IpAddressFamily }; -import type { TcpSocket } from '../imports/tcp'; -export { TcpSocket }; -import type { Error } from '../imports/network'; -export { Error }; diff --git a/packages/preview2-shim/types/imports/tcp.d.ts b/packages/preview2-shim/types/imports/tcp.d.ts deleted file mode 100644 index a7d607ab4..000000000 --- a/packages/preview2-shim/types/imports/tcp.d.ts +++ /dev/null @@ -1,52 +0,0 @@ -export namespace Tcp { - export function bind(this: TcpSocket, network: Network, localAddress: IpSocketAddress): void; - export function connect(this: TcpSocket, network: Network, remoteAddress: IpSocketAddress): [InputStream, OutputStream]; - export function listen(this: TcpSocket, network: Network): void; - export function accept(this: TcpSocket): [TcpSocket, InputStream, OutputStream]; - export function localAddress(this: TcpSocket): IpSocketAddress; - export function remoteAddress(this: TcpSocket): IpSocketAddress; - export function addressFamily(this: TcpSocket): IpAddressFamily; - export function ipv6Only(this: TcpSocket): boolean; - export function setIpv6Only(this: TcpSocket, value: boolean): void; - export function setListenBacklogSize(this: TcpSocket, value: bigint): void; - export function keepAlive(this: TcpSocket): boolean; - export function setKeepAlive(this: TcpSocket, value: boolean): void; - export function noDelay(this: TcpSocket): boolean; - export function setNoDelay(this: TcpSocket, value: boolean): void; - export function unicastHopLimit(this: TcpSocket): number; - export function setUnicastHopLimit(this: TcpSocket, value: number): void; - export function receiveBufferSize(this: TcpSocket): bigint; - export function setReceiveBufferSize(this: TcpSocket, value: bigint): void; - export function sendBufferSize(this: TcpSocket): bigint; - export function setSendBufferSize(this: TcpSocket, value: bigint): void; - export function nonBlocking(this: TcpSocket): boolean; - export function setNonBlocking(this: TcpSocket, value: boolean): void; - export function subscribe(this: TcpSocket): Pollable; - export function shutdown(this: TcpSocket, shutdownType: ShutdownType): void; - export function dropTcpSocket(this: TcpSocket): void; -} -export type TcpSocket = number; -import type { Network } from '../imports/network'; -export { Network }; -import type { IpSocketAddress } from '../imports/network'; -export { IpSocketAddress }; -import type { Error } from '../imports/network'; -export { Error }; -import type { InputStream } from '../imports/streams'; -export { InputStream }; -import type { OutputStream } from '../imports/streams'; -export { OutputStream }; -import type { IpAddressFamily } from '../imports/network'; -export { IpAddressFamily }; -import type { Pollable } from '../imports/poll'; -export { Pollable }; -/** - * # Variants - * - * ## `"receive"` - * - * ## `"send"` - * - * ## `"both"` - */ -export type ShutdownType = 'receive' | 'send' | 'both'; diff --git a/packages/preview2-shim/types/imports/timezone.d.ts b/packages/preview2-shim/types/imports/timezone.d.ts deleted file mode 100644 index a3a065375..000000000 --- a/packages/preview2-shim/types/imports/timezone.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -export namespace Timezone { - export function display(this: Timezone, when: Datetime): TimezoneDisplay; - export function utcOffset(this: Timezone, when: Datetime): number; - export function dropTimezone(this: Timezone): void; -} -export type Timezone = number; -import type { Datetime } from '../imports/wall-clock'; -export { Datetime }; -export interface TimezoneDisplay { - utcOffset: number, - name: string, - inDaylightSavingTime: boolean, -} diff --git a/packages/preview2-shim/types/imports/udp-create-socket.d.ts b/packages/preview2-shim/types/imports/udp-create-socket.d.ts deleted file mode 100644 index ddce04657..000000000 --- a/packages/preview2-shim/types/imports/udp-create-socket.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export namespace UdpCreateSocket { - export function createUdpSocket(addressFamily: IpAddressFamily): UdpSocket; -} -import type { IpAddressFamily } from '../imports/network'; -export { IpAddressFamily }; -import type { UdpSocket } from '../imports/udp'; -export { UdpSocket }; -import type { Error } from '../imports/network'; -export { Error }; diff --git a/packages/preview2-shim/types/imports/udp.d.ts b/packages/preview2-shim/types/imports/udp.d.ts deleted file mode 100644 index 31030a5d6..000000000 --- a/packages/preview2-shim/types/imports/udp.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -export namespace Udp { - export function bind(this: UdpSocket, network: Network, localAddress: IpSocketAddress): void; - export function connect(this: UdpSocket, network: Network, remoteAddress: IpSocketAddress): void; - export function receive(this: UdpSocket): Datagram; - export function send(this: UdpSocket, datagram: Datagram): void; - export function localAddress(this: UdpSocket): IpSocketAddress; - export function remoteAddress(this: UdpSocket): IpSocketAddress; - export function addressFamily(this: UdpSocket): IpAddressFamily; - export function ipv6Only(this: UdpSocket): boolean; - export function setIpv6Only(this: UdpSocket, value: boolean): void; - export function unicastHopLimit(this: UdpSocket): number; - export function setUnicastHopLimit(this: UdpSocket, value: number): void; - export function receiveBufferSize(this: UdpSocket): bigint; - export function setReceiveBufferSize(this: UdpSocket, value: bigint): void; - export function sendBufferSize(this: UdpSocket): bigint; - export function setSendBufferSize(this: UdpSocket, value: bigint): void; - export function nonBlocking(this: UdpSocket): boolean; - export function setNonBlocking(this: UdpSocket, value: boolean): void; - export function subscribe(this: UdpSocket): Pollable; - export function dropUdpSocket(this: UdpSocket): void; -} -export type UdpSocket = number; -import type { Network } from '../imports/network'; -export { Network }; -import type { IpSocketAddress } from '../imports/network'; -export { IpSocketAddress }; -import type { Error } from '../imports/network'; -export { Error }; -export interface Datagram { - data: Uint8Array, - remoteAddress: IpSocketAddress, -} -import type { IpAddressFamily } from '../imports/network'; -export { IpAddressFamily }; -import type { Pollable } from '../imports/poll'; -export { Pollable }; diff --git a/packages/preview2-shim/types/imports/wall-clock.d.ts b/packages/preview2-shim/types/imports/wall-clock.d.ts deleted file mode 100644 index 6c96c5b29..000000000 --- a/packages/preview2-shim/types/imports/wall-clock.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export namespace WallClock { - export function now(): Datetime; - export function resolution(): Datetime; -} -export interface Datetime { - seconds: bigint, - nanoseconds: number, -} diff --git a/packages/preview2-shim/types/index.d.ts b/packages/preview2-shim/types/index.d.ts deleted file mode 100644 index 0cf5d647a..000000000 --- a/packages/preview2-shim/types/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ImportObject as WasiProxyImportObject } from "./wasi-proxy"; -import { ImportObject as WasiReactorImportObject } from "./wasi-reactor"; - -export type ImportObject = WasiProxyImportObject & WasiReactorImportObject; - -export declare const importObject: ImportObject; - -export default importObject; diff --git a/packages/preview2-shim/types/wasi-command.d.ts b/packages/preview2-shim/types/wasi-command.d.ts new file mode 100644 index 000000000..b3af6a1f4 --- /dev/null +++ b/packages/preview2-shim/types/wasi-command.d.ts @@ -0,0 +1,23 @@ +import { CliBaseEnvironment as CliBaseEnvironmentImports } from './imports/cli-base-environment'; +import { CliBasePreopens as CliBasePreopensImports } from './imports/cli-base-preopens'; +import { CliBaseExit as CliBaseExitImports } from './imports/cli-base-exit'; +import { CliBaseStdin as CliBaseStdinImports } from './imports/cli-base-stdin'; +import { CliBaseStdout as CliBaseStdoutImports } from './imports/cli-base-stdout'; +import { CliBaseStderr as CliBaseStderrImports } from './imports/cli-base-stderr'; +import { ClocksWallClock as ClocksWallClockImports } from './imports/clocks-wall-clock'; +import { ClocksMonotonicClock as ClocksMonotonicClockImports } from './imports/clocks-monotonic-clock'; +import { ClocksTimezone as ClocksTimezoneImports } from './imports/clocks-timezone'; +import { FilesystemFilesystem as FilesystemFilesystemImports } from './imports/filesystem-filesystem'; +import { IoStreams as IoStreamsImports } from './imports/io-streams'; +import { PollPoll as PollPollImports } from './imports/poll-poll'; +import { RandomRandom as RandomRandomImports } from './imports/random-random'; +import { RandomInsecure as RandomInsecureImports } from './imports/random-insecure'; +import { RandomInsecureSeed as RandomInsecureSeedImports } from './imports/random-insecure-seed'; +import { SocketsNetwork as SocketsNetworkImports } from './imports/sockets-network'; +import { SocketsInstanceNetwork as SocketsInstanceNetworkImports } from './imports/sockets-instance-network'; +import { SocketsIpNameLookup as SocketsIpNameLookupImports } from './imports/sockets-ip-name-lookup'; +import { SocketsTcp as SocketsTcpImports } from './imports/sockets-tcp'; +import { SocketsTcpCreateSocket as SocketsTcpCreateSocketImports } from './imports/sockets-tcp-create-socket'; +import { SocketsUdp as SocketsUdpImports } from './imports/sockets-udp'; +import { SocketsUdpCreateSocket as SocketsUdpCreateSocketImports } from './imports/sockets-udp-create-socket'; +export function run(): void; diff --git a/packages/preview2-shim/types/wasi-proxy.d.ts b/packages/preview2-shim/types/wasi-proxy.d.ts index d66c8b653..afa23fd53 100644 --- a/packages/preview2-shim/types/wasi-proxy.d.ts +++ b/packages/preview2-shim/types/wasi-proxy.d.ts @@ -1,44 +1,11 @@ -import { Random as RandomImports } from './imports/random'; -import { Console as ConsoleImports } from './imports/console'; -import { Poll as PollImports } from './imports/poll'; -import { Streams as StreamsImports } from './imports/streams'; -import { Types as TypesImports } from './imports/types'; -import { DefaultOutgoingHttp as DefaultOutgoingHttpImports } from './imports/default-outgoing-HTTP'; -import { Http as HttpExports } from './exports/HTTP'; -export interface ImportObject { - 'random': typeof RandomImports, - 'console': typeof ConsoleImports, - 'poll': typeof PollImports, - 'streams': typeof StreamsImports, - 'types': typeof TypesImports, - 'default-outgoing-HTTP': typeof DefaultOutgoingHttpImports, -} -export interface WasiProxy { - 'HTTP': typeof HttpExports, -} - -/** -* Instantiates this component with the provided imports and -* returns a map of all the exports of the component. -* -* This function is intended to be similar to the -* `WebAssembly.instantiate` function. The second `imports` -* argument is the "import object" for wasm, except here it -* uses component-model-layer types instead of core wasm -* integers/numbers/etc. -* -* The first argument to this function, `compileCore`, is -* used to compile core wasm modules within the component. -* Components are composed of core wasm modules and this callback -* will be invoked per core wasm module. The caller of this -* function is responsible for reading the core wasm module -* identified by `path` and returning its compiled -* WebAssembly.Module object. This would use `compileStreaming` -* on the web, for example. -*/ -export function instantiate( -compileCore: (path: string, imports: Record) => Promise, -imports: ImportObject, -instantiateCore?: (module: WebAssembly.Module, imports: Record) => Promise -): Promise; - +import { HttpTypes as HttpTypesImports } from './imports/http-types'; +import { HttpOutgoingHandler as HttpOutgoingHandlerImports } from './imports/http-outgoing-handler'; +import { IoStreams as IoStreamsImports } from './imports/io-streams'; +import { LoggingHandler as LoggingHandlerImports } from './imports/logging-handler'; +import { PollPoll as PollPollImports } from './imports/poll-poll'; +import { RandomRandom as RandomRandomImports } from './imports/random-random'; +import { RandomInsecure as RandomInsecureImports } from './imports/random-insecure'; +import { RandomInsecureSeed as RandomInsecureSeedImports } from './imports/random-insecure-seed'; +import { HttpIncomingHandler as HttpIncomingHandlerExports } from './exports/http-incoming-handler'; +export const httpIncomingHandler: typeof HttpIncomingHandlerExports; +export const incomingHandler: typeof HttpIncomingHandlerExports; diff --git a/packages/preview2-shim/types/wasi-reactor.d.ts b/packages/preview2-shim/types/wasi-reactor.d.ts index 8cd6183c5..255ffbda9 100644 --- a/packages/preview2-shim/types/wasi-reactor.d.ts +++ b/packages/preview2-shim/types/wasi-reactor.d.ts @@ -1,70 +1,23 @@ -import { WallClock as WallClockImports } from './imports/wall-clock'; -import { Poll as PollImports } from './imports/poll'; -import { MonotonicClock as MonotonicClockImports } from './imports/monotonic-clock'; -import { Timezone as TimezoneImports } from './imports/timezone'; -import { Streams as StreamsImports } from './imports/streams'; -import { Filesystem as FilesystemImports } from './imports/filesystem'; -import { Network as NetworkImports } from './imports/network'; -import { InstanceNetwork as InstanceNetworkImports } from './imports/instance-network'; -import { IpNameLookup as IpNameLookupImports } from './imports/ip-name-lookup'; -import { Tcp as TcpImports } from './imports/tcp'; -import { TcpCreateSocket as TcpCreateSocketImports } from './imports/tcp-create-socket'; -import { Udp as UdpImports } from './imports/udp'; -import { UdpCreateSocket as UdpCreateSocketImports } from './imports/udp-create-socket'; -import { Random as RandomImports } from './imports/random'; -import { Console as ConsoleImports } from './imports/console'; -import { Types as TypesImports } from './imports/types'; -import { DefaultOutgoingHttp as DefaultOutgoingHttpImports } from './imports/default-outgoing-HTTP'; -import { Environment as EnvironmentImports } from './imports/environment'; -import { Preopens as PreopensImports } from './imports/preopens'; -import { Exit as ExitImports } from './imports/exit'; -export interface ImportObject { - 'wall-clock': typeof WallClockImports, - 'poll': typeof PollImports, - 'monotonic-clock': typeof MonotonicClockImports, - 'timezone': typeof TimezoneImports, - 'streams': typeof StreamsImports, - 'filesystem': typeof FilesystemImports, - 'network': typeof NetworkImports, - 'instance-network': typeof InstanceNetworkImports, - 'ip-name-lookup': typeof IpNameLookupImports, - 'tcp': typeof TcpImports, - 'tcp-create-socket': typeof TcpCreateSocketImports, - 'udp': typeof UdpImports, - 'udp-create-socket': typeof UdpCreateSocketImports, - 'random': typeof RandomImports, - 'console': typeof ConsoleImports, - 'types': typeof TypesImports, - 'default-outgoing-HTTP': typeof DefaultOutgoingHttpImports, - 'environment': typeof EnvironmentImports, - 'preopens': typeof PreopensImports, - 'exit': typeof ExitImports, -} -export interface WasiReactor { -} - -/** -* Instantiates this component with the provided imports and -* returns a map of all the exports of the component. -* -* This function is intended to be similar to the -* `WebAssembly.instantiate` function. The second `imports` -* argument is the "import object" for wasm, except here it -* uses component-model-layer types instead of core wasm -* integers/numbers/etc. -* -* The first argument to this function, `compileCore`, is -* used to compile core wasm modules within the component. -* Components are composed of core wasm modules and this callback -* will be invoked per core wasm module. The caller of this -* function is responsible for reading the core wasm module -* identified by `path` and returning its compiled -* WebAssembly.Module object. This would use `compileStreaming` -* on the web, for example. -*/ -export function instantiate( -compileCore: (path: string, imports: Record) => Promise, -imports: ImportObject, -instantiateCore?: (module: WebAssembly.Module, imports: Record) => Promise -): Promise; - +import { CliBaseEnvironment as CliBaseEnvironmentImports } from './imports/cli-base-environment'; +import { CliBasePreopens as CliBasePreopensImports } from './imports/cli-base-preopens'; +import { CliBaseExit as CliBaseExitImports } from './imports/cli-base-exit'; +import { CliBaseStdin as CliBaseStdinImports } from './imports/cli-base-stdin'; +import { CliBaseStdout as CliBaseStdoutImports } from './imports/cli-base-stdout'; +import { CliBaseStderr as CliBaseStderrImports } from './imports/cli-base-stderr'; +import { ClocksWallClock as ClocksWallClockImports } from './imports/clocks-wall-clock'; +import { ClocksMonotonicClock as ClocksMonotonicClockImports } from './imports/clocks-monotonic-clock'; +import { ClocksTimezone as ClocksTimezoneImports } from './imports/clocks-timezone'; +import { FilesystemFilesystem as FilesystemFilesystemImports } from './imports/filesystem-filesystem'; +import { HttpTypes as HttpTypesImports } from './imports/http-types'; +import { HttpOutgoingHandler as HttpOutgoingHandlerImports } from './imports/http-outgoing-handler'; +import { IoStreams as IoStreamsImports } from './imports/io-streams'; +import { LoggingHandler as LoggingHandlerImports } from './imports/logging-handler'; +import { PollPoll as PollPollImports } from './imports/poll-poll'; +import { RandomRandom as RandomRandomImports } from './imports/random-random'; +import { SocketsNetwork as SocketsNetworkImports } from './imports/sockets-network'; +import { SocketsInstanceNetwork as SocketsInstanceNetworkImports } from './imports/sockets-instance-network'; +import { SocketsIpNameLookup as SocketsIpNameLookupImports } from './imports/sockets-ip-name-lookup'; +import { SocketsTcp as SocketsTcpImports } from './imports/sockets-tcp'; +import { SocketsTcpCreateSocket as SocketsTcpCreateSocketImports } from './imports/sockets-tcp-create-socket'; +import { SocketsUdp as SocketsUdpImports } from './imports/sockets-udp'; +import { SocketsUdpCreateSocket as SocketsUdpCreateSocketImports } from './imports/sockets-udp-create-socket'; diff --git a/test/fixtures/wit/wasi/command-extended.wit b/test/fixtures/wit/wasi/command-extended.wit new file mode 100644 index 000000000..2beb119da --- /dev/null +++ b/test/fixtures/wit/wasi/command-extended.wit @@ -0,0 +1,36 @@ +package wasi:preview + +world command-extended { + import wasi:clocks/wall-clock + import wasi:clocks/monotonic-clock + import wasi:clocks/timezone + import wasi:filesystem/filesystem + import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:sockets/network + import wasi:sockets/tcp-create-socket + import wasi:sockets/tcp + import wasi:sockets/udp-create-socket + import wasi:sockets/udp + import wasi:random/random + import wasi:random/insecure + import wasi:random/insecure-seed + import wasi:poll/poll + import wasi:io/streams + import wasi:cli-base/environment + import wasi:cli-base/preopens + import wasi:cli-base/exit + import wasi:cli-base/stdin + import wasi:cli-base/stdout + import wasi:cli-base/stderr + + // We should replace all others with `include self.command` + // as soon as the unioning of worlds is available: + // https://github.com/WebAssembly/component-model/issues/169 + import wasi:logging/handler + import wasi:http/outgoing-handler + + export run: func( + args: list, + ) -> result +} diff --git a/test/fixtures/wit/wasi/command.wit b/test/fixtures/wit/wasi/command.wit new file mode 100644 index 000000000..62608a05c --- /dev/null +++ b/test/fixtures/wit/wasi/command.wit @@ -0,0 +1,26 @@ +world command { + import wasi:clocks/wall-clock + import wasi:clocks/monotonic-clock + import wasi:clocks/timezone + import wasi:filesystem/filesystem + import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:sockets/network + import wasi:sockets/tcp-create-socket + import wasi:sockets/tcp + import wasi:sockets/udp-create-socket + import wasi:sockets/udp + import wasi:random/random + import wasi:random/insecure + import wasi:random/insecure-seed + import wasi:poll/poll + import wasi:io/streams + import wasi:cli-base/environment + import wasi:cli-base/preopens + import wasi:cli-base/exit + import wasi:cli-base/stdin + import wasi:cli-base/stdout + import wasi:cli-base/stderr + + export run: func() -> result +} diff --git a/test/fixtures/wit/wasi/deps/clocks/monotonic-clock.wit b/test/fixtures/wit/wasi/deps/clocks/monotonic-clock.wit new file mode 100644 index 000000000..50eb4de11 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/clocks/monotonic-clock.wit @@ -0,0 +1,34 @@ +package wasi:clocks + +/// WASI Monotonic Clock is a clock API intended to let users measure elapsed +/// time. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A monotonic clock is a clock which has an unspecified initial value, and +/// successive reads of the clock will produce non-decreasing values. +/// +/// It is intended for measuring elapsed time. +interface monotonic-clock { + use wasi:poll/poll.{pollable} + + /// A timestamp in nanoseconds. + type instant = u64 + + /// Read the current value of the clock. + /// + /// The clock is monotonic, therefore calling this function repeatedly will + /// produce a sequence of non-decreasing values. + now: func() -> instant + + /// Query the resolution of the clock. + resolution: func() -> instant + + /// Create a `pollable` which will resolve once the specified time has been + /// reached. + subscribe: func( + when: instant, + absolute: bool + ) -> pollable +} diff --git a/test/fixtures/wit/wasi/deps/clocks/timezone.wit b/test/fixtures/wit/wasi/deps/clocks/timezone.wit new file mode 100644 index 000000000..2b6855668 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/clocks/timezone.wit @@ -0,0 +1,63 @@ +package wasi:clocks + +interface timezone { + use wall-clock.{datetime} + + /// A timezone. + /// + /// In timezones that recognize daylight saving time, also known as daylight + /// time and summer time, the information returned from the functions varies + /// over time to reflect these adjustments. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type timezone = u32 + + /// Return information needed to display the given `datetime`. This includes + /// the UTC offset, the time zone name, and a flag indicating whether + /// daylight saving time is active. + /// + /// If the timezone cannot be determined for the given `datetime`, return a + /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight + /// saving time. + display: func(this: timezone, when: datetime) -> timezone-display + + /// The same as `display`, but only return the UTC offset. + utc-offset: func(this: timezone, when: datetime) -> s32 + + /// Dispose of the specified input-stream, after which it may no longer + /// be used. + drop-timezone: func(this: timezone) + + /// Information useful for displaying the timezone of a specific `datetime`. + /// + /// This information may vary within a single `timezone` to reflect daylight + /// saving time adjustments. + record timezone-display { + /// The number of seconds difference between UTC time and the local + /// time of the timezone. + /// + /// The returned value will always be less than 86400 which is the + /// number of seconds in a day (24*60*60). + /// + /// In implementations that do not expose an actual time zone, this + /// should return 0. + utc-offset: s32, + + /// The abbreviated name of the timezone to display to a user. The name + /// `UTC` indicates Coordinated Universal Time. Otherwise, this should + /// reference local standards for the name of the time zone. + /// + /// In implementations that do not expose an actual time zone, this + /// should be the string `UTC`. + /// + /// In time zones that do not have an applicable name, a formatted + /// representation of the UTC offset may be returned, such as `-04:00`. + name: string, + + /// Whether daylight saving time is active. + /// + /// In implementations that do not expose an actual time zone, this + /// should return false. + in-daylight-saving-time: bool, + } +} diff --git a/test/fixtures/wit/wasi/deps/clocks/wall-clock.wit b/test/fixtures/wit/wasi/deps/clocks/wall-clock.wit new file mode 100644 index 000000000..6137724f6 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/clocks/wall-clock.wit @@ -0,0 +1,43 @@ +package wasi:clocks + +/// WASI Wall Clock is a clock API intended to let users query the current +/// time. The name "wall" makes an analogy to a "clock on the wall", which +/// is not necessarily monotonic as it may be reset. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A wall clock is a clock which measures the date and time according to +/// some external reference. +/// +/// External references may be reset, so this clock is not necessarily +/// monotonic, making it unsuitable for measuring elapsed time. +/// +/// It is intended for reporting the current date and time for humans. +interface wall-clock { + /// A time and date in seconds plus nanoseconds. + record datetime { + seconds: u64, + nanoseconds: u32, + } + + /// Read the current value of the clock. + /// + /// This clock is not monotonic, therefore calling this function repeatedly + /// will not necessarily produce a sequence of non-decreasing values. + /// + /// The returned timestamps represent the number of seconds since + /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + /// also known as [Unix Time]. + /// + /// The nanoseconds field of the output is always less than 1000000000. + /// + /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + now: func() -> datetime + + /// Query the resolution of the clock. + /// + /// The nanoseconds field of the output is always less than 1000000000. + resolution: func() -> datetime +} diff --git a/test/fixtures/wit/wasi/deps/filesystem/filesystem.wit b/test/fixtures/wit/wasi/deps/filesystem/filesystem.wit new file mode 100644 index 000000000..930f37567 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/filesystem/filesystem.wit @@ -0,0 +1,782 @@ +package wasi:filesystem + +/// WASI filesystem is a filesystem API primarily intended to let users run WASI +/// programs that access their files on their existing filesystems, without +/// significant overhead. +/// +/// It is intended to be roughly portable between Unix-family platforms and +/// Windows, though it does not hide many of the major differences. +/// +/// Paths are passed as interface-type `string`s, meaning they must consist of +/// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain +/// paths which are not accessible by this API. +/// +/// The directory separator in WASI is always the forward-slash (`/`). +/// +/// All paths in WASI are relative paths, and are interpreted relative to a +/// `descriptor` referring to a base directory. If a `path` argument to any WASI +/// function starts with `/`, or if any step of resolving a `path`, including +/// `..` and symbolic link steps, reaches a directory outside of the base +/// directory, or reaches a symlink to an absolute or rooted path in the +/// underlying filesystem, the function fails with `error-code::not-permitted`. +interface filesystem { + use wasi:io/streams.{input-stream, output-stream} + use wasi:clocks/wall-clock.{datetime} + + /// File size or length of a region within a file. + type filesize = u64 + + /// The type of a filesystem object referenced by a descriptor. + /// + /// Note: This was called `filetype` in earlier versions of WASI. + enum descriptor-type { + /// The type of the descriptor or file is unknown or is different from + /// any of the other types specified. + unknown, + /// The descriptor refers to a block device inode. + block-device, + /// The descriptor refers to a character device inode. + character-device, + /// The descriptor refers to a directory inode. + directory, + /// The descriptor refers to a named pipe. + fifo, + /// The file refers to a symbolic link inode. + symbolic-link, + /// The descriptor refers to a regular file inode. + regular-file, + /// The descriptor refers to a socket. + socket, + } + + /// Descriptor flags. + /// + /// Note: This was called `fdflags` in earlier versions of WASI. + flags descriptor-flags { + /// Read mode: Data can be read. + read, + /// Write mode: Data can be written to. + write, + /// Request that writes be performed according to synchronized I/O file + /// integrity completion. The data stored in the file and the file's + /// metadata are synchronized. This is similar to `O_SYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + file-integrity-sync, + /// Request that writes be performed according to synchronized I/O data + /// integrity completion. Only the data stored in the file is + /// synchronized. This is similar to `O_DSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + data-integrity-sync, + /// Requests that reads be performed at the same level of integrety + /// requested for writes. This is similar to `O_RSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + requested-write-sync, + /// Mutating directories mode: Directory contents may be mutated. + /// + /// When this flag is unset on a descriptor, operations using the + /// descriptor which would create, rename, delete, modify the data or + /// metadata of filesystem objects, or obtain another handle which + /// would permit any of those, shall fail with `error-code::read-only` if + /// they would otherwise succeed. + /// + /// This may only be set on directories. + mutate-directory, + } + + /// File attributes. + /// + /// Note: This was called `filestat` in earlier versions of WASI. + record descriptor-stat { + /// Device ID of device containing the file. + device: device, + /// File serial number. + inode: inode, + /// File type. + %type: descriptor-type, + /// Number of hard links to the file. + link-count: link-count, + /// For regular files, the file size in bytes. For symbolic links, the + /// length in bytes of the pathname contained in the symbolic link. + size: filesize, + /// Last data access timestamp. + data-access-timestamp: datetime, + /// Last data modification timestamp. + data-modification-timestamp: datetime, + /// Last file status change timestamp. + status-change-timestamp: datetime, + } + + /// Flags determining the method of how paths are resolved. + flags path-flags { + /// As long as the resolved path corresponds to a symbolic link, it is + /// expanded. + symlink-follow, + } + + /// Open flags used by `open-at`. + flags open-flags { + /// Create file if it does not exist, similar to `O_CREAT` in POSIX. + create, + /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX. + directory, + /// Fail if file already exists, similar to `O_EXCL` in POSIX. + exclusive, + /// Truncate file to size 0, similar to `O_TRUNC` in POSIX. + truncate, + } + + /// Permissions mode used by `open-at`, `change-file-permissions-at`, and + /// similar. + flags modes { + /// True if the resource is considered readable by the containing + /// filesystem. + readable, + /// True if the resource is considered writable by the containing + /// filesystem. + writable, + /// True if the resource is considered executable by the containing + /// filesystem. This does not apply to directories. + executable, + } + + /// Access type used by `access-at`. + variant access-type { + /// Test for readability, writeability, or executability. + access(modes), + + /// Test whether the path exists. + exists, + } + + /// Number of hard links to an inode. + type link-count = u64 + + /// Identifier for a device containing a file system. Can be used in + /// combination with `inode` to uniquely identify a file or directory in + /// the filesystem. + type device = u64 + + /// Filesystem object serial number that is unique within its file system. + type inode = u64 + + /// When setting a timestamp, this gives the value to set it to. + variant new-timestamp { + /// Leave the timestamp set to its previous value. + no-change, + /// Set the timestamp to the current time of the system clock associated + /// with the filesystem. + now, + /// Set the timestamp to the given value. + timestamp(datetime), + } + + /// A directory entry. + record directory-entry { + /// The serial number of the object referred to by this directory entry. + /// May be none if the inode value is not known. + /// + /// When this is none, libc implementations might do an extra `stat-at` + /// call to retrieve the inode number to fill their `d_ino` fields, so + /// implementations which can set this to a non-none value should do so. + inode: option, + + /// The type of the file referred to by this directory entry. + %type: descriptor-type, + + /// The name of the object. + name: string, + } + + /// Error codes returned by functions, similar to `errno` in POSIX. + /// Not all of these error codes are returned by the functions provided by this + /// API; some are used in higher-level library layers, and others are provided + /// merely for alignment with POSIX. + enum error-code { + /// Permission denied, similar to `EACCES` in POSIX. + access, + /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. + would-block, + /// Connection already in progress, similar to `EALREADY` in POSIX. + already, + /// Bad descriptor, similar to `EBADF` in POSIX. + bad-descriptor, + /// Device or resource busy, similar to `EBUSY` in POSIX. + busy, + /// Resource deadlock would occur, similar to `EDEADLK` in POSIX. + deadlock, + /// Storage quota exceeded, similar to `EDQUOT` in POSIX. + quota, + /// File exists, similar to `EEXIST` in POSIX. + exist, + /// File too large, similar to `EFBIG` in POSIX. + file-too-large, + /// Illegal byte sequence, similar to `EILSEQ` in POSIX. + illegal-byte-sequence, + /// Operation in progress, similar to `EINPROGRESS` in POSIX. + in-progress, + /// Interrupted function, similar to `EINTR` in POSIX. + interrupted, + /// Invalid argument, similar to `EINVAL` in POSIX. + invalid, + /// I/O error, similar to `EIO` in POSIX. + io, + /// Is a directory, similar to `EISDIR` in POSIX. + is-directory, + /// Too many levels of symbolic links, similar to `ELOOP` in POSIX. + loop, + /// Too many links, similar to `EMLINK` in POSIX. + too-many-links, + /// Message too large, similar to `EMSGSIZE` in POSIX. + message-size, + /// Filename too long, similar to `ENAMETOOLONG` in POSIX. + name-too-long, + /// No such device, similar to `ENODEV` in POSIX. + no-device, + /// No such file or directory, similar to `ENOENT` in POSIX. + no-entry, + /// No locks available, similar to `ENOLCK` in POSIX. + no-lock, + /// Not enough space, similar to `ENOMEM` in POSIX. + insufficient-memory, + /// No space left on device, similar to `ENOSPC` in POSIX. + insufficient-space, + /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX. + not-directory, + /// Directory not empty, similar to `ENOTEMPTY` in POSIX. + not-empty, + /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX. + not-recoverable, + /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX. + unsupported, + /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX. + no-tty, + /// No such device or address, similar to `ENXIO` in POSIX. + no-such-device, + /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX. + overflow, + /// Operation not permitted, similar to `EPERM` in POSIX. + not-permitted, + /// Broken pipe, similar to `EPIPE` in POSIX. + pipe, + /// Read-only file system, similar to `EROFS` in POSIX. + read-only, + /// Invalid seek, similar to `ESPIPE` in POSIX. + invalid-seek, + /// Text file busy, similar to `ETXTBSY` in POSIX. + text-file-busy, + /// Cross-device link, similar to `EXDEV` in POSIX. + cross-device, + } + + /// File or memory access pattern advisory information. + enum advice { + /// The application has no advice to give on its behavior with respect + /// to the specified data. + normal, + /// The application expects to access the specified data sequentially + /// from lower offsets to higher offsets. + sequential, + /// The application expects to access the specified data in a random + /// order. + random, + /// The application expects to access the specified data in the near + /// future. + will-need, + /// The application expects that it will not access the specified data + /// in the near future. + dont-need, + /// The application expects to access the specified data once and then + /// not reuse it thereafter. + no-reuse, + } + + /// A descriptor is a reference to a filesystem object, which may be a file, + /// directory, named pipe, special file, or other object on which filesystem + /// calls may be made. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type descriptor = u32 + + /// Return a stream for reading from a file. + /// + /// Multiple read, write, and append streams may be active on the same open + /// file and they do not interfere with each other. + /// + /// Note: This allows using `wasi:io/streams.read`, which is similar to `read` in POSIX. + read-via-stream: func( + this: descriptor, + /// The offset within the file at which to start reading. + offset: filesize, + ) -> result + + /// Return a stream for writing to a file. + /// + /// Note: This allows using `wasi:io/streams.write`, which is similar to `write` in + /// POSIX. + write-via-stream: func( + this: descriptor, + /// The offset within the file at which to start writing. + offset: filesize, + ) -> result + + /// Return a stream for appending to a file. + /// + /// Note: This allows using `wasi:io/streams.write`, which is similar to `write` with + /// `O_APPEND` in in POSIX. + append-via-stream: func( + this: descriptor, + ) -> result + + /// Provide file advisory information on a descriptor. + /// + /// This is similar to `posix_fadvise` in POSIX. + advise: func( + this: descriptor, + /// The offset within the file to which the advisory applies. + offset: filesize, + /// The length of the region to which the advisory applies. + length: filesize, + /// The advice. + advice: advice + ) -> result<_, error-code> + + /// Synchronize the data of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fdatasync` in POSIX. + sync-data: func(this: descriptor) -> result<_, error-code> + + /// Get flags associated with a descriptor. + /// + /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + /// + /// Note: This returns the value that was the `fs_flags` value returned + /// from `fdstat_get` in earlier versions of WASI. + get-flags: func(this: descriptor) -> result + + /// Get the dynamic type of a descriptor. + /// + /// Note: This returns the same value as the `type` field of the `fd-stat` + /// returned by `stat`, `stat-at` and similar. + /// + /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided + /// by `fstat` in POSIX. + /// + /// Note: This returns the value that was the `fs_filetype` value returned + /// from `fdstat_get` in earlier versions of WASI. + get-type: func(this: descriptor) -> result + + /// Adjust the size of an open file. If this increases the file's size, the + /// extra bytes are filled with zeros. + /// + /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + set-size: func(this: descriptor, size: filesize) -> result<_, error-code> + + /// Adjust the timestamps of an open file or directory. + /// + /// Note: This is similar to `futimens` in POSIX. + /// + /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + set-times: func( + this: descriptor, + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + /// The desired values of the data modification timestamp. + data-modification-timestamp: new-timestamp, + ) -> result<_, error-code> + + /// Read from a descriptor, without using and updating the descriptor's offset. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// file was reached. The returned list will contain up to `length` bytes; it + /// may return fewer than requested, if the end of the file is reached or + /// if the I/O operation is interrupted. + /// + /// In the future, this may change to return a `stream`. + /// + /// Note: This is similar to `pread` in POSIX. + read: func( + this: descriptor, + /// The maximum number of bytes to read. + length: filesize, + /// The offset within the file at which to read. + offset: filesize, + ) -> result, bool>, error-code> + + /// Write to a descriptor, without using and updating the descriptor's offset. + /// + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// In the future, this may change to take a `stream`. + /// + /// Note: This is similar to `pwrite` in POSIX. + write: func( + this: descriptor, + /// Data to write + buffer: list, + /// The offset within the file at which to write. + offset: filesize, + ) -> result + + /// Read directory entries from a directory. + /// + /// On filesystems where directories contain entries referring to themselves + /// and their parents, often named `.` and `..` respectively, these entries + /// are omitted. + /// + /// This always returns a new stream which starts at the beginning of the + /// directory. Multiple streams may be active on the same directory, and they + /// do not interfere with each other. + read-directory: func( + this: descriptor + ) -> result + + /// Synchronize the data and metadata of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fsync` in POSIX. + sync: func(this: descriptor) -> result<_, error-code> + + /// Create a directory. + /// + /// Note: This is similar to `mkdirat` in POSIX. + create-directory-at: func( + this: descriptor, + /// The relative path at which to create the directory. + path: string, + ) -> result<_, error-code> + + /// Return the attributes of an open file or directory. + /// + /// Note: This is similar to `fstat` in POSIX. + /// + /// Note: This was called `fd_filestat_get` in earlier versions of WASI. + stat: func(this: descriptor) -> result + + /// Return the attributes of a file or directory. + /// + /// Note: This is similar to `fstatat` in POSIX. + /// + /// Note: This was called `path_filestat_get` in earlier versions of WASI. + stat-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to inspect. + path: string, + ) -> result + + /// Adjust the timestamps of a file or directory. + /// + /// Note: This is similar to `utimensat` in POSIX. + /// + /// Note: This was called `path_filestat_set_times` in earlier versions of + /// WASI. + set-times-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the file or directory to operate on. + path: string, + /// The desired values of the data access timestamp. + data-access-timestamp: new-timestamp, + /// The desired values of the data modification timestamp. + data-modification-timestamp: new-timestamp, + ) -> result<_, error-code> + + /// Create a hard link. + /// + /// Note: This is similar to `linkat` in POSIX. + link-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + old-path-flags: path-flags, + /// The relative source path from which to link. + old-path: string, + /// The base directory for `new-path`. + new-descriptor: descriptor, + /// The relative destination path at which to create the hard link. + new-path: string, + ) -> result<_, error-code> + + /// Open a file or directory. + /// + /// The returned descriptor is not guaranteed to be the lowest-numbered + /// descriptor not currently open/ it is randomized to prevent applications + /// from depending on making assumptions about indexes, since this is + /// error-prone in multi-threaded contexts. The returned descriptor is + /// guaranteed to be less than 2**31. + /// + /// If `flags` contains `descriptor-flags::mutate-directory`, and the base + /// descriptor doesn't have `descriptor-flags::mutate-directory` set, + /// `open-at` fails with `error-code::read-only`. + /// + /// If `flags` contains `write` or `mutate-directory`, or `open-flags` + /// contains `truncate` or `create`, and the base descriptor doesn't have + /// `descriptor-flags::mutate-directory` set, `open-at` fails with + /// `error-code::read-only`. + /// + /// Note: This is similar to `openat` in POSIX. + open-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path of the object to open. + path: string, + /// The method by which to open the file. + open-flags: open-flags, + /// Flags to use for the resulting descriptor. + %flags: descriptor-flags, + /// Permissions to use when creating a new file. + modes: modes + ) -> result + + /// Read the contents of a symbolic link. + /// + /// If the contents contain an absolute or rooted path in the underlying + /// filesystem, this function fails with `error-code::not-permitted`. + /// + /// Note: This is similar to `readlinkat` in POSIX. + readlink-at: func( + this: descriptor, + /// The relative path of the symbolic link from which to read. + path: string, + ) -> result + + /// Remove a directory. + /// + /// Return `error-code::not-empty` if the directory is not empty. + /// + /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + remove-directory-at: func( + this: descriptor, + /// The relative path to a directory to remove. + path: string, + ) -> result<_, error-code> + + /// Rename a filesystem object. + /// + /// Note: This is similar to `renameat` in POSIX. + rename-at: func( + this: descriptor, + /// The relative source path of the file or directory to rename. + old-path: string, + /// The base directory for `new-path`. + new-descriptor: descriptor, + /// The relative destination path to which to rename the file or directory. + new-path: string, + ) -> result<_, error-code> + + /// Create a symbolic link (also known as a "symlink"). + /// + /// If `old-path` starts with `/`, the function fails with + /// `error-code::not-permitted`. + /// + /// Note: This is similar to `symlinkat` in POSIX. + symlink-at: func( + this: descriptor, + /// The contents of the symbolic link. + old-path: string, + /// The relative destination path at which to create the symbolic link. + new-path: string, + ) -> result<_, error-code> + + /// Check accessibility of a filesystem path. + /// + /// Check whether the given filesystem path names an object which is + /// readable, writable, or executable, or whether it exists. + /// + /// This does not a guarantee that subsequent accesses will succeed, as + /// filesystem permissions may be modified asynchronously by external + /// entities. + /// + /// Note: This is similar to `faccessat` with the `AT_EACCESS` flag in POSIX. + access-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to check. + path: string, + /// The type of check to perform. + %type: access-type + ) -> result<_, error-code> + + /// Unlink a filesystem object that is not a directory. + /// + /// Return `error-code::is-directory` if the path refers to a directory. + /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + unlink-file-at: func( + this: descriptor, + /// The relative path to a file to unlink. + path: string, + ) -> result<_, error-code> + + /// Change the permissions of a filesystem object that is not a directory. + /// + /// Note that the ultimate meanings of these permissions is + /// filesystem-specific. + /// + /// Note: This is similar to `fchmodat` in POSIX. + change-file-permissions-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to operate on. + path: string, + /// The new permissions for the filesystem object. + modes: modes, + ) -> result<_, error-code> + + /// Change the permissions of a directory. + /// + /// Note that the ultimate meanings of these permissions is + /// filesystem-specific. + /// + /// Unlike in POSIX, the `executable` flag is not reinterpreted as a "search" + /// flag. `read` on a directory implies readability and searchability, and + /// `execute` is not valid for directories. + /// + /// Note: This is similar to `fchmodat` in POSIX. + change-directory-permissions-at: func( + this: descriptor, + /// Flags determining the method of how the path is resolved. + path-flags: path-flags, + /// The relative path to operate on. + path: string, + /// The new permissions for the directory. + modes: modes, + ) -> result<_, error-code> + + /// Request a shared advisory lock for an open file. + /// + /// This requests a *shared* lock; more than one shared lock can be held for + /// a file at the same time. + /// + /// If the open file has an exclusive lock, this function downgrades the lock + /// to a shared lock. If it has a shared lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified how shared locks interact with locks acquired by + /// non-WASI programs. + /// + /// This function blocks until the lock can be acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_SH)` in Unix. + lock-shared: func(this: descriptor) -> result<_, error-code> + + /// Request an exclusive advisory lock for an open file. + /// + /// This requests an *exclusive* lock; no other locks may be held for the + /// file while an exclusive lock is held. + /// + /// If the open file has a shared lock and there are no exclusive locks held + /// for the file, this function upgrades the lock to an exclusive lock. If the + /// open file already has an exclusive lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified whether this function succeeds if the file descriptor + /// is not opened for writing. It is unspecified how exclusive locks interact + /// with locks acquired by non-WASI programs. + /// + /// This function blocks until the lock can be acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_EX)` in Unix. + lock-exclusive: func(this: descriptor) -> result<_, error-code> + + /// Request a shared advisory lock for an open file. + /// + /// This requests a *shared* lock; more than one shared lock can be held for + /// a file at the same time. + /// + /// If the open file has an exclusive lock, this function downgrades the lock + /// to a shared lock. If it has a shared lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified how shared locks interact with locks acquired by + /// non-WASI programs. + /// + /// This function returns `error-code::would-block` if the lock cannot be + /// acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. + try-lock-shared: func(this: descriptor) -> result<_, error-code> + + /// Request an exclusive advisory lock for an open file. + /// + /// This requests an *exclusive* lock; no other locks may be held for the + /// file while an exclusive lock is held. + /// + /// If the open file has a shared lock and there are no exclusive locks held + /// for the file, this function upgrades the lock to an exclusive lock. If the + /// open file already has an exclusive lock, this function has no effect. + /// + /// This requests an *advisory* lock, meaning that the file could be accessed + /// by other programs that don't hold the lock. + /// + /// It is unspecified whether this function succeeds if the file descriptor + /// is not opened for writing. It is unspecified how exclusive locks interact + /// with locks acquired by non-WASI programs. + /// + /// This function returns `error-code::would-block` if the lock cannot be + /// acquired. + /// + /// Not all filesystems support locking; on filesystems which don't support + /// locking, this function returns `error-code::unsupported`. + /// + /// Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. + try-lock-exclusive: func(this: descriptor) -> result<_, error-code> + + /// Release a shared or exclusive lock on an open file. + /// + /// Note: This is similar to `flock(fd, LOCK_UN)` in Unix. + unlock: func(this: descriptor) -> result<_, error-code> + + /// Dispose of the specified `descriptor`, after which it may no longer + /// be used. + drop-descriptor: func(this: descriptor) + + /// A stream of directory entries. + /// + /// This [represents a stream of `dir-entry`](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Streams). + type directory-entry-stream = u32 + + /// Read a single directory entry from a `directory-entry-stream`. + read-directory-entry: func( + this: directory-entry-stream + ) -> result, error-code> + + /// Dispose of the specified `directory-entry-stream`, after which it may no longer + /// be used. + drop-directory-entry-stream: func(this: directory-entry-stream) +} diff --git a/test/fixtures/wit/wasi/deps/http/incoming-handler.wit b/test/fixtures/wit/wasi/deps/http/incoming-handler.wit new file mode 100644 index 000000000..d0e270465 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/http/incoming-handler.wit @@ -0,0 +1,24 @@ +// The `wasi:http/incoming-handler` interface is meant to be exported by +// components and called by the host in response to a new incoming HTTP +// response. +// +// NOTE: in Preview3, this interface will be merged with +// `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface +// that takes a `request` parameter and returns a `response` result. +// +interface incoming-handler { + use types.{incoming-request, response-outparam} + + // The `handle` function takes an outparam instead of returning its response + // so that the component may stream its response while streaming any other + // request or response bodies. The callee MUST write a response to the + // `response-out` and then finish the response before returning. The `handle` + // function is allowed to continue execution after finishing the response's + // output stream. While this post-response execution is taken off the + // critical path, since there is no return value, there is no way to report + // its success or failure. + handle: func( + request: incoming-request, + response-out: response-outparam + ) +} diff --git a/test/fixtures/wit/wasi/deps/http/outgoing-handler.wit b/test/fixtures/wit/wasi/deps/http/outgoing-handler.wit new file mode 100644 index 000000000..06c8e469f --- /dev/null +++ b/test/fixtures/wit/wasi/deps/http/outgoing-handler.wit @@ -0,0 +1,18 @@ +// The `wasi:http/outgoing-handler` interface is meant to be imported by +// components and implemented by the host. +// +// NOTE: in Preview3, this interface will be merged with +// `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface +// that takes a `request` parameter and returns a `response` result. +// +interface outgoing-handler { + use types.{outgoing-request, request-options, future-incoming-response} + + // The parameter and result types of the `handle` function allow the caller + // to concurrently stream the bodies of the outgoing request and the incoming + // response. + handle: func( + request: outgoing-request, + options: option + ) -> future-incoming-response +} diff --git a/test/fixtures/wit/wasi/deps/http/types.wit b/test/fixtures/wit/wasi/deps/http/types.wit new file mode 100644 index 000000000..ee4227f42 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/http/types.wit @@ -0,0 +1,159 @@ +package wasi:http + +// The `wasi:http/types` interface is meant to be imported by components to +// define the HTTP resource types and operations used by the component's +// imported and exported interfaces. +interface types { + use wasi:io/streams.{input-stream, output-stream} + use wasi:poll/poll.{pollable} + + // This type corresponds to HTTP standard Methods. + variant method { + get, + head, + post, + put, + delete, + connect, + options, + trace, + patch, + other(string) + } + + // This type corresponds to HTTP standard Related Schemes. + variant scheme { + HTTP, + HTTPS, + other(string) + } + + // TODO: perhaps better align with HTTP semantics? + // This type enumerates the different kinds of errors that may occur when + // initially returning a response. + variant error { + invalid-url(string), + timeout-error(string), + protocol-error(string), + unexpected-error(string) + } + + // This following block defines the `fields` resource which corresponds to + // HTTP standard Fields. Soon, when resource types are added, the `type + // fields = u32` type alias can be replaced by a proper `resource fields` + // definition containing all the functions using the method syntactic sugar. + type fields = u32 + drop-fields: func(fields: fields) + new-fields: func(entries: list>) -> fields + fields-get: func(fields: fields, name: string) -> list + fields-set: func(fields: fields, name: string, value: list) + fields-delete: func(fields: fields, name: string) + fields-append: func(fields: fields, name: string, value: string) + fields-entries: func(fields: fields) -> list> + fields-clone: func(fields: fields) -> fields + + type headers = fields + type trailers = fields + + // The following block defines stream types which corresponds to the HTTP + // standard Contents and Trailers. With Preview3, all of these fields can be + // replaced by a stream>. In the interim, we need to + // build on separate resource types defined by `wasi:io/streams`. The + // `finish-` functions emulate the stream's result value and MUST be called + // exactly once after the final read/write from/to the stream before dropping + // the stream. + type incoming-stream = input-stream + type outgoing-stream = output-stream + finish-incoming-stream: func(s: incoming-stream) -> option + finish-outgoing-stream: func(s: outgoing-stream, trailers: option) + + // The following block defines the `incoming-request` and `outgoing-request` + // resource types that correspond to HTTP standard Requests. Soon, when + // resource types are added, the `u32` type aliases can be replaced by + // proper `resource` type definitions containing all the functions as + // methods. Later, Preview2 will allow both types to be merged together into + // a single `request` type (that uses the single `stream` type mentioned + // above). The `consume` and `write` methods may only be called once (and + // return failure thereafter). + type incoming-request = u32 + type outgoing-request = u32 + drop-incoming-request: func(request: incoming-request) + drop-outgoing-request: func(request: outgoing-request) + incoming-request-method: func(request: incoming-request) -> method + incoming-request-path: func(request: incoming-request) -> string + incoming-request-query: func(request: incoming-request) -> string + incoming-request-scheme: func(request: incoming-request) -> option + incoming-request-authority: func(request: incoming-request) -> string + incoming-request-headers: func(request: incoming-request) -> headers + incoming-request-consume: func(request: incoming-request) -> result + new-outgoing-request: func( + method: method, + path: string, + query: string, + scheme: option, + authority: string, + headers: headers + ) -> outgoing-request + outgoing-request-write: func(request: outgoing-request) -> result + + // Additional optional parameters that can be set when making a request. + record request-options { + // The following timeouts are specific to the HTTP protocol and work + // independently of the overall timeouts passed to `io.poll.poll-oneoff`. + + // The timeout for the initial connect. + connect-timeout-ms: option, + + // The timeout for receiving the first byte of the response body. + first-byte-timeout-ms: option, + + // The timeout for receiving the next chunk of bytes in the response body + // stream. + between-bytes-timeout-ms: option + } + + // The following block defines a special resource type used by the + // `wasi:http/incoming-handler` interface. When resource types are added, this + // block can be replaced by a proper `resource response-outparam { ... }` + // definition. Later, with Preview3, the need for an outparam goes away entirely + // (the `wasi:http/handler` interface used for both incoming and outgoing can + // simply return a `stream`). + type response-outparam = u32 + drop-response-outparam: func(response: response-outparam) + set-response-outparam: func(response: result) -> result + + // This type corresponds to the HTTP standard Status Code. + type status-code = u16 + + // The following block defines the `incoming-response` and `outgoing-response` + // resource types that correspond to HTTP standard Responses. Soon, when + // resource types are added, the `u32` type aliases can be replaced by proper + // `resource` type definitions containing all the functions as methods. Later, + // Preview2 will allow both types to be merged together into a single `response` + // type (that uses the single `stream` type mentioned above). The `consume` and + // `write` methods may only be called once (and return failure thereafter). + type incoming-response = u32 + type outgoing-response = u32 + drop-incoming-response: func(response: incoming-response) + drop-outgoing-response: func(response: outgoing-response) + incoming-response-status: func(response: incoming-response) -> status-code + incoming-response-headers: func(response: incoming-response) -> headers + incoming-response-consume: func(response: incoming-response) -> result + new-outgoing-response: func( + status-code: status-code, + headers: headers + ) -> outgoing-response + outgoing-response-write: func(response: outgoing-response) -> result + + // The following block defines a special resource type used by the + // `wasi:http/outgoing-handler` interface to emulate + // `future>` in advance of Preview3. Given a + // `future-incoming-response`, the client can call the non-blocking `get` + // method to get the result if it is available. If the result is not available, + // the client can call `listen` to get a `pollable` that can be passed to + // `io.poll.poll-oneoff`. + type future-incoming-response = u32 + drop-future-incoming-response: func(f: future-incoming-response) + future-incoming-response-get: func(f: future-incoming-response) -> option> + listen-to-future-incoming-response: func(f: future-incoming-response) -> pollable +} diff --git a/test/fixtures/wit/wasi/deps/io/streams.wit b/test/fixtures/wit/wasi/deps/io/streams.wit new file mode 100644 index 000000000..008e36cf5 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/io/streams.wit @@ -0,0 +1,215 @@ +package wasi:io + +/// WASI I/O is an I/O abstraction API which is currently focused on providing +/// stream types. +/// +/// In the future, the component model is expected to add built-in stream types; +/// when it does, they are expected to subsume this API. +interface streams { + use wasi:poll/poll.{pollable} + + /// An error type returned from a stream operation. Currently this + /// doesn't provide any additional information. + record stream-error {} + + /// An input bytestream. In the future, this will be replaced by handle + /// types. + /// + /// This conceptually represents a `stream`. It's temporary + /// scaffolding until component-model's async features are ready. + /// + /// `input-stream`s are *non-blocking* to the extent practical on underlying + /// platforms. I/O operations always return promptly; if fewer bytes are + /// promptly available than requested, they return the number of bytes promptly + /// available, which could even be zero. To wait for data to be available, + /// use the `subscribe-to-input-stream` function to obtain a `pollable` which + /// can be polled for using `wasi_poll`. + /// + /// And at present, it is a `u32` instead of being an actual handle, until + /// the wit-bindgen implementation of handles and resources is ready. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type input-stream = u32 + + /// Read bytes from a stream. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// stream was reached. The returned list will contain up to `len` bytes; it + /// may return fewer than requested, but not more. + /// + /// Once a stream has reached the end, subsequent calls to read or + /// `skip` will always report end-of-stream rather than producing more + /// data. + /// + /// If `len` is 0, it represents a request to read 0 bytes, which should + /// always succeed, assuming the stream hasn't reached its end yet, and + /// return an empty list. + /// + /// The len here is a `u64`, but some callees may not be able to allocate + /// a buffer as large as that would imply. + /// FIXME: describe what happens if allocation fails. + read: func( + this: input-stream, + /// The maximum number of bytes to read + len: u64 + ) -> result, bool>, stream-error> + + /// Read bytes from a stream, with blocking. + /// + /// This is similar to `read`, except that it blocks until at least one + /// byte can be read. + blocking-read: func( + this: input-stream, + /// The maximum number of bytes to read + len: u64 + ) -> result, bool>, stream-error> + + /// Skip bytes from a stream. + /// + /// This is similar to the `read` function, but avoids copying the + /// bytes into the instance. + /// + /// Once a stream has reached the end, subsequent calls to read or + /// `skip` will always report end-of-stream rather than producing more + /// data. + /// + /// This function returns the number of bytes skipped, along with a bool + /// indicating whether the end of the stream was reached. The returned + /// value will be at most `len`; it may be less. + skip: func( + this: input-stream, + /// The maximum number of bytes to skip. + len: u64, + ) -> result, stream-error> + + /// Skip bytes from a stream, with blocking. + /// + /// This is similar to `skip`, except that it blocks until at least one + /// byte can be consumed. + blocking-skip: func( + this: input-stream, + /// The maximum number of bytes to skip. + len: u64, + ) -> result, stream-error> + + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + subscribe-to-input-stream: func(this: input-stream) -> pollable + + /// Dispose of the specified `input-stream`, after which it may no longer + /// be used. + drop-input-stream: func(this: input-stream) + + /// An output bytestream. In the future, this will be replaced by handle + /// types. + /// + /// This conceptually represents a `stream`. It's temporary + /// scaffolding until component-model's async features are ready. + /// + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe-to-output-stream` function to obtain a + /// `pollable` which can be polled for using `wasi_poll`. + /// + /// And at present, it is a `u32` instead of being an actual handle, until + /// the wit-bindgen implementation of handles and resources is ready. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type output-stream = u32 + + /// Write bytes to a stream. + /// + /// This function returns a `u64` indicating the number of bytes from + /// `buf` that were written; it may be less than the full list. + write: func( + this: output-stream, + /// Data to write + buf: list + ) -> result + + /// Write bytes to a stream, with blocking. + /// + /// This is similar to `write`, except that it blocks until at least one + /// byte can be written. + blocking-write: func( + this: output-stream, + /// Data to write + buf: list + ) -> result + + /// Write multiple zero bytes to a stream. + /// + /// This function returns a `u64` indicating the number of zero bytes + /// that were written; it may be less than `len`. + write-zeroes: func( + this: output-stream, + /// The number of zero bytes to write + len: u64 + ) -> result + + /// Write multiple zero bytes to a stream, with blocking. + /// + /// This is similar to `write-zeroes`, except that it blocks until at least + /// one byte can be written. + blocking-write-zeroes: func( + this: output-stream, + /// The number of zero bytes to write + len: u64 + ) -> result + + /// Read from one stream and write to another. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + /// + /// Unlike other I/O functions, this function blocks until all the data + /// read from the input stream has been written to the output stream. + splice: func( + this: output-stream, + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result, stream-error> + + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until at least + /// one byte can be read. + blocking-splice: func( + this: output-stream, + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result, stream-error> + + /// Forward the entire contents of an input stream to an output stream. + /// + /// This function repeatedly reads from the input stream and writes + /// the data to the output stream, until the end of the input stream + /// is reached, or an error is encountered. + /// + /// Unlike other I/O functions, this function blocks until the end + /// of the input stream is seen and all the data has been written to + /// the output stream. + /// + /// This function returns the number of bytes transferred. + forward: func( + this: output-stream, + /// The stream to read from + src: input-stream + ) -> result + + /// Create a `pollable` which will resolve once either the specified stream + /// is ready to accept bytes or the other end of the stream has been closed. + subscribe-to-output-stream: func(this: output-stream) -> pollable + + /// Dispose of the specified `output-stream`, after which it may no longer + /// be used. + drop-output-stream: func(this: output-stream) +} diff --git a/test/fixtures/wit/wasi/deps/logging/handler.wit b/test/fixtures/wit/wasi/deps/logging/handler.wit new file mode 100644 index 000000000..e6b077be8 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/logging/handler.wit @@ -0,0 +1,34 @@ +package wasi:logging + +/// WASI Logging is a logging API intended to let users emit log messages with +/// simple priority levels and context values. +interface handler { + /// A log level, describing a kind of message. + enum level { + /// Describes messages about the values of variables and the flow of + /// control within a program. + trace, + + /// Describes messages likely to be of interest to someone debugging a + /// program. + debug, + + /// Describes messages likely to be of interest to someone monitoring a + /// program. + info, + + /// Describes messages indicating hazardous situations. + warn, + + /// Describes messages indicating serious errors. + error, + } + + /// Emit a log message. + /// + /// A log message has a `level` describing what kind of message is being + /// sent, a context, which is an uninterpreted string meant to help + /// consumers group similar messages, and a string containing the message + /// text. + log: func(level: level, context: string, message: string) +} diff --git a/test/fixtures/wit/wasi/deps/poll/poll.wit b/test/fixtures/wit/wasi/deps/poll/poll.wit new file mode 100644 index 000000000..cf5d8779c --- /dev/null +++ b/test/fixtures/wit/wasi/deps/poll/poll.wit @@ -0,0 +1,41 @@ +package wasi:poll + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +interface poll { + /// A "pollable" handle. + /// + /// This is conceptually represents a `stream<_, _>`, or in other words, + /// a stream that one can wait on, repeatedly, but which does not itself + /// produce any data. It's temporary scaffolding until component-model's + /// async features are ready. + /// + /// And at present, it is a `u32` instead of being an actual handle, until + /// the wit-bindgen implementation of handles and resources is ready. + /// + /// `pollable` lifetimes are not automatically managed. Users must ensure + /// that they do not outlive the resource they reference. + /// + /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). + type pollable = u32 + + /// Dispose of the specified `pollable`, after which it may no longer + /// be used. + drop-pollable: func(this: pollable) + + /// Poll for completion on a set of pollables. + /// + /// The "oneoff" in the name refers to the fact that this function must do a + /// linear scan through the entire list of subscriptions, which may be + /// inefficient if the number is large and the same subscriptions are used + /// many times. In the future, this is expected to be obsoleted by the + /// component model async proposal, which will include a scalable waiting + /// facility. + /// + /// Note that the return type would ideally be `list`, but that would + /// be more difficult to polyfill given the current state of `wit-bindgen`. + /// See + /// for details. For now, we use zero to mean "not ready" and non-zero to + /// mean "ready". + poll-oneoff: func(in: list) -> list +} diff --git a/test/fixtures/wit/wasi/deps/random/insecure-seed.wit b/test/fixtures/wit/wasi/deps/random/insecure-seed.wit new file mode 100644 index 000000000..ff2ff65d0 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/random/insecure-seed.wit @@ -0,0 +1,24 @@ +/// The insecure-seed interface for seeding hash-map DoS resistance. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +interface insecure-seed { + /// Return a 128-bit value that may contain a pseudo-random value. + /// + /// The returned value is not required to be computed from a CSPRNG, and may + /// even be entirely deterministic. Host implementations are encouraged to + /// provide pseudo-random values to any program exposed to + /// attacker-controlled content, to enable DoS protection built into many + /// languages' hash-map implementations. + /// + /// This function is intended to only be called once, by a source language + /// to initialize Denial Of Service (DoS) protection in its hash-map + /// implementation. + /// + /// # Expected future evolution + /// + /// This will likely be changed to a value import, to prevent it from being + /// called multiple times and potentially used for purposes other than DoS + /// protection. + insecure-seed: func() -> tuple +} diff --git a/test/fixtures/wit/wasi/deps/random/insecure.wit b/test/fixtures/wit/wasi/deps/random/insecure.wit new file mode 100644 index 000000000..ff0826822 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/random/insecure.wit @@ -0,0 +1,21 @@ +/// The insecure interface for insecure pseudo-random numbers. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +interface insecure { + /// Return `len` insecure pseudo-random bytes. + /// + /// This function is not cryptographically secure. Do not use it for + /// anything related to security. + /// + /// There are no requirements on the values of the returned bytes, however + /// implementations are encouraged to return evenly distributed values with + /// a long period. + get-insecure-random-bytes: func(len: u64) -> list + + /// Return an insecure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-insecure-random-bytes`, represented as a `u64`. + get-insecure-random-u64: func() -> u64 +} diff --git a/test/fixtures/wit/wasi/deps/random/random.wit b/test/fixtures/wit/wasi/deps/random/random.wit new file mode 100644 index 000000000..f2bd6358c --- /dev/null +++ b/test/fixtures/wit/wasi/deps/random/random.wit @@ -0,0 +1,25 @@ +package wasi:random + +/// WASI Random is a random data API. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +interface random { + /// Return `len` cryptographically-secure pseudo-random bytes. + /// + /// This function must produce data from an adequately seeded + /// cryptographically-secure pseudo-random number generator (CSPRNG), so it + /// must not block, from the perspective of the calling program, and the + /// returned data is always unpredictable. + /// + /// This function must always return fresh pseudo-random data. Deterministic + /// environments must omit this function, rather than implementing it with + /// deterministic data. + get-random-bytes: func(len: u64) -> list + + /// Return a cryptographically-secure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-random-bytes`, represented as a `u64`. + get-random-u64: func() -> u64 +} diff --git a/test/fixtures/wit/wasi/deps/sockets/instance-network.wit b/test/fixtures/wit/wasi/deps/sockets/instance-network.wit new file mode 100644 index 000000000..d911a29cc --- /dev/null +++ b/test/fixtures/wit/wasi/deps/sockets/instance-network.wit @@ -0,0 +1,9 @@ + +/// This interface provides a value-export of the default network handle.. +interface instance-network { + use network.{network} + + /// Get a handle to the default network. + instance-network: func() -> network + +} diff --git a/test/fixtures/wit/wasi/deps/sockets/ip-name-lookup.wit b/test/fixtures/wit/wasi/deps/sockets/ip-name-lookup.wit new file mode 100644 index 000000000..6c64b4617 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/sockets/ip-name-lookup.wit @@ -0,0 +1,69 @@ + +interface ip-name-lookup { + use wasi:poll/poll.{pollable} + use network.{network, error-code, ip-address, ip-address-family} + + + /// Resolve an internet host name to a list of IP addresses. + /// + /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. + /// + /// # Parameters + /// - `name`: The name to look up. IP addresses are not allowed. Unicode domain names are automatically converted + /// to ASCII using IDNA encoding. + /// - `address-family`: If provided, limit the results to addresses of this specific address family. + /// - `include-unavailable`: When set to true, this function will also return addresses of which the runtime + /// thinks (or knows) can't be connected to at the moment. For example, this will return IPv6 addresses on + /// systems without an active IPv6 interface. Notes: + /// - Even when no public IPv6 interfaces are present or active, names like "localhost" can still resolve to an IPv6 address. + /// - Whatever is "available" or "unavailable" is volatile and can change everytime a network cable is unplugged. + /// + /// This function never blocks. It either immediately fails or immediately returns successfully with a `resolve-address-stream` + /// that can be used to (asynchronously) fetch the results. + /// + /// At the moment, the stream never completes successfully with 0 items. Ie. the first call + /// to `resolve-next-address` never returns `ok(none)`. This may change in the future. + /// + /// # Typical errors + /// - `invalid-name`: `name` is a syntactically invalid domain name. + /// - `invalid-name`: `name` is an IP address. + /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) + /// + /// # References: + /// - + /// - + /// - + /// - + resolve-addresses: func(network: network, name: string, address-family: option, include-unavailable: bool) -> result + + + + type resolve-address-stream = u32 + + /// Returns the next address from the resolver. + /// + /// This function should be called multiple times. On each call, it will + /// return the next address in connection order preference. If all + /// addresses have been exhausted, this function returns `none`. + /// After which, you should release the stream with `drop-resolve-address-stream`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + resolve-next-address: func(this: resolve-address-stream) -> result, error-code> + + /// Dispose of the specified `resolve-address-stream`, after which it may no longer be used. + /// + /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. + drop-resolve-address-stream: func(this: resolve-address-stream) + + /// Create a `pollable` which will resolve once the stream is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func(this: resolve-address-stream) -> pollable +} diff --git a/test/fixtures/wit/wasi/deps/sockets/network.wit b/test/fixtures/wit/wasi/deps/sockets/network.wit new file mode 100644 index 000000000..c370214ce --- /dev/null +++ b/test/fixtures/wit/wasi/deps/sockets/network.wit @@ -0,0 +1,187 @@ +package wasi:sockets + +interface network { + /// An opaque resource that represents access to (a subset of) the network. + /// This enables context-based security for networking. + /// There is no need for this to map 1:1 to a physical network interface. + /// + /// FYI, In the future this will be replaced by handle types. + type network = u32 + + /// Dispose of the specified `network`, after which it may no longer be used. + /// + /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. + drop-network: func(this: network) + + + /// Error codes. + /// + /// In theory, every API can return any error code. + /// In practice, API's typically only return the errors documented per API + /// combined with a couple of errors that are always possible: + /// - `unknown` + /// - `access-denied` + /// - `not-supported` + /// - `out-of-memory` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + enum error-code { + // ### GENERAL ERRORS ### + + /// Unknown error + unknown, + + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, + + /// The operation is not supported. + /// + /// POSIX equivalent: EOPNOTSUPP + not-supported, + + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + out-of-memory, + + /// The operation timed out before it could finish completely. + timeout, + + /// This operation is incompatible with another asynchronous operation that is already in progress. + concurrency-conflict, + + /// Trying to finish an asynchronous operation that: + /// - has not been started yet, or: + /// - was already finished by a previous `finish-*` call. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + not-in-progress, + + /// The operation has been aborted because it could not be completed immediately. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + would-block, + + + // ### IP ERRORS ### + + /// The specified address-family is not supported. + address-family-not-supported, + + /// An IPv4 address was passed to an IPv6 resource, or vice versa. + address-family-mismatch, + + /// The socket address is not a valid remote address. E.g. the IP address is set to INADDR_ANY, or the port is set to 0. + invalid-remote-address, + + /// The operation is only supported on IPv4 resources. + ipv4-only-operation, + + /// The operation is only supported on IPv6 resources. + ipv6-only-operation, + + + + // ### TCP & UDP SOCKET ERRORS ### + + /// A new socket resource could not be created because of a system limit. + new-socket-limit, + + /// The socket is already attached to another network. + already-attached, + + /// The socket is already bound. + already-bound, + + /// The socket is already in the Connection state. + already-connected, + + /// The socket is not bound to any local address. + not-bound, + + /// The socket is not in the Connection state. + not-connected, + + /// A bind operation failed because the provided address is not an address that the `network` can bind to. + address-not-bindable, + + /// A bind operation failed because the provided address is already in use. + address-in-use, + + /// A bind operation failed because there are no ephemeral ports available. + ephemeral-ports-exhausted, + + /// The remote address is not reachable + remote-unreachable, + + + // ### TCP SOCKET ERRORS ### + + /// The socket is already in the Listener state. + already-listening, + + /// The socket is already in the Listener state. + not-listening, + + /// The connection was forcefully rejected + connection-refused, + + /// The connection was reset. + connection-reset, + + + // ### UDP SOCKET ERRORS ### + datagram-too-large, + + + // ### NAME LOOKUP ERRORS ### + + /// The provided name is a syntactically invalid domain name. + invalid-name, + + /// Name does not exist or has no suitable associated IP addresses. + name-unresolvable, + + /// A temporary failure in name resolution occurred. + temporary-resolver-failure, + + /// A permanent failure in name resolution occurred. + permanent-resolver-failure, + } + + enum ip-address-family { + /// Similar to `AF_INET` in POSIX. + ipv4, + + /// Similar to `AF_INET6` in POSIX. + ipv6, + } + + type ipv4-address = tuple + type ipv6-address = tuple + + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } + + record ipv4-socket-address { + port: u16, // sin_port + address: ipv4-address, // sin_addr + } + + record ipv6-socket-address { + port: u16, // sin6_port + flow-info: u32, // sin6_flowinfo + address: ipv6-address, // sin6_addr + scope-id: u32, // sin6_scope_id + } + + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } + +} diff --git a/test/fixtures/wit/wasi/deps/sockets/tcp-create-socket.wit b/test/fixtures/wit/wasi/deps/sockets/tcp-create-socket.wit new file mode 100644 index 000000000..f467d2856 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/sockets/tcp-create-socket.wit @@ -0,0 +1,27 @@ + +interface tcp-create-socket { + use network.{network, error-code, ip-address-family} + use tcp.{tcp-socket} + + /// Create a new TCP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect` + /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP) + /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + create-tcp-socket: func(address-family: ip-address-family) -> result +} diff --git a/test/fixtures/wit/wasi/deps/sockets/tcp.wit b/test/fixtures/wit/wasi/deps/sockets/tcp.wit new file mode 100644 index 000000000..7ed46a690 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/sockets/tcp.wit @@ -0,0 +1,255 @@ + +interface tcp { + use wasi:io/streams.{input-stream, output-stream} + use wasi:poll/poll.{pollable} + use network.{network, error-code, ip-socket-address, ip-address-family} + + /// A TCP socket handle. + type tcp-socket = u32 + + + enum shutdown-type { + /// Similar to `SHUT_RD` in POSIX. + receive, + + /// Similar to `SHUT_WR` in POSIX. + send, + + /// Similar to `SHUT_RDWR` in POSIX. + both, + } + + + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will + /// implicitly bind the socket. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + /// - `already-bound`: The socket is already bound. (EINVAL) + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-bind: func(this: tcp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> + finish-bind: func(this: tcp-socket) -> result<_, error-code> + + /// Connect to a remote endpoint. + /// + /// On success: + /// - the socket is transitioned into the Connection state + /// - a pair of streams is returned that can be used to read & write to the connection + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `already-connected`: The socket is already in the Connection state. (EISCONN) + /// - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A `connect` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-connect: func(this: tcp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> + finish-connect: func(this: tcp-socket) -> result, error-code> + + /// Start listening for new connections. + /// + /// Transitions the socket into the Listener state. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `already-attached`: The socket is already attached to a different network. The `network` passed to `listen` must be identical to the one passed to `bind`. + /// - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) + /// - `already-listening`: The socket is already in the Listener state. + /// - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A `listen` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-listen: func(this: tcp-socket, network: network) -> result<_, error-code> + finish-listen: func(this: tcp-socket) -> result<_, error-code> + + /// Accept a new client socket. + /// + /// The returned socket is bound and in the Connection state. + /// + /// On success, this function returns the newly accepted client socket along with + /// a pair of streams that can be used to read & write to the connection. + /// + /// # Typical errors + /// - `not-listening`: Socket is not in the Listener state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// + /// Host implementations must skip over transient errors returned by the native accept syscall. + /// + /// # References + /// - + /// - + /// - + /// - + accept: func(this: tcp-socket) -> result, error-code> + + /// Get the bound local address. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func(this: tcp-socket) -> result + + /// Get the bound remote address. + /// + /// # Typical errors + /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func(this: tcp-socket) -> result + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func(this: tcp-socket) -> ip-address-family + + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// + /// Equivalent to the IPV6_V6ONLY socket option. + /// + /// # Typical errors + /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + ipv6-only: func(this: tcp-socket) -> result + set-ipv6-only: func(this: tcp-socket, value: bool) -> result<_, error-code> + + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + set-listen-backlog-size: func(this: tcp-socket, value: u64) -> result<_, error-code> + + /// Equivalent to the SO_KEEPALIVE socket option. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + keep-alive: func(this: tcp-socket) -> result + set-keep-alive: func(this: tcp-socket, value: bool) -> result<_, error-code> + + /// Equivalent to the TCP_NODELAY socket option. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + no-delay: func(this: tcp-socket) -> result + set-no-delay: func(this: tcp-socket, value: bool) -> result<_, error-code> + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `already-listening`: (set) The socket is already in the Listener state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + unicast-hop-limit: func(this: tcp-socket) -> result + set-unicast-hop-limit: func(this: tcp-socket, value: u8) -> result<_, error-code> + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. + /// In other words, after setting a value, reading the same setting back may return a different value. + /// + /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of + /// actual data to be sent/received by the application, because the kernel might also use the buffer space + /// for internal metadata structures. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `already-connected`: (set) The socket is already in the Connection state. + /// - `already-listening`: (set) The socket is already in the Listener state. + /// - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) + receive-buffer-size: func(this: tcp-socket) -> result + set-receive-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> + send-buffer-size: func(this: tcp-socket) -> result + set-send-buffer-size: func(this: tcp-socket, value: u64) -> result<_, error-code> + + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func(this: tcp-socket) -> pollable + + /// Initiate a graceful shutdown. + /// + /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read + /// operations on the `input-stream` associated with this socket will return an End Of Stream indication. + /// Any data still in the receive queue at time of calling `shutdown` will be discarded. + /// - send: the socket is not expecting to send any more data to the peer. All subsequent write + /// operations on the `output-stream` associated with this socket will return an error. + /// - both: same effect as receive & send combined. + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `not-connected`: The socket is not in the Connection state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + shutdown: func(this: tcp-socket, shutdown-type: shutdown-type) -> result<_, error-code> + + /// Dispose of the specified `tcp-socket`, after which it may no longer be used. + /// + /// Similar to the POSIX `close` function. + /// + /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. + drop-tcp-socket: func(this: tcp-socket) +} diff --git a/test/fixtures/wit/wasi/deps/sockets/udp-create-socket.wit b/test/fixtures/wit/wasi/deps/sockets/udp-create-socket.wit new file mode 100644 index 000000000..1cfbd7f0b --- /dev/null +++ b/test/fixtures/wit/wasi/deps/sockets/udp-create-socket.wit @@ -0,0 +1,27 @@ + +interface udp-create-socket { + use network.{network, error-code, ip-address-family} + use udp.{udp-socket} + + /// Create a new UDP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` is called, + /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The host does not support UDP sockets. (EOPNOTSUPP) + /// - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References: + /// - + /// - + /// - + /// - + create-udp-socket: func(address-family: ip-address-family) -> result +} diff --git a/test/fixtures/wit/wasi/deps/sockets/udp.wit b/test/fixtures/wit/wasi/deps/sockets/udp.wit new file mode 100644 index 000000000..9dd4573bd --- /dev/null +++ b/test/fixtures/wit/wasi/deps/sockets/udp.wit @@ -0,0 +1,211 @@ + +interface udp { + use wasi:poll/poll.{pollable} + use network.{network, error-code, ip-socket-address, ip-address-family} + + + /// A UDP socket handle. + type udp-socket = u32 + + + record datagram { + data: list, // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. + remote-address: ip-socket-address, + + /// Possible future additions: + /// local-address: ip-socket-address, // IP_PKTINFO / IP_RECVDSTADDR / IPV6_PKTINFO + /// local-interface: u32, // IP_PKTINFO / IP_RECVIF + /// ttl: u8, // IP_RECVTTL + /// dscp: u6, // IP_RECVTOS + /// ecn: u2, // IP_RECVTOS + } + + + + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// When a socket is not explicitly bound, the first invocation to connect will implicitly bind the socket. + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) + /// - `already-bound`: The socket is already bound. (EINVAL) + /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-bind: func(this: udp-socket, network: network, local-address: ip-socket-address) -> result<_, error-code> + finish-bind: func(this: udp-socket) -> result<_, error-code> + + /// Set the destination address. + /// + /// The local-address is updated based on the best network path to `remote-address`. + /// + /// When a destination address is set: + /// - all receive operations will only return datagrams sent from the provided `remote-address`. + /// - the `send` function can only be used to send to this destination. + /// + /// Note that this function does not generate any network traffic and the peer is not aware of this "connection". + /// + /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts. + /// + /// # Typical `start` errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) + /// + /// # Typical `finish` errors + /// - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A `connect` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + start-connect: func(this: udp-socket, network: network, remote-address: ip-socket-address) -> result<_, error-code> + finish-connect: func(this: udp-socket) -> result<_, error-code> + + /// Receive a message. + /// + /// Returns: + /// - The sender address of the datagram + /// - The number of bytes read. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. (EINVAL) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + receive: func(this: udp-socket) -> result + + /// Send a message to a specific destination address. + /// + /// The remote address option is required. To send a message to the "connected" peer, + /// call `remote-address` to get their address. + /// + /// # Typical errors + /// - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) + /// - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. + /// - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// - `would-block`: The send buffer is currently full. (EWOULDBLOCK, EAGAIN) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + send: func(this: udp-socket, datagram: datagram) -> result<_, error-code> + + /// Get the current bound address. + /// + /// # Typical errors + /// - `not-bound`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + local-address: func(this: udp-socket) -> result + + /// Get the address set with `connect`. + /// + /// # Typical errors + /// - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func(this: udp-socket) -> result + + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + address-family: func(this: udp-socket) -> ip-address-family + + /// Whether IPv4 compatibility (dual-stack) mode is disabled or not. + /// + /// Equivalent to the IPV6_V6ONLY socket option. + /// + /// # Typical errors + /// - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. + /// - `already-bound`: (set) The socket is already bound. + /// - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + ipv6-only: func(this: udp-socket) -> result + set-ipv6-only: func(this: udp-socket, value: bool) -> result<_, error-code> + + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + unicast-hop-limit: func(this: udp-socket) -> result + set-unicast-hop-limit: func(this: udp-socket, value: u8) -> result<_, error-code> + + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// Note #1: an implementation may choose to cap or round the buffer size when setting the value. + /// In other words, after setting a value, reading the same setting back may return a different value. + /// + /// Note #2: there is not necessarily a direct relationship between the kernel buffer size and the bytes of + /// actual data to be sent/received by the application, because the kernel might also use the buffer space + /// for internal metadata structures. + /// + /// Fails when this socket is in the Listening state. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) + receive-buffer-size: func(this: udp-socket) -> result + set-receive-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> + send-buffer-size: func(this: udp-socket) -> result + set-send-buffer-size: func(this: udp-socket, value: u64) -> result<_, error-code> + + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI Preview2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + subscribe: func(this: udp-socket) -> pollable + + /// Dispose of the specified `udp-socket`, after which it may no longer be used. + /// + /// Note: this function is scheduled to be removed when Resources are natively supported in Wit. + drop-udp-socket: func(this: udp-socket) +} diff --git a/test/fixtures/wit/wasi/deps/wasi-cli-base/environment.wit b/test/fixtures/wit/wasi/deps/wasi-cli-base/environment.wit new file mode 100644 index 000000000..4c97c85d1 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/wasi-cli-base/environment.wit @@ -0,0 +1,16 @@ +package wasi:cli-base + +interface environment { + /// Get the POSIX-style environment variables. + /// + /// Each environment variable is provided as a pair of string variable names + /// and string value. + /// + /// Morally, these are a value import, but until value imports are available + /// in the component model, this import function should return the same + /// values each time it is called. + get-environment: func() -> list> + + /// Get the POSIX-style arguments to the program. + get-arguments: func() -> list +} diff --git a/test/fixtures/wit/wasi/deps/wasi-cli-base/exit.wit b/test/fixtures/wit/wasi/deps/wasi-cli-base/exit.wit new file mode 100644 index 000000000..66835aa70 --- /dev/null +++ b/test/fixtures/wit/wasi/deps/wasi-cli-base/exit.wit @@ -0,0 +1,4 @@ +interface exit { + /// Exit the curerent instance and any linked instances. + exit: func(status: result) +} diff --git a/test/fixtures/wit/wasi/deps/wasi-cli-base/preopens.wit b/test/fixtures/wit/wasi/deps/wasi-cli-base/preopens.wit new file mode 100644 index 000000000..f268f6b0b --- /dev/null +++ b/test/fixtures/wit/wasi/deps/wasi-cli-base/preopens.wit @@ -0,0 +1,7 @@ +interface preopens { + use wasi:filesystem/filesystem.{descriptor} + use wasi:io/streams.{input-stream, output-stream} + + /// Return the set of of preopened directories, and their path. + get-directories: func() -> list> +} diff --git a/test/fixtures/wit/wasi/deps/wasi-cli-base/stdio.wit b/test/fixtures/wit/wasi/deps/wasi-cli-base/stdio.wit new file mode 100644 index 000000000..6c9d4a41a --- /dev/null +++ b/test/fixtures/wit/wasi/deps/wasi-cli-base/stdio.wit @@ -0,0 +1,17 @@ +interface stdin { + use wasi:io/streams.{input-stream} + + get-stdin: func() -> input-stream +} + +interface stdout { + use wasi:io/streams.{output-stream} + + get-stdout: func() -> output-stream +} + +interface stderr { + use wasi:io/streams.{output-stream} + + get-stderr: func() -> output-stream +} diff --git a/test/fixtures/wit/wasi/proxy.wit b/test/fixtures/wit/wasi/proxy.wit new file mode 100644 index 000000000..a616daa1c --- /dev/null +++ b/test/fixtures/wit/wasi/proxy.wit @@ -0,0 +1,9 @@ +world proxy { + import wasi:random/random + import wasi:random/insecure + import wasi:random/insecure-seed + import wasi:logging/handler + import wasi:http/outgoing-handler + + export wasi:http/incoming-handler +} diff --git a/test/fixtures/wit/wasi/reactor.wit b/test/fixtures/wit/wasi/reactor.wit new file mode 100644 index 000000000..4a262c551 --- /dev/null +++ b/test/fixtures/wit/wasi/reactor.wit @@ -0,0 +1,24 @@ +world reactor { + import wasi:clocks/wall-clock + import wasi:clocks/monotonic-clock + import wasi:clocks/timezone + import wasi:filesystem/filesystem + import wasi:sockets/instance-network + import wasi:sockets/ip-name-lookup + import wasi:sockets/network + import wasi:sockets/tcp-create-socket + import wasi:sockets/tcp + import wasi:sockets/udp-create-socket + import wasi:sockets/udp + import wasi:random/random + import wasi:poll/poll + import wasi:io/streams + import wasi:logging/handler + import wasi:http/outgoing-handler + import wasi:cli-base/environment + import wasi:cli-base/preopens + import wasi:cli-base/exit + import wasi:cli-base/stdin + import wasi:cli-base/stdout + import wasi:cli-base/stderr +} diff --git a/test/runtime/dummy_proxy.ts b/test/runtime/dummy_proxy.ts index b44df741b..9730af0b1 100644 --- a/test/runtime/dummy_proxy.ts +++ b/test/runtime/dummy_proxy.ts @@ -1,6 +1,7 @@ // Flags: --instantiation import * as assert from "node:assert"; +// @ts-ignore import { importObject } from "@bytecodealliance/preview2-shim"; // @ts-ignore From 3cdd56f047a8d439aa7c7a6c320b6e5e0731aea4 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 13 Jun 2023 11:29:51 -0700 Subject: [PATCH 3/3] finish wasi, tests --- build.rs | 4 +- crates/js-component-bindgen/src/ts_bindgen.rs | 31 +- .../preview2-shim/lib/browser/cli-base.js | 57 ++++ packages/preview2-shim/lib/browser/clocks.js | 50 ++++ packages/preview2-shim/lib/browser/console.js | 12 - .../lib/browser/default-outgoing-HTTP.js | 3 - .../preview2-shim/lib/browser/environment.js | 17 -- packages/preview2-shim/lib/browser/exit.js | 21 -- .../preview2-shim/lib/browser/filesystem.js | 275 +++++++++--------- packages/preview2-shim/lib/browser/http.js | 120 ++++++++ packages/preview2-shim/lib/browser/index.js | 52 +--- .../lib/browser/instance-network.js | 3 - packages/preview2-shim/lib/browser/io.js | 82 ++++-- .../lib/browser/ip-name-lookup.js | 23 -- packages/preview2-shim/lib/browser/logging.js | 16 + .../lib/browser/monotonic-clock.js | 14 - packages/preview2-shim/lib/browser/network.js | 3 - packages/preview2-shim/lib/browser/poll.js | 16 +- .../preview2-shim/lib/browser/preopens.js | 11 - packages/preview2-shim/lib/browser/random.js | 57 ++-- packages/preview2-shim/lib/browser/sockets.js | 203 +++++++++++++ packages/preview2-shim/lib/browser/stderr.js | 4 - packages/preview2-shim/lib/browser/streams.js | 60 ---- .../lib/browser/tcp-create-socket.js | 3 - packages/preview2-shim/lib/browser/tcp.js | 75 ----- .../preview2-shim/lib/browser/timezone.js | 12 - packages/preview2-shim/lib/browser/types.js | 99 ------- .../lib/browser/udp-create-socket.js | 3 - packages/preview2-shim/lib/browser/udp.js | 75 ----- .../preview2-shim/lib/browser/wall-clock.js | 9 - packages/preview2-shim/lib/nodejs/cli-base.js | 9 +- packages/preview2-shim/lib/nodejs/clocks.js | 50 ++++ packages/preview2-shim/lib/nodejs/console.js | 12 - .../lib/nodejs/default-outgoing-HTTP.js | 3 - packages/preview2-shim/lib/nodejs/http.js | 120 ++++++++ packages/preview2-shim/lib/nodejs/index.js | 17 +- .../lib/nodejs/instance-network.js | 3 - .../lib/nodejs/ip-name-lookup.js | 23 -- packages/preview2-shim/lib/nodejs/logging.js | 16 + .../lib/nodejs/monotonic-clock.js | 14 - packages/preview2-shim/lib/nodejs/network.js | 3 - packages/preview2-shim/lib/nodejs/poll.js | 11 + packages/preview2-shim/lib/nodejs/sockets.js | 203 +++++++++++++ .../lib/nodejs/tcp-create-socket.js | 3 - packages/preview2-shim/lib/nodejs/tcp.js | 75 ----- packages/preview2-shim/lib/nodejs/timezone.js | 12 - packages/preview2-shim/lib/nodejs/types.js | 99 ------- .../lib/nodejs/udp-create-socket.js | 3 - packages/preview2-shim/lib/nodejs/udp.js | 75 ----- .../preview2-shim/lib/nodejs/wall-clock.js | 9 - packages/preview2-shim/test/test.js | 2 +- .../types/imports/cli-base-environment.d.ts | 8 +- .../types/imports/cli-base-exit.d.ts | 4 +- .../types/imports/cli-base-preopens.d.ts | 4 +- .../types/imports/clocks-monotonic-clock.d.ts | 12 +- .../types/imports/clocks-timezone.d.ts | 12 +- .../types/imports/clocks-wall-clock.d.ts | 8 +- .../types/imports/filesystem-filesystem.d.ts | 140 ++++----- .../types/imports/io-streams.d.ts | 60 ++-- .../types/imports/logging-handler.d.ts | 4 +- .../types/imports/poll-poll.d.ts | 8 +- .../types/imports/random-insecure-seed.d.ts | 4 +- .../types/imports/random-insecure.d.ts | 8 +- .../types/imports/random-random.d.ts | 8 +- .../imports/sockets-instance-network.d.ts | 4 +- .../types/imports/sockets-ip-name-lookup.d.ts | 16 +- .../types/imports/sockets-network.d.ts | 4 +- .../imports/sockets-tcp-create-socket.d.ts | 4 +- .../types/imports/sockets-tcp.d.ts | 64 ++-- .../imports/sockets-udp-create-socket.d.ts | 4 +- .../types/imports/sockets-udp.d.ts | 48 +-- 71 files changed, 1349 insertions(+), 1247 deletions(-) create mode 100644 packages/preview2-shim/lib/browser/cli-base.js create mode 100644 packages/preview2-shim/lib/browser/clocks.js delete mode 100644 packages/preview2-shim/lib/browser/console.js delete mode 100644 packages/preview2-shim/lib/browser/default-outgoing-HTTP.js delete mode 100644 packages/preview2-shim/lib/browser/environment.js delete mode 100644 packages/preview2-shim/lib/browser/exit.js delete mode 100644 packages/preview2-shim/lib/browser/instance-network.js delete mode 100644 packages/preview2-shim/lib/browser/ip-name-lookup.js create mode 100644 packages/preview2-shim/lib/browser/logging.js delete mode 100644 packages/preview2-shim/lib/browser/monotonic-clock.js delete mode 100644 packages/preview2-shim/lib/browser/network.js delete mode 100644 packages/preview2-shim/lib/browser/preopens.js create mode 100644 packages/preview2-shim/lib/browser/sockets.js delete mode 100644 packages/preview2-shim/lib/browser/stderr.js delete mode 100644 packages/preview2-shim/lib/browser/streams.js delete mode 100644 packages/preview2-shim/lib/browser/tcp-create-socket.js delete mode 100644 packages/preview2-shim/lib/browser/tcp.js delete mode 100644 packages/preview2-shim/lib/browser/timezone.js delete mode 100644 packages/preview2-shim/lib/browser/types.js delete mode 100644 packages/preview2-shim/lib/browser/udp-create-socket.js delete mode 100644 packages/preview2-shim/lib/browser/udp.js delete mode 100644 packages/preview2-shim/lib/browser/wall-clock.js create mode 100644 packages/preview2-shim/lib/nodejs/clocks.js delete mode 100644 packages/preview2-shim/lib/nodejs/console.js delete mode 100644 packages/preview2-shim/lib/nodejs/default-outgoing-HTTP.js delete mode 100644 packages/preview2-shim/lib/nodejs/instance-network.js delete mode 100644 packages/preview2-shim/lib/nodejs/ip-name-lookup.js create mode 100644 packages/preview2-shim/lib/nodejs/logging.js delete mode 100644 packages/preview2-shim/lib/nodejs/monotonic-clock.js delete mode 100644 packages/preview2-shim/lib/nodejs/network.js create mode 100644 packages/preview2-shim/lib/nodejs/poll.js create mode 100644 packages/preview2-shim/lib/nodejs/sockets.js delete mode 100644 packages/preview2-shim/lib/nodejs/tcp-create-socket.js delete mode 100644 packages/preview2-shim/lib/nodejs/tcp.js delete mode 100644 packages/preview2-shim/lib/nodejs/timezone.js delete mode 100644 packages/preview2-shim/lib/nodejs/types.js delete mode 100644 packages/preview2-shim/lib/nodejs/udp-create-socket.js delete mode 100644 packages/preview2-shim/lib/nodejs/udp.js delete mode 100644 packages/preview2-shim/lib/nodejs/wall-clock.js diff --git a/build.rs b/build.rs index 6d1b1890e..57343d8e7 100644 --- a/build.rs +++ b/build.rs @@ -33,9 +33,9 @@ fn main() -> Result<()> { let mut file = fs::File::create(outfile).unwrap(); file.write_all(contents).unwrap(); } - // println!("cargo:rerun-if-changed={:?}", preview2_wit_path); + println!("cargo:rerun-if-changed={:?}", preview2_wit_path); } } - // println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=build.rs"); Ok(()) } diff --git a/crates/js-component-bindgen/src/ts_bindgen.rs b/crates/js-component-bindgen/src/ts_bindgen.rs index 96fabc63f..57921cbc4 100644 --- a/crates/js-component-bindgen/src/ts_bindgen.rs +++ b/crates/js-component-bindgen/src/ts_bindgen.rs @@ -138,7 +138,7 @@ pub fn ts_bindgen( } } if !funcs.is_empty() { - let end_character = if opts.instantiation { "," } else { ";" }; + let end_character = if opts.instantiation { ',' } else { ';' }; bindgen.export_funcs(resolve, id, &funcs, files, end_character); } @@ -272,7 +272,7 @@ impl TsBindgen { ) { let mut gen = self.js_interface(resolve); for func in funcs { - gen.ts_func(func, AbiVariant::GuestImport, true, ","); + gen.ts_func(func, AbiVariant::GuestImport, true, "", ','); } gen.gen.import_object.push_str(&gen.src); } @@ -316,14 +316,16 @@ impl TsBindgen { _world: WorldId, funcs: &[(String, &Function)], _files: &mut Files, - end_character: &str, + end_character: char, ) { let mut gen = self.js_interface(resolve); + let prefix = if end_character == ';' { + "export function " + } else { + "" + }; for (_, func) in funcs { - if end_character == ";" { - gen.src.push_str("export function "); - } - gen.ts_func(func, AbiVariant::GuestExport, false, end_character); + gen.ts_func(func, AbiVariant::GuestExport, false, prefix, end_character); } gen.gen.export_object.push_str(&gen.src); @@ -348,9 +350,9 @@ impl TsBindgen { let mut gen = self.js_interface(resolve); uwriteln!(gen.src, "export namespace {camel} {{"); + let prefix = "export function "; for (_, func) in resolve.interfaces[id].functions.iter() { - gen.src.push_str("export function "); - gen.ts_func(func, abi, false, ";"); + gen.ts_func(func, abi, false, prefix, ';'); } uwriteln!(gen.src, "}}"); @@ -507,9 +509,18 @@ impl<'a> TsInterface<'a> { self.src.push_str("]"); } - fn ts_func(&mut self, func: &Function, abi: AbiVariant, default: bool, end_character: &str) { + fn ts_func( + &mut self, + func: &Function, + abi: AbiVariant, + default: bool, + prefix: &str, + end_character: char, + ) { self.docs(&func.docs); + self.src.push_str(prefix); + if default { self.src.push_str("default"); } else { diff --git a/packages/preview2-shim/lib/browser/cli-base.js b/packages/preview2-shim/lib/browser/cli-base.js new file mode 100644 index 000000000..6c83cfd5f --- /dev/null +++ b/packages/preview2-shim/lib/browser/cli-base.js @@ -0,0 +1,57 @@ +let _env; +export function _setEnv (envObj) { + _env = Object.entries(envObj); +} + +export const cliBaseEnvironment = { + getEnvironment () { + if (!_env) _env = []; + return _env; + } +}; + +class ComponentExit extends Error { + constructor(code) { + super(`Component exited ${code === 0 ? 'successfully' : 'with error'}`); + this.code = code; + } +} + +export const cliBaseExit = { + exit (status) { + throw new ComponentExit(status.tag === 'err' ? 1 : 0); + } +}; + +export const cliBasePreopens = { + getDirectories () { + return []; + } +} + +export const cliBaseStdin = { + getStdin () { + return 0; + } +}; + +export const cliBaseStdout = { + getStdout () { + return 1; + } +}; + +export const cliBaseStderr = { + getStderr () { + return 2; + } +}; + +export { + cliBaseEnvironment as environment, + cliBaseExit as exit, + cliBasePreopens as preopens, + cliBaseStdin as stdin, + cliBaseStdout as stdout, + cliBaseStderr as stderr +} diff --git a/packages/preview2-shim/lib/browser/clocks.js b/packages/preview2-shim/lib/browser/clocks.js new file mode 100644 index 000000000..4db96fa0d --- /dev/null +++ b/packages/preview2-shim/lib/browser/clocks.js @@ -0,0 +1,50 @@ +function _hrtimeBigint () { + return BigInt(Math.floor(performance.now() * 1e9)); +} + +let _hrStart = _hrtimeBigint(); + +export const clocksMonotonicClock = { + resolution() { + return 1n; + }, + now () { + return _hrtimeBigint() - _hrStart; + }, + subscribe (_when, _absolute) { + console.log(`[monotonic-clock] Subscribe`); + } +}; + +export const clocksTimezone = { + display (timezone, when) { + console.log(`[timezone] DISPLAY ${timezone} ${when}`); + }, + + utcOffset (timezone, when) { + console.log(`[timezone] UTC OFFSET ${timezone} ${when}`); + return 0; + }, + + dropTimezone (timezone) { + console.log(`[timezone] DROP ${timezone}`); + } +}; + +export const clocksWallClock = { + now() { + const seconds = BigInt(Math.floor(Date.now() / 1e3)); + const nanoseconds = (Date.now() % 1e3) * 1e6; + return { seconds, nanoseconds }; + }, + + resolution() { + console.log(`[wall-clock] Wall clock resolution`); + } +}; + +export { + clocksMonotonicClock as monotonicClock, + clocksTimezone as timezone, + clocksWallClock as wallClock +} diff --git a/packages/preview2-shim/lib/browser/console.js b/packages/preview2-shim/lib/browser/console.js deleted file mode 100644 index 2d3f0890a..000000000 --- a/packages/preview2-shim/lib/browser/console.js +++ /dev/null @@ -1,12 +0,0 @@ -const levels = ["trace", "debug", "info", "warn", "error"]; - -let logLevel = levels.indexOf("warn"); - -export function setLevel(level) { - logLevel = levels.indexOf(level); -} - -export function log(level, context, msg) { - if (logLevel > levels.indexOf(level)) return; - console[level](`(${context}) ${msg}\n`); -} diff --git a/packages/preview2-shim/lib/browser/default-outgoing-HTTP.js b/packages/preview2-shim/lib/browser/default-outgoing-HTTP.js deleted file mode 100644 index bb24fa000..000000000 --- a/packages/preview2-shim/lib/browser/default-outgoing-HTTP.js +++ /dev/null @@ -1,3 +0,0 @@ -export function handle(_request, _options) { - console.log("[default-outgoing-HTTP] Handle"); -} diff --git a/packages/preview2-shim/lib/browser/environment.js b/packages/preview2-shim/lib/browser/environment.js deleted file mode 100644 index 253a41a49..000000000 --- a/packages/preview2-shim/lib/browser/environment.js +++ /dev/null @@ -1,17 +0,0 @@ -let _env; -export function _setEnv (envObj) { - _env = Object.entries(envObj); -} - -export function getEnvironment () { - if (!_env) _env = []; - return _env; -} - -export function preopens () { - return []; -} - -export function getArguments () { - return []; -} \ No newline at end of file diff --git a/packages/preview2-shim/lib/browser/exit.js b/packages/preview2-shim/lib/browser/exit.js deleted file mode 100644 index 5509bdc65..000000000 --- a/packages/preview2-shim/lib/browser/exit.js +++ /dev/null @@ -1,21 +0,0 @@ -class FailureExit extends Error { - code = 1; - constructor() { - super("failure-exit"); - } -} - -class SuccessfulExit extends Error { - code = 0; - constructor() { - super("successful-exit"); - } -} - -export function exit(status) { - console.log(`[exit] Exit: ${JSON.stringify(status)}`); - if (status.tag === "err") { - throw new FailureExit(); - } - throw new SuccessfulExit(); -} diff --git a/packages/preview2-shim/lib/browser/filesystem.js b/packages/preview2-shim/lib/browser/filesystem.js index a3f848b0f..4c6621b63 100644 --- a/packages/preview2-shim/lib/browser/filesystem.js +++ b/packages/preview2-shim/lib/browser/filesystem.js @@ -1,150 +1,143 @@ -// export interface DescriptorStat { -// dev: Device, -// ino: Inode, -// type: DescriptorType, -// nlink: Linkcount, -// size: Filesize, -// atim: Timestamp, -// mtim: Timestamp, -// ctim: Timestamp, -// } - -export function readViaStream(fd, offset) { - console.log(`[filesystem] READ STREAM ${fd} ${offset}`); -} - -export function writeViaStream(fd, offset) { - console.log(`[filesystem] WRITE STREAM ${fd} ${offset}`); -} - -export function appendViaStream(fd) { - console.log(`[filesystem] APPEND STREAM ${fd}`); -} - -export function advise(fd, offset, length, advice) { - console.log(`[filesystem] ADVISE`, fd, offset, length, advice); -} - -export function syncData(fd) { - console.log(`[filesystem] SYNC DATA ${fd}`); -} - -export function getFlags(fd) { - console.log(`[filesystem] FLAGS FOR ${fd}`); -} - -export function getType(fd) { - console.log(`[filesystem] GET TYPE ${fd}`); -} - -export function setFlags(fd, flags) { - console.log(`[filesystem] SET FLAGS ${fd} ${JSON.stringify(flags)}`); -} - -export function setSize(fd, size) { - console.log(`[filesystem] SET SIZE`, fd, size); -} - -export function setTimes(fd, dataAccessTimestamp, dataModificationTimestamp) { - console.log(`[filesystem] SET TIMES`, fd, dataAccessTimestamp, dataModificationTimestamp); -} - -export function read(fd, length, offset) { - console.log(`[filesystem] READ`, fd, length, offset); -} - -export function write(fd, buffer, offset) { - console.log(`[filesystem] WRITE`, fd, buffer, offset); -} - -export function readDirectory(fd) { - console.log(`[filesystem] READ DIR`, fd); -} - -export function sync(fd) { - console.log(`[filesystem] SYNC`, fd); -} - -export function createDirectoryAt(fd, path) { - console.log(`[filesystem] CREATE DIRECTORY`, fd, path); -} - -export function stat(fd) { - console.log(`[filesystem] STAT`, fd); -} - -export function statAt(fd, pathFlags, path) { - console.log(`[filesystem] STAT`, fd, pathFlags, path); -} - -export function setTimesAt(fd) { - console.log(`[filesystem] SET TIMES AT`, fd); -} - -export function linkAt(fd) { - console.log(`[filesystem] LINK AT`, fd); -} - -export function openAt(fd) { - console.log(`[filesystem] OPEN AT`, fd); -} - -export function readlinkAt(fd) { - console.log(`[filesystem] READLINK AT`, fd); -} - -export function removeDirectoryAt(fd) { - console.log(`[filesystem] REMOVE DIR AT`, fd); -} - -export function renameAt(fd) { - console.log(`[filesystem] RENAME AT`, fd); -} - -export function symlinkAt(fd) { - console.log(`[filesystem] SYMLINK AT`, fd); -} - -export function unlinkFileAt(fd) { - console.log(`[filesystem] UNLINK FILE AT`, fd); -} +export const filesystem = { + readViaStream(fd, offset) { + console.log(`[filesystem] READ STREAM ${fd} ${offset}`); + }, + + writeViaStream(fd, offset) { + console.log(`[filesystem] WRITE STREAM ${fd} ${offset}`); + }, + + appendViaStream(fd) { + console.log(`[filesystem] APPEND STREAM ${fd}`); + }, + + advise(fd, offset, length, advice) { + console.log(`[filesystem] ADVISE`, fd, offset, length, advice); + }, + + syncData(fd) { + console.log(`[filesystem] SYNC DATA ${fd}`); + }, + + getFlags(fd) { + console.log(`[filesystem] FLAGS FOR ${fd}`); + }, + + getType(fd) { + console.log(`[filesystem] GET TYPE ${fd}`); + }, + + setFlags(fd, flags) { + console.log(`[filesystem] SET FLAGS ${fd} ${JSON.stringify(flags)}`); + }, + + setSize(fd, size) { + console.log(`[filesystem] SET SIZE`, fd, size); + }, + + setTimes(fd, dataAccessTimestamp, dataModificationTimestamp) { + console.log(`[filesystem] SET TIMES`, fd, dataAccessTimestamp, dataModificationTimestamp); + }, + + read(fd, length, offset) { + console.log(`[filesystem] READ`, fd, length, offset); + }, + + write(fd, buffer, offset) { + console.log(`[filesystem] WRITE`, fd, buffer, offset); + }, + + readDirectory(fd) { + console.log(`[filesystem] READ DIR`, fd); + }, + + sync(fd) { + console.log(`[filesystem] SYNC`, fd); + }, + + createDirectoryAt(fd, path) { + console.log(`[filesystem] CREATE DIRECTORY`, fd, path); + }, + + stat(fd) { + console.log(`[filesystem] STAT`, fd); + }, + + statAt(fd, pathFlags, path) { + console.log(`[filesystem] STAT`, fd, pathFlags, path); + }, + + setTimesAt(fd) { + console.log(`[filesystem] SET TIMES AT`, fd); + }, + + linkAt(fd) { + console.log(`[filesystem] LINK AT`, fd); + }, + + openAt(fd) { + console.log(`[filesystem] OPEN AT ${fd}`); + }, + + readlinkAt(fd) { + console.log(`[filesystem] READLINK AT`, fd); + }, + + removeDirectoryAt(fd) { + console.log(`[filesystem] REMOVE DIR AT`, fd); + }, + + renameAt(fd) { + console.log(`[filesystem] RENAME AT`, fd); + }, + + symlinkAt(fd) { + console.log(`[filesystem] SYMLINK AT`, fd); + }, + + unlinkFileAt(fd) { + console.log(`[filesystem] UNLINK FILE AT`, fd); + }, + + changeFilePermissionsAt(fd) { + console.log(`[filesystem] CHANGE FILE PERMISSIONS AT`, fd); + }, -export function changeFilePermissionsAt(fd) { - console.log(`[filesystem] CHANGE FILE PERMISSIONS AT`, fd); -} + changeDirectoryPermissionsAt(fd) { + console.log(`[filesystem] CHANGE DIR PERMISSIONS AT`, fd); + }, -export function changeDirectoryPermissionsAt(fd) { - console.log(`[filesystem] CHANGE DIR PERMISSIONS AT`, fd); -} + lockShared(fd) { + console.log(`[filesystem] LOCK SHARED`, fd); + }, -export function lockShared(fd) { - console.log(`[filesystem] LOCK SHARED`, fd); -} + lockExclusive(fd) { + console.log(`[filesystem] LOCK EXCLUSIVE`, fd); + }, -export function lockExclusive(fd) { - console.log(`[filesystem] LOCK EXCLUSIVE`, fd); -} + tryLockShared(fd) { + console.log(`[filesystem] TRY LOCK SHARED`, fd); + }, -export function tryLockShared(fd) { - console.log(`[filesystem] TRY LOCK SHARED`, fd); -} + tryLockExclusive(fd) { + console.log(`[filesystem] TRY LOCK EXCLUSIVE`, fd); + }, -export function tryLockExclusive(fd) { - console.log(`[filesystem] TRY LOCK EXCLUSIVE`, fd); -} + unlock(fd) { + console.log(`[filesystem] UNLOCK`, fd); + }, -export function unlock(fd) { - console.log(`[filesystem] UNLOCK`, fd); -} + dropDescriptor(fd) { + console.log(`[filesystem] DROP DESCRIPTOR`, fd); + }, -export function dropDescriptor(fd) { - console.log(`[filesystem] DROP DESCRIPTOR`, fd); -} + readDirectoryEntry(stream) { + console.log(`[filesystem] READ DIRECTRY ENTRY`, stream); + }, -export function readDirectoryEntry(stream) { - console.log(`[filesystem] READ DIRECTRY ENTRY`, stream); -} - -export function dropDirectoryEntryStream(stream) { - console.log(`[filesystem] DROP DIRECTORY ENTRY`, stream); -} + dropDirectoryEntryStream(stream) { + console.log(`[filesystem] DROP DIRECTORY ENTRY`, stream); + } +}; + +export { filesystem as filesystemFilesystem } diff --git a/packages/preview2-shim/lib/browser/http.js b/packages/preview2-shim/lib/browser/http.js index 031eba30d..82aea80f6 100644 --- a/packages/preview2-shim/lib/browser/http.js +++ b/packages/preview2-shim/lib/browser/http.js @@ -33,3 +33,123 @@ export function send(req) { throw new UnexpectedError(err.message); } } + +export const httpIncomingHandler = { + handle () { + + } +}; + +export const httpOutgoingHandler = { + handle () { + + } +}; + +export const httpTypes = { + dropFields(_fields) { + console.log("[types] Drop fields"); + }, + newFields(_entries) { + console.log("[types] New fields"); + }, + fieldsGet(_fields, _name) { + console.log("[types] Fields get"); + }, + fieldsSet(_fields, _name, _value) { + console.log("[types] Fields set"); + }, + fieldsDelete(_fields, _name) { + console.log("[types] Fields delete"); + }, + fieldsAppend(_fields, _name, _value) { + console.log("[types] Fields append"); + }, + fieldsEntries(_fields) { + console.log("[types] Fields entries"); + }, + fieldsClone(_fields) { + console.log("[types] Fields clone"); + }, + finishIncomingStream(s) { + console.log(`[types] Finish incoming stream ${s}`); + }, + finishOutgoingStream(s, _trailers) { + console.log(`[types] Finish outgoing stream ${s}`); + }, + dropIncomingRequest(_req) { + console.log("[types] Drop incoming request"); + }, + dropOutgoingRequest(_req) { + console.log("[types] Drop outgoing request"); + }, + incomingRequestMethod(_req) { + console.log("[types] Incoming request method"); + }, + incomingRequestPath(_req) { + console.log("[types] Incoming request path"); + }, + incomingRequestQuery(_req) { + console.log("[types] Incoming request query"); + }, + incomingRequestScheme(_req) { + console.log("[types] Incoming request scheme"); + }, + incomingRequestAuthority(_req) { + console.log("[types] Incoming request authority"); + }, + incomingRequestHeaders(_req) { + console.log("[types] Incoming request headers"); + }, + incomingRequestConsume(_req) { + console.log("[types] Incoming request consume"); + }, + newOutgoingRequest(_method, _path, _query, _scheme, _authority, _headers) { + console.log("[types] New outgoing request"); + }, + outgoingRequestWrite(_req) { + console.log("[types] Outgoing request write"); + }, + dropResponseOutparam(_res) { + console.log("[types] Drop response outparam"); + }, + setResponseOutparam(_response) { + console.log("[types] Drop fields"); + }, + dropIncomingResponse(_res) { + console.log("[types] Drop incoming response"); + }, + dropOutgoingResponse(_res) { + console.log("[types] Drop outgoing response"); + }, + incomingResponseStatus(_res) { + console.log("[types] Incoming response status"); + }, + incomingResponseHeaders(_res) { + console.log("[types] Incoming response headers"); + }, + incomingResponseConsume(_res) { + console.log("[types] Incoming response consume"); + }, + newOutgoingResponse(_statusCode, _headers) { + console.log("[types] New outgoing response"); + }, + outgoingResponseWrite(_res) { + console.log("[types] Outgoing response write"); + }, + dropFutureIncomingResponse(_f) { + console.log("[types] Drop future incoming response"); + }, + futureIncomingResponseGet(_f) { + console.log("[types] Future incoming response get"); + }, + listenToFutureIncomingResponse(_f) { + console.log("[types] Listen to future incoming response"); + } +}; + +export { + httpIncomingHandler as incomingHandler, + httpOutgoingHandler as outgoingHandler, + httpTypes as types +} diff --git a/packages/preview2-shim/lib/browser/index.js b/packages/preview2-shim/lib/browser/index.js index c7bc2cd6f..0935e25c6 100644 --- a/packages/preview2-shim/lib/browser/index.js +++ b/packages/preview2-shim/lib/browser/index.js @@ -1,45 +1,23 @@ -import * as console from "./console.js"; -import * as defaultOutgoingHttp from "./default-outgoing-HTTP.js"; -import * as environment from "./environment.js"; -import * as exit from "./exit.js"; +import * as clocks from "./clocks.js"; import * as filesystem from "./filesystem.js"; -import * as instanceNetwork from "./instance-network.js"; -import * as ipNameLookup from "./ip-name-lookup.js"; -import * as monotonicClock from "./monotonic-clock.js"; -import * as network from "./network.js"; +import * as http from "./http.js"; +import * as io from "./io.js"; +import * as logging from "./logging.js"; import * as poll from "./poll.js"; -import * as preopens from "./preopens.js"; import * as random from "./random.js"; -import * as streams from "./streams.js"; -import * as tcpCreateSocket from "./tcp-create-socket.js"; -import * as tcp from "./tcp.js"; -import * as timezone from "./timezone.js"; -import * as types from "./types.js"; -import * as udpCreateSocket from "./udp-create-socket.js"; -import * as udp from "./udp.js"; -import * as wallClock from "./wall-clock.js"; +import * as sockets from "./sockets.js"; +import * as cliBase from "./cli-base.js"; export const importObject = { - 'console': console, - 'default-outgoing-HTTP': defaultOutgoingHttp, - 'environment': environment, - 'exit': exit, - 'filesystem': filesystem, - 'instance-network': instanceNetwork, - 'ip-name-lookup': ipNameLookup, - 'monotonic-clock': monotonicClock, - 'network': network, - 'poll': poll, - 'preopens': preopens, - 'random': random, - 'streams': streams, - 'tcp-create-socket': tcpCreateSocket, - 'tcp': tcp, - 'timezone': timezone, - 'types': types, - 'udp-create-socket': udpCreateSocket, - 'udp': udp, - 'wall-clock': wallClock + clocks, + filesystem, + http, + io, + logging, + poll, + random, + sockets, + cliBase, }; export { WasiHttp } from "../http/wasi-http.js"; diff --git a/packages/preview2-shim/lib/browser/instance-network.js b/packages/preview2-shim/lib/browser/instance-network.js deleted file mode 100644 index 8c108b824..000000000 --- a/packages/preview2-shim/lib/browser/instance-network.js +++ /dev/null @@ -1,3 +0,0 @@ -export function instanceNetwork () { - -} diff --git a/packages/preview2-shim/lib/browser/io.js b/packages/preview2-shim/lib/browser/io.js index 68a35ffd6..ec4cfbaea 100644 --- a/packages/preview2-shim/lib/browser/io.js +++ b/packages/preview2-shim/lib/browser/io.js @@ -1,22 +1,64 @@ -export function read(s, len) { - console.log(`[io] Read ${s} ${len}`); -} - -export function write(s, buf) { - switch (s) { - case 0: - throw new Error(`TODO: write stdin`); - case 1: { - const decoder = new TextDecoder(); - console.log(decoder.decode(buf)); - return BigInt(buf.byteLength); - } - case 2: { - const decoder = new TextDecoder(); - console.error(decoder.decode(buf)); - return BigInt(buf.byteLength); +export const ioStreams = { + read(s, len) { + console.log(`[streams] Read ${s} ${len}`); + }, + blockingRead(s, len) { + console.log(`[streams] Blocking read ${s} ${len}`); + }, + skip(s, _len) { + console.log(`[streams] Skip ${s}`); + }, + blockingSkip(s, _len) { + console.log(`[streams] Blocking skip ${s}`); + }, + subscribeToInputStream(s) { + console.log(`[streams] Subscribe to input stream ${s}`); + }, + dropInputStream(s) { + console.log(`[streams] Drop input stream ${s}`); + }, + write(s, buf) { + switch (s) { + case 0: + throw new Error(`TODO: write stdin`); + case 1: { + const decoder = new TextDecoder(); + console.log(decoder.decode(buf)); + return BigInt(buf.byteLength); + } + case 2: { + const decoder = new TextDecoder(); + console.error(decoder.decode(buf)); + return BigInt(buf.byteLength); + } + default: + throw new Error(`TODO: write ${s}`); } - default: - throw new Error(`TODO: write ${s}`); + }, + blockingWrite(s, _buf) { + console.log(`[streams] Blocking write ${s}`); + }, + writeZeroes(s, _len) { + console.log(`[streams] Write zeroes ${s}`); + }, + blockingWriteZeroes(s, _len) { + console.log(`[streams] Blocking write zeroes ${s}`); + }, + splice(s, _src, _len) { + console.log(`[streams] Splice ${s}`); + }, + blockingSplice(s, _src, _len) { + console.log(`[streams] Blocking splice ${s}`); + }, + forward(s, _src) { + console.log(`[streams] Forward ${s}`); + }, + subscribeToOutputStream(s) { + console.log(`[streams] Subscribe to output stream ${s}`); + }, + dropOutputStream(s) { + console.log(`[streams] Drop output stream ${s}`); } -} +}; + +export { ioStreams as streams } diff --git a/packages/preview2-shim/lib/browser/ip-name-lookup.js b/packages/preview2-shim/lib/browser/ip-name-lookup.js deleted file mode 100644 index e1a449704..000000000 --- a/packages/preview2-shim/lib/browser/ip-name-lookup.js +++ /dev/null @@ -1,23 +0,0 @@ -export function dropResolveAddressStream () { - -} - -export function subscribe () { - -} - -export function resolveAddresses () { - -} - -export function resolveNextAddress () { - -} - -export function nonBlocking () { - -} - -export function setNonBlocking () { - -} diff --git a/packages/preview2-shim/lib/browser/logging.js b/packages/preview2-shim/lib/browser/logging.js new file mode 100644 index 000000000..e41a2d772 --- /dev/null +++ b/packages/preview2-shim/lib/browser/logging.js @@ -0,0 +1,16 @@ +const levels = ["trace", "debug", "info", "warn", "error"]; + +let logLevel = levels.indexOf("warn"); + +export const loggingHandler = { + log(level, context, msg) { + if (logLevel > levels.indexOf(level)) return; + console[level](`(${context}) ${msg}\n`); + } +}; + +export function setLevel(level) { + logLevel = levels.indexOf(level); +} + +export { loggingHandler as handler } diff --git a/packages/preview2-shim/lib/browser/monotonic-clock.js b/packages/preview2-shim/lib/browser/monotonic-clock.js deleted file mode 100644 index 6e31ffe1c..000000000 --- a/packages/preview2-shim/lib/browser/monotonic-clock.js +++ /dev/null @@ -1,14 +0,0 @@ - -export function resolution(clock) { - console.log(`[monotonic-clock] Monotonic clock resolution ${clock}`); -} - -function _hrtimeBigint () { - return BigInt(Math.floor(performance.now() * 1e9)); -} - -let _hrStart = _hrtimeBigint(); - -export function now () { - return _hrtimeBigint() - _hrStart; -} diff --git a/packages/preview2-shim/lib/browser/network.js b/packages/preview2-shim/lib/browser/network.js deleted file mode 100644 index 0f1f0ea0d..000000000 --- a/packages/preview2-shim/lib/browser/network.js +++ /dev/null @@ -1,3 +0,0 @@ -export function dropNetwork () { - -} diff --git a/packages/preview2-shim/lib/browser/poll.js b/packages/preview2-shim/lib/browser/poll.js index 60dc3c05c..e66f24beb 100644 --- a/packages/preview2-shim/lib/browser/poll.js +++ b/packages/preview2-shim/lib/browser/poll.js @@ -1,7 +1,11 @@ -export function dropPollable(p) { - console.log(`[poll] Drop pollable ${p}`); -} +export const pollPoll = { + dropPollable (pollable) { + console.log(`[poll] Drop (${pollable})`); + }, + pollOneoff (input) { + console.log(`[poll] Oneoff (${input})`); + return []; + } +}; -export function pollOneoff(f) { - console.log(`[poll] Poll oneoff ${f}`); -} +export { pollPoll as poll } diff --git a/packages/preview2-shim/lib/browser/preopens.js b/packages/preview2-shim/lib/browser/preopens.js deleted file mode 100644 index dd98e8edf..000000000 --- a/packages/preview2-shim/lib/browser/preopens.js +++ /dev/null @@ -1,11 +0,0 @@ -export function getStdio () { - return { - stdin: 0, - stdout: 1, - stderr: 2, - }; -} - -export function getDirectories () { - return []; -} diff --git a/packages/preview2-shim/lib/browser/random.js b/packages/preview2-shim/lib/browser/random.js index 7d3d0f3b9..ab45e900b 100644 --- a/packages/preview2-shim/lib/browser/random.js +++ b/packages/preview2-shim/lib/browser/random.js @@ -1,32 +1,39 @@ const MAX_BYTES = 65536; -export function getRandomBytes(len) { - const bytes = new Uint8Array(Number(len)); - - if (len > MAX_BYTES) { - // this is the max bytes crypto.getRandomValues - // can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues - for (var generated = 0; generated < len; generated += MAX_BYTES) { - // buffer.slice automatically checks if the end is past the end of - // the buffer so we don't have to here - crypto.getRandomValues(bytes.slice(generated, generated + MAX_BYTES)); - } - } else { - crypto.getRandomValues(bytes); - } - - return bytes; -} +let insecureRandomValue1, insecureRandomValue2; -export function getRandomU64 () { +function getRandomU64 () { return crypto.getRandomValues(new BigUint64Array(1))[0]; } -let insecureRandomValue1, insecureRandomValue2; -export function insecureRandom () { - if (insecureRandomValue1 === undefined) { - insecureRandomValue1 = getRandomU64(); - insecureRandomValue2 = getRandomU64(); +export const randomRandom = { + getRandomBytes(len) { + const bytes = new Uint8Array(Number(len)); + + if (len > MAX_BYTES) { + // this is the max bytes crypto.getRandomValues + // can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues + for (var generated = 0; generated < len; generated += MAX_BYTES) { + // buffer.slice automatically checks if the end is past the end of + // the buffer so we don't have to here + crypto.getRandomValues(bytes.slice(generated, generated + MAX_BYTES)); + } + } else { + crypto.getRandomValues(bytes); + } + + return bytes; + }, + + getRandomU64: getRandomU64, + + insecureRandom () { + if (insecureRandomValue1 === undefined) { + insecureRandomValue1 = getRandomU64(); + insecureRandomValue2 = getRandomU64(); + } + return [insecureRandomValue1, insecureRandomValue2]; } - return [insecureRandomValue1, insecureRandomValue2]; -} +}; + +export { randomRandom as random } diff --git a/packages/preview2-shim/lib/browser/sockets.js b/packages/preview2-shim/lib/browser/sockets.js new file mode 100644 index 000000000..210e6ccb3 --- /dev/null +++ b/packages/preview2-shim/lib/browser/sockets.js @@ -0,0 +1,203 @@ +export const socketsInstanceNetwork = { + instanceNetwork () { + console.log(`[sockets] instance network`); + } +}; + +export const socketsIpNameLookup = { + dropResolveAddressStream () { + + }, + subscribe () { + + }, + resolveAddresses () { + + }, + resolveNextAddress () { + + }, + nonBlocking () { + + }, + setNonBlocking () { + + }, +}; + +export const socketsNetwork = { + dropNetwork () { + + } +}; + +export const socketsTcpCreateSocket = { + createTcpSocket () { + + } +}; + +export const socketsTcp = { + subscribe () { + + }, + dropTcpSocket() { + + }, + bind() { + + }, + connect() { + + }, + listen() { + + }, + accept() { + + }, + localAddress() { + + }, + remoteAddress() { + + }, + addressFamily() { + + }, + ipv6Only() { + + }, + setIpv6Only() { + + }, + setListenBacklogSize() { + + }, + keepAlive() { + + }, + setKeepAlive() { + + }, + noDelay() { + + }, + setNoDelay() { + + }, + unicastHopLimit() { + + }, + setUnicastHopLimit() { + + }, + receiveBufferSize() { + + }, + setReceiveBufferSize() { + + }, + sendBufferSize() { + + }, + setSendBufferSize() { + + }, + nonBlocking() { + + }, + setNonBlocking() { + + }, + shutdown() { + + } +}; + +export const socketsUdp = { + subscribe () { + + }, + + dropUdpSocket () { + + }, + + bind () { + + }, + + connect () { + + }, + + receive () { + + }, + + send () { + + }, + + localAddress () { + + }, + + remoteAddress () { + + }, + + addressFamily () { + + }, + + ipv6Only () { + + }, + + setIpv6Only () { + + }, + + unicastHopLimit () { + + }, + + setUnicastHopLimit () { + + }, + + receiveBufferSize () { + + }, + + setReceiveBufferSize () { + + }, + + sendBufferSize () { + + }, + + setSendBufferSize () { + + }, + + nonBlocking () { + + }, + + setNonBlocking () { + + } +}; + +export { + socketsInstanceNetwork as instanceNetwork, + socketsIpNameLookup as ipNameLookup, + socketsNetwork as network, + socketsTcpCreateSocket as tcpCreateSocket, + socketsTcp as tcp, + socketsUdp as udp +} diff --git a/packages/preview2-shim/lib/browser/stderr.js b/packages/preview2-shim/lib/browser/stderr.js deleted file mode 100644 index 8a603d5d8..000000000 --- a/packages/preview2-shim/lib/browser/stderr.js +++ /dev/null @@ -1,4 +0,0 @@ -// TODO: remove -export function print(message) { - console.error(message); -} diff --git a/packages/preview2-shim/lib/browser/streams.js b/packages/preview2-shim/lib/browser/streams.js deleted file mode 100644 index 24d2cb818..000000000 --- a/packages/preview2-shim/lib/browser/streams.js +++ /dev/null @@ -1,60 +0,0 @@ -export function read(s, _len) { - console.log(`[streams] Read ${s}`); -} -export function blockingRead(s, _len) { - console.log(`[streams] Blocking read ${s}`); -} -export function skip(s, _len) { - console.log(`[streams] Skip ${s}`); -} -export function blockingSkip(s, _len) { - console.log(`[streams] Blocking skip ${s}`); -} -export function subscribeToInputStream(s) { - console.log(`[streams] Subscribe to input stream ${s}`); -} -export function dropInputStream(s) { - console.log(`[streams] Drop input stream ${s}`); -} -export function write(s, buf) { - switch (s) { - case 0: - throw new Error(`TODO: write stdin`); - case 1: { - const decoder = new TextDecoder(); - console.log(decoder.decode(buf)); - return BigInt(buf.byteLength); - } - case 2: { - const decoder = new TextDecoder(); - console.error(decoder.decode(buf)); - return BigInt(buf.byteLength); - } - default: - throw new Error(`TODO: write ${s}`); - } -} -export function blockingWrite(s, _buf) { - console.log(`[streams] Blocking write ${s}`); -} -export function writeZeroes(s, _len) { - console.log(`[streams] Write zeroes ${s}`); -} -export function blockingWriteZeroes(s, _len) { - console.log(`[streams] Blocking write zeroes ${s}`); -} -export function splice(s, _src, _len) { - console.log(`[streams] Splice ${s}`); -} -export function blockingSplice(s, _src, _len) { - console.log(`[streams] Blocking splice ${s}`); -} -export function forward(s, _src) { - console.log(`[streams] Forward ${s}`); -} -export function subscribeToOutputStream(s) { - console.log(`[streams] Subscribe to output stream ${s}`); -} -export function dropOutputStream(s) { - console.log(`[streams] Drop output stream ${s}`); -} diff --git a/packages/preview2-shim/lib/browser/tcp-create-socket.js b/packages/preview2-shim/lib/browser/tcp-create-socket.js deleted file mode 100644 index 0cf6401dd..000000000 --- a/packages/preview2-shim/lib/browser/tcp-create-socket.js +++ /dev/null @@ -1,3 +0,0 @@ -export function createTcpSocket () { - -} \ No newline at end of file diff --git a/packages/preview2-shim/lib/browser/tcp.js b/packages/preview2-shim/lib/browser/tcp.js deleted file mode 100644 index 45dbf9d98..000000000 --- a/packages/preview2-shim/lib/browser/tcp.js +++ /dev/null @@ -1,75 +0,0 @@ -export function subscribe () { - -} -export function dropTcpSocket() { - -} -export function bind() { - -} -export function connect() { - -} -export function listen() { - -} -export function accept() { - -} -export function localAddress() { - -} -export function remoteAddress() { - -} -export function addressFamily() { - -} -export function ipv6Only() { - -} -export function setIpv6Only() { - -} -export function setListenBacklogSize() { - -} -export function keepAlive() { - -} -export function setKeepAlive() { - -} -export function noDelay() { - -} -export function setNoDelay() { - -} -export function unicastHopLimit() { - -} -export function setUnicastHopLimit() { - -} -export function receiveBufferSize() { - -} -export function setReceiveBufferSize() { - -} -export function sendBufferSize() { - -} -export function setSendBufferSize() { - -} -export function nonBlocking() { - -} -export function setNonBlocking() { - -} -export function shutdown() { - -} \ No newline at end of file diff --git a/packages/preview2-shim/lib/browser/timezone.js b/packages/preview2-shim/lib/browser/timezone.js deleted file mode 100644 index 7568396f0..000000000 --- a/packages/preview2-shim/lib/browser/timezone.js +++ /dev/null @@ -1,12 +0,0 @@ -export function display (timezone, when) { - console.log(`[timezone] DISPLAY ${timezone} ${when}`); -} - -export function utcOffset (timezone, when) { - console.log(`[timezone] UTC OFFSET ${timezone} ${when}`); - return 0; -} - -export function dropTimezone (timezone) { - console.log(`[timezone] DROP ${timezone}`); -} diff --git a/packages/preview2-shim/lib/browser/types.js b/packages/preview2-shim/lib/browser/types.js deleted file mode 100644 index 8a66ecf2e..000000000 --- a/packages/preview2-shim/lib/browser/types.js +++ /dev/null @@ -1,99 +0,0 @@ -export function dropFields(_fields) { - console.log("[types] Drop fields"); -} -export function newFields(_entries) { - console.log("[types] New fields"); -} -export function fieldsGet(_fields, _name) { - console.log("[types] Fields get"); -} -export function fieldsSet(_fields, _name, _value) { - console.log("[types] Fields set"); -} -export function fieldsDelete(_fields, _name) { - console.log("[types] Fields delete"); -} -export function fieldsAppend(_fields, _name, _value) { - console.log("[types] Fields append"); -} -export function fieldsEntries(_fields) { - console.log("[types] Fields entries"); -} -export function fieldsClone(_fields) { - console.log("[types] Fields clone"); -} -export function finishIncomingStream(s) { - console.log(`[types] Finish incoming stream ${s}`); -} -export function finishOutgoingStream(s, _trailers) { - console.log(`[types] Finish outgoing stream ${s}`); -} -export function dropIncomingRequest(_req) { - console.log("[types] Drop incoming request"); -} -export function dropOutgoingRequest(_req) { - console.log("[types] Drop outgoing request"); -} -export function incomingRequestMethod(_req) { - console.log("[types] Incoming request method"); -} -export function incomingRequestPath(_req) { - console.log("[types] Incoming request path"); -} -export function incomingRequestQuery(_req) { - console.log("[types] Incoming request query"); -} -export function incomingRequestScheme(_req) { - console.log("[types] Incoming request scheme"); -} -export function incomingRequestAuthority(_req) { - console.log("[types] Incoming request authority"); -} -export function incomingRequestHeaders(_req) { - console.log("[types] Incoming request headers"); -} -export function incomingRequestConsume(_req) { - console.log("[types] Incoming request consume"); -} -export function newOutgoingRequest(_method, _path, _query, _scheme, _authority, _headers) { - console.log("[types] New outgoing request"); -} -export function outgoingRequestWrite(_req) { - console.log("[types] Outgoing request write"); -} -export function dropResponseOutparam(_res) { - console.log("[types] Drop response outparam"); -} -export function setResponseOutparam(_res) { - console.log("[types] Drop fields"); -} -export function dropIncomingResponse(_res) { - console.log("[types] Drop incoming response"); -} -export function dropOutgoingResponse(_res) { - console.log("[types] Drop outgoing response"); -} -export function incomingResponseStatus(_res) { - console.log("[types] Incoming response status"); -} -export function incomingResponseHeaders(_res) { - console.log("[types] Incoming response headers"); -} -export function incomingResponseConsume(_res) { - console.log("[types] Incoming response consume"); -} -export function newOutgoingResponse(_statusCode, _headers) { - console.log("[types] New outgoing response"); -} -export function outgoingResponseWrite(_res) { - console.log("[types] Outgoing response write"); -} -export function dropFutureIncomingResponse(_f) { - console.log("[types] Drop future incoming response"); -} -export function futureIncomingResponseGet(_f) { - console.log("[types] Future incoming response get"); -} -export function listenToFutureIncomingResponse(_f) { - console.log("[types] Listen to future incoming response"); -} diff --git a/packages/preview2-shim/lib/browser/udp-create-socket.js b/packages/preview2-shim/lib/browser/udp-create-socket.js deleted file mode 100644 index b0a86b496..000000000 --- a/packages/preview2-shim/lib/browser/udp-create-socket.js +++ /dev/null @@ -1,3 +0,0 @@ -export function createUdpSocket () { - -} diff --git a/packages/preview2-shim/lib/browser/udp.js b/packages/preview2-shim/lib/browser/udp.js deleted file mode 100644 index 9f47bc4d4..000000000 --- a/packages/preview2-shim/lib/browser/udp.js +++ /dev/null @@ -1,75 +0,0 @@ -export function subscribe () { - -} - -export function dropUdpSocket () { - -} - -export function bind () { - -} - -export function connect () { - -} - -export function receive () { - -} - -export function send () { - -} - -export function localAddress () { - -} - -export function remoteAddress () { - -} - -export function addressFamily () { - -} - -export function ipv6Only () { - -} - -export function setIpv6Only () { - -} - -export function unicastHopLimit () { - -} - -export function setUnicastHopLimit () { - -} - -export function receiveBufferSize () { - -} - -export function setReceiveBufferSize () { - -} - -export function sendBufferSize () { - -} - -export function setSendBufferSize () { - -} - -export function nonBlocking () { - -} - -export function setNonBlocking () { - -} \ No newline at end of file diff --git a/packages/preview2-shim/lib/browser/wall-clock.js b/packages/preview2-shim/lib/browser/wall-clock.js deleted file mode 100644 index 0ff3988f2..000000000 --- a/packages/preview2-shim/lib/browser/wall-clock.js +++ /dev/null @@ -1,9 +0,0 @@ -export function now() { - const seconds = BigInt(Math.floor(Date.now() / 1e3)); - const nanoseconds = (Date.now() % 1e3) * 1e6; - return { seconds, nanoseconds }; -} - -export function resolution() { - console.log(`[wall-clock] Wall clock resolution`); -} diff --git a/packages/preview2-shim/lib/nodejs/cli-base.js b/packages/preview2-shim/lib/nodejs/cli-base.js index a1960b1a9..348803770 100644 --- a/packages/preview2-shim/lib/nodejs/cli-base.js +++ b/packages/preview2-shim/lib/nodejs/cli-base.js @@ -98,4 +98,11 @@ export const cliBaseStderr = { } }; -export { cliBaseEnvironment as environment, cliBaseExit as exit, cliBasePreopens as preopens, cliBaseStdin as stdin, cliBaseStdout as stdout, cliBaseStderr as stderr } +export { + cliBaseEnvironment as environment, + cliBaseExit as exit, + cliBasePreopens as preopens, + cliBaseStdin as stdin, + cliBaseStdout as stdout, + cliBaseStderr as stderr +} diff --git a/packages/preview2-shim/lib/nodejs/clocks.js b/packages/preview2-shim/lib/nodejs/clocks.js new file mode 100644 index 000000000..90e50932a --- /dev/null +++ b/packages/preview2-shim/lib/nodejs/clocks.js @@ -0,0 +1,50 @@ +import { hrtime } from "node:process"; + +let _hrStart = hrtime.bigint(); + +export const clocksMonotonicClock = { + resolution () { + return 1n; + }, + + now () { + return hrtime.bigint() - _hrStart; + }, + + subscribe (_when, _absolute) { + console.log(`[monotonic-clock] Subscribe`); + } +}; + +export const clocksTimezone = { + display (timezone, when) { + console.log(`[timezone] DISPLAY ${timezone} ${when}`); + }, + + utcOffset (timezone, when) { + console.log(`[timezone] UTC OFFSET ${timezone} ${when}`); + return 0; + }, + + dropTimezone (timezone) { + console.log(`[timezone] DROP ${timezone}`); + } +}; + +export const clocksWallClock = { + now() { + const seconds = BigInt(Math.floor(Date.now() / 1e3)); + const nanoseconds = (Date.now() % 1e3) * 1e6; + return { seconds, nanoseconds }; + }, + + resolution() { + console.log(`[wall-clock] Wall clock resolution`); + } +}; + +export { + clocksMonotonicClock as monotonicClock, + clocksTimezone as timezone, + clocksWallClock as wallClock +} diff --git a/packages/preview2-shim/lib/nodejs/console.js b/packages/preview2-shim/lib/nodejs/console.js deleted file mode 100644 index b19b831f8..000000000 --- a/packages/preview2-shim/lib/nodejs/console.js +++ /dev/null @@ -1,12 +0,0 @@ -const levels = ["trace", "debug", "info", "warn", "error"]; - -let logLevel = levels.indexOf("warn"); - -export function setLevel(level) { - logLevel = levels.indexOf(level); -} - -export function log(level, context, msg) { - if (logLevel > levels.indexOf(level)) return; - process.stdout.write(`${level}: (${context}) ${msg}\n`); -} diff --git a/packages/preview2-shim/lib/nodejs/default-outgoing-HTTP.js b/packages/preview2-shim/lib/nodejs/default-outgoing-HTTP.js deleted file mode 100644 index bb24fa000..000000000 --- a/packages/preview2-shim/lib/nodejs/default-outgoing-HTTP.js +++ /dev/null @@ -1,3 +0,0 @@ -export function handle(_request, _options) { - console.log("[default-outgoing-HTTP] Handle"); -} diff --git a/packages/preview2-shim/lib/nodejs/http.js b/packages/preview2-shim/lib/nodejs/http.js index cedef0c69..fa5368dbb 100644 --- a/packages/preview2-shim/lib/nodejs/http.js +++ b/packages/preview2-shim/lib/nodejs/http.js @@ -17,3 +17,123 @@ export function send(req) { } throw new UnexpectedError(response); } + +export const httpIncomingHandler = { + handle () { + + } +}; + +export const httpOutgoingHandler = { + handle () { + + } +}; + +export const httpTypes = { + dropFields(_fields) { + console.log("[types] Drop fields"); + }, + newFields(_entries) { + console.log("[types] New fields"); + }, + fieldsGet(_fields, _name) { + console.log("[types] Fields get"); + }, + fieldsSet(_fields, _name, _value) { + console.log("[types] Fields set"); + }, + fieldsDelete(_fields, _name) { + console.log("[types] Fields delete"); + }, + fieldsAppend(_fields, _name, _value) { + console.log("[types] Fields append"); + }, + fieldsEntries(_fields) { + console.log("[types] Fields entries"); + }, + fieldsClone(_fields) { + console.log("[types] Fields clone"); + }, + finishIncomingStream(s) { + console.log(`[types] Finish incoming stream ${s}`); + }, + finishOutgoingStream(s, _trailers) { + console.log(`[types] Finish outgoing stream ${s}`); + }, + dropIncomingRequest(_req) { + console.log("[types] Drop incoming request"); + }, + dropOutgoingRequest(_req) { + console.log("[types] Drop outgoing request"); + }, + incomingRequestMethod(_req) { + console.log("[types] Incoming request method"); + }, + incomingRequestPath(_req) { + console.log("[types] Incoming request path"); + }, + incomingRequestQuery(_req) { + console.log("[types] Incoming request query"); + }, + incomingRequestScheme(_req) { + console.log("[types] Incoming request scheme"); + }, + incomingRequestAuthority(_req) { + console.log("[types] Incoming request authority"); + }, + incomingRequestHeaders(_req) { + console.log("[types] Incoming request headers"); + }, + incomingRequestConsume(_req) { + console.log("[types] Incoming request consume"); + }, + newOutgoingRequest(_method, _path, _query, _scheme, _authority, _headers) { + console.log("[types] New outgoing request"); + }, + outgoingRequestWrite(_req) { + console.log("[types] Outgoing request write"); + }, + dropResponseOutparam(_res) { + console.log("[types] Drop response outparam"); + }, + setResponseOutparam(_response) { + console.log("[types] Drop fields"); + }, + dropIncomingResponse(_res) { + console.log("[types] Drop incoming response"); + }, + dropOutgoingResponse(_res) { + console.log("[types] Drop outgoing response"); + }, + incomingResponseStatus(_res) { + console.log("[types] Incoming response status"); + }, + incomingResponseHeaders(_res) { + console.log("[types] Incoming response headers"); + }, + incomingResponseConsume(_res) { + console.log("[types] Incoming response consume"); + }, + newOutgoingResponse(_statusCode, _headers) { + console.log("[types] New outgoing response"); + }, + outgoingResponseWrite(_res) { + console.log("[types] Outgoing response write"); + }, + dropFutureIncomingResponse(_f) { + console.log("[types] Drop future incoming response"); + }, + futureIncomingResponseGet(_f) { + console.log("[types] Future incoming response get"); + }, + listenToFutureIncomingResponse(_f) { + console.log("[types] Listen to future incoming response"); + } +}; + +export { + httpIncomingHandler as incomingHandler, + httpOutgoingHandler as outgoingHandler, + httpTypes as types +} diff --git a/packages/preview2-shim/lib/nodejs/index.js b/packages/preview2-shim/lib/nodejs/index.js index 298f8f1dd..0935e25c6 100644 --- a/packages/preview2-shim/lib/nodejs/index.js +++ b/packages/preview2-shim/lib/nodejs/index.js @@ -1,17 +1,12 @@ import * as clocks from "./clocks.js"; -import * as environment from "./environment.js"; -import * as exit from "./exit.js"; import * as filesystem from "./filesystem.js"; -import * as monotonicClock from "./clocks.js"; +import * as http from "./http.js"; +import * as io from "./io.js"; +import * as logging from "./logging.js"; +import * as poll from "./poll.js"; import * as random from "./random.js"; -import * as streams from "./io.js"; -import * as tcpCreateSocket from "./tcp-create-socket.js"; -import * as tcp from "./tcp.js"; -import * as timezone from "./timezone.js"; -import * as types from "./types.js"; -import * as udpCreateSocket from "./udp-create-socket.js"; -import * as udp from "./udp.js"; -import * as wallClock from "./wall-clock.js"; +import * as sockets from "./sockets.js"; +import * as cliBase from "./cli-base.js"; export const importObject = { clocks, diff --git a/packages/preview2-shim/lib/nodejs/instance-network.js b/packages/preview2-shim/lib/nodejs/instance-network.js deleted file mode 100644 index 8c108b824..000000000 --- a/packages/preview2-shim/lib/nodejs/instance-network.js +++ /dev/null @@ -1,3 +0,0 @@ -export function instanceNetwork () { - -} diff --git a/packages/preview2-shim/lib/nodejs/ip-name-lookup.js b/packages/preview2-shim/lib/nodejs/ip-name-lookup.js deleted file mode 100644 index e1a449704..000000000 --- a/packages/preview2-shim/lib/nodejs/ip-name-lookup.js +++ /dev/null @@ -1,23 +0,0 @@ -export function dropResolveAddressStream () { - -} - -export function subscribe () { - -} - -export function resolveAddresses () { - -} - -export function resolveNextAddress () { - -} - -export function nonBlocking () { - -} - -export function setNonBlocking () { - -} diff --git a/packages/preview2-shim/lib/nodejs/logging.js b/packages/preview2-shim/lib/nodejs/logging.js new file mode 100644 index 000000000..25ae1d76d --- /dev/null +++ b/packages/preview2-shim/lib/nodejs/logging.js @@ -0,0 +1,16 @@ +const levels = ["trace", "debug", "info", "warn", "error"]; + +let logLevel = levels.indexOf("warn"); + +export const loggingHandler = { + log(level, context, msg) { + if (logLevel > levels.indexOf(level)) return; + process.stdout.write(`${level}: (${context}) ${msg}\n`); + } +}; + +export function setLevel(level) { + logLevel = levels.indexOf(level); +} + +export { loggingHandler as handler } diff --git a/packages/preview2-shim/lib/nodejs/monotonic-clock.js b/packages/preview2-shim/lib/nodejs/monotonic-clock.js deleted file mode 100644 index 932fd83d4..000000000 --- a/packages/preview2-shim/lib/nodejs/monotonic-clock.js +++ /dev/null @@ -1,14 +0,0 @@ -import { hrtime } from "node:process"; - -export function resolution () { - return 1n; -} - -let _hrStart = hrtime.bigint(); -export function now () { - return hrtime.bigint() - _hrStart; -} - -export function subscribe (_when, _absolute) { - console.log(`[monotonic-clock] Subscribe`); -} diff --git a/packages/preview2-shim/lib/nodejs/network.js b/packages/preview2-shim/lib/nodejs/network.js deleted file mode 100644 index 0f1f0ea0d..000000000 --- a/packages/preview2-shim/lib/nodejs/network.js +++ /dev/null @@ -1,3 +0,0 @@ -export function dropNetwork () { - -} diff --git a/packages/preview2-shim/lib/nodejs/poll.js b/packages/preview2-shim/lib/nodejs/poll.js new file mode 100644 index 000000000..e66f24beb --- /dev/null +++ b/packages/preview2-shim/lib/nodejs/poll.js @@ -0,0 +1,11 @@ +export const pollPoll = { + dropPollable (pollable) { + console.log(`[poll] Drop (${pollable})`); + }, + pollOneoff (input) { + console.log(`[poll] Oneoff (${input})`); + return []; + } +}; + +export { pollPoll as poll } diff --git a/packages/preview2-shim/lib/nodejs/sockets.js b/packages/preview2-shim/lib/nodejs/sockets.js new file mode 100644 index 000000000..210e6ccb3 --- /dev/null +++ b/packages/preview2-shim/lib/nodejs/sockets.js @@ -0,0 +1,203 @@ +export const socketsInstanceNetwork = { + instanceNetwork () { + console.log(`[sockets] instance network`); + } +}; + +export const socketsIpNameLookup = { + dropResolveAddressStream () { + + }, + subscribe () { + + }, + resolveAddresses () { + + }, + resolveNextAddress () { + + }, + nonBlocking () { + + }, + setNonBlocking () { + + }, +}; + +export const socketsNetwork = { + dropNetwork () { + + } +}; + +export const socketsTcpCreateSocket = { + createTcpSocket () { + + } +}; + +export const socketsTcp = { + subscribe () { + + }, + dropTcpSocket() { + + }, + bind() { + + }, + connect() { + + }, + listen() { + + }, + accept() { + + }, + localAddress() { + + }, + remoteAddress() { + + }, + addressFamily() { + + }, + ipv6Only() { + + }, + setIpv6Only() { + + }, + setListenBacklogSize() { + + }, + keepAlive() { + + }, + setKeepAlive() { + + }, + noDelay() { + + }, + setNoDelay() { + + }, + unicastHopLimit() { + + }, + setUnicastHopLimit() { + + }, + receiveBufferSize() { + + }, + setReceiveBufferSize() { + + }, + sendBufferSize() { + + }, + setSendBufferSize() { + + }, + nonBlocking() { + + }, + setNonBlocking() { + + }, + shutdown() { + + } +}; + +export const socketsUdp = { + subscribe () { + + }, + + dropUdpSocket () { + + }, + + bind () { + + }, + + connect () { + + }, + + receive () { + + }, + + send () { + + }, + + localAddress () { + + }, + + remoteAddress () { + + }, + + addressFamily () { + + }, + + ipv6Only () { + + }, + + setIpv6Only () { + + }, + + unicastHopLimit () { + + }, + + setUnicastHopLimit () { + + }, + + receiveBufferSize () { + + }, + + setReceiveBufferSize () { + + }, + + sendBufferSize () { + + }, + + setSendBufferSize () { + + }, + + nonBlocking () { + + }, + + setNonBlocking () { + + } +}; + +export { + socketsInstanceNetwork as instanceNetwork, + socketsIpNameLookup as ipNameLookup, + socketsNetwork as network, + socketsTcpCreateSocket as tcpCreateSocket, + socketsTcp as tcp, + socketsUdp as udp +} diff --git a/packages/preview2-shim/lib/nodejs/tcp-create-socket.js b/packages/preview2-shim/lib/nodejs/tcp-create-socket.js deleted file mode 100644 index 0cf6401dd..000000000 --- a/packages/preview2-shim/lib/nodejs/tcp-create-socket.js +++ /dev/null @@ -1,3 +0,0 @@ -export function createTcpSocket () { - -} \ No newline at end of file diff --git a/packages/preview2-shim/lib/nodejs/tcp.js b/packages/preview2-shim/lib/nodejs/tcp.js deleted file mode 100644 index 45dbf9d98..000000000 --- a/packages/preview2-shim/lib/nodejs/tcp.js +++ /dev/null @@ -1,75 +0,0 @@ -export function subscribe () { - -} -export function dropTcpSocket() { - -} -export function bind() { - -} -export function connect() { - -} -export function listen() { - -} -export function accept() { - -} -export function localAddress() { - -} -export function remoteAddress() { - -} -export function addressFamily() { - -} -export function ipv6Only() { - -} -export function setIpv6Only() { - -} -export function setListenBacklogSize() { - -} -export function keepAlive() { - -} -export function setKeepAlive() { - -} -export function noDelay() { - -} -export function setNoDelay() { - -} -export function unicastHopLimit() { - -} -export function setUnicastHopLimit() { - -} -export function receiveBufferSize() { - -} -export function setReceiveBufferSize() { - -} -export function sendBufferSize() { - -} -export function setSendBufferSize() { - -} -export function nonBlocking() { - -} -export function setNonBlocking() { - -} -export function shutdown() { - -} \ No newline at end of file diff --git a/packages/preview2-shim/lib/nodejs/timezone.js b/packages/preview2-shim/lib/nodejs/timezone.js deleted file mode 100644 index 7568396f0..000000000 --- a/packages/preview2-shim/lib/nodejs/timezone.js +++ /dev/null @@ -1,12 +0,0 @@ -export function display (timezone, when) { - console.log(`[timezone] DISPLAY ${timezone} ${when}`); -} - -export function utcOffset (timezone, when) { - console.log(`[timezone] UTC OFFSET ${timezone} ${when}`); - return 0; -} - -export function dropTimezone (timezone) { - console.log(`[timezone] DROP ${timezone}`); -} diff --git a/packages/preview2-shim/lib/nodejs/types.js b/packages/preview2-shim/lib/nodejs/types.js deleted file mode 100644 index 722d31c59..000000000 --- a/packages/preview2-shim/lib/nodejs/types.js +++ /dev/null @@ -1,99 +0,0 @@ -export function dropFields(_fields) { - console.log("[types] Drop fields"); -} -export function newFields(_entries) { - console.log("[types] New fields"); -} -export function fieldsGet(_fields, _name) { - console.log("[types] Fields get"); -} -export function fieldsSet(_fields, _name, _value) { - console.log("[types] Fields set"); -} -export function fieldsDelete(_fields, _name) { - console.log("[types] Fields delete"); -} -export function fieldsAppend(_fields, _name, _value) { - console.log("[types] Fields append"); -} -export function fieldsEntries(_fields) { - console.log("[types] Fields entries"); -} -export function fieldsClone(_fields) { - console.log("[types] Fields clone"); -} -export function finishIncomingStream(s) { - console.log(`[types] Finish incoming stream ${s}`); -} -export function finishOutgoingStream(s, _trailers) { - console.log(`[types] Finish outgoing stream ${s}`); -} -export function dropIncomingRequest(_req) { - console.log("[types] Drop incoming request"); -} -export function dropOutgoingRequest(_req) { - console.log("[types] Drop outgoing request"); -} -export function incomingRequestMethod(_req) { - console.log("[types] Incoming request method"); -} -export function incomingRequestPath(_req) { - console.log("[types] Incoming request path"); -} -export function incomingRequestQuery(_req) { - console.log("[types] Incoming request query"); -} -export function incomingRequestScheme(_req) { - console.log("[types] Incoming request scheme"); -} -export function incomingRequestAuthority(_req) { - console.log("[types] Incoming request authority"); -} -export function incomingRequestHeaders(_req) { - console.log("[types] Incoming request headers"); -} -export function incomingRequestConsume(_req) { - console.log("[types] Incoming request consume"); -} -export function newOutgoingRequest(_method, _path, _query, _scheme, _authority, _headers) { - console.log("[types] New outgoing request"); -} -export function outgoingRequestWrite(_req) { - console.log("[types] Outgoing request write"); -} -export function dropResponseOutparam(_res) { - console.log("[types] Drop response outparam"); -} -export function setResponseOutparam(_response) { - console.log("[types] Drop fields"); -} -export function dropIncomingResponse(_res) { - console.log("[types] Drop incoming response"); -} -export function dropOutgoingResponse(_res) { - console.log("[types] Drop outgoing response"); -} -export function incomingResponseStatus(_res) { - console.log("[types] Incoming response status"); -} -export function incomingResponseHeaders(_res) { - console.log("[types] Incoming response headers"); -} -export function incomingResponseConsume(_res) { - console.log("[types] Incoming response consume"); -} -export function newOutgoingResponse(_statusCode, _headers) { - console.log("[types] New outgoing response"); -} -export function outgoingResponseWrite(_res) { - console.log("[types] Outgoing response write"); -} -export function dropFutureIncomingResponse(_f) { - console.log("[types] Drop future incoming response"); -} -export function futureIncomingResponseGet(_f) { - console.log("[types] Future incoming response get"); -} -export function listenToFutureIncomingResponse(_f) { - console.log("[types] Listen to future incoming response"); -} diff --git a/packages/preview2-shim/lib/nodejs/udp-create-socket.js b/packages/preview2-shim/lib/nodejs/udp-create-socket.js deleted file mode 100644 index b0a86b496..000000000 --- a/packages/preview2-shim/lib/nodejs/udp-create-socket.js +++ /dev/null @@ -1,3 +0,0 @@ -export function createUdpSocket () { - -} diff --git a/packages/preview2-shim/lib/nodejs/udp.js b/packages/preview2-shim/lib/nodejs/udp.js deleted file mode 100644 index 9f47bc4d4..000000000 --- a/packages/preview2-shim/lib/nodejs/udp.js +++ /dev/null @@ -1,75 +0,0 @@ -export function subscribe () { - -} - -export function dropUdpSocket () { - -} - -export function bind () { - -} - -export function connect () { - -} - -export function receive () { - -} - -export function send () { - -} - -export function localAddress () { - -} - -export function remoteAddress () { - -} - -export function addressFamily () { - -} - -export function ipv6Only () { - -} - -export function setIpv6Only () { - -} - -export function unicastHopLimit () { - -} - -export function setUnicastHopLimit () { - -} - -export function receiveBufferSize () { - -} - -export function setReceiveBufferSize () { - -} - -export function sendBufferSize () { - -} - -export function setSendBufferSize () { - -} - -export function nonBlocking () { - -} - -export function setNonBlocking () { - -} \ No newline at end of file diff --git a/packages/preview2-shim/lib/nodejs/wall-clock.js b/packages/preview2-shim/lib/nodejs/wall-clock.js deleted file mode 100644 index 0ff3988f2..000000000 --- a/packages/preview2-shim/lib/nodejs/wall-clock.js +++ /dev/null @@ -1,9 +0,0 @@ -export function now() { - const seconds = BigInt(Math.floor(Date.now() / 1e3)); - const nanoseconds = (Date.now() % 1e3) * 1e6; - return { seconds, nanoseconds }; -} - -export function resolution() { - console.log(`[wall-clock] Wall clock resolution`); -} diff --git a/packages/preview2-shim/test/test.js b/packages/preview2-shim/test/test.js index b1e9308b7..deab6ad5e 100644 --- a/packages/preview2-shim/test/test.js +++ b/packages/preview2-shim/test/test.js @@ -3,6 +3,6 @@ import { ok } from 'node:assert'; suite('Node.js', async () => { test('Basic importing', async () => { const { default: wasiImport } = await import('@bytecodealliance/preview2-shim'); - ok(wasiImport['filesystem'].stat); + ok(wasiImport['filesystem'].filesystem.stat); }); }); diff --git a/packages/preview2-shim/types/imports/cli-base-environment.d.ts b/packages/preview2-shim/types/imports/cli-base-environment.d.ts index e47855763..cc8992eb9 100644 --- a/packages/preview2-shim/types/imports/cli-base-environment.d.ts +++ b/packages/preview2-shim/types/imports/cli-base-environment.d.ts @@ -1,5 +1,5 @@ export namespace CliBaseEnvironment { - export function /** + /** * Get the POSIX-style environment variables. * * Each environment variable is provided as a pair of string variable names @@ -9,9 +9,9 @@ export namespace CliBaseEnvironment { * in the component model, this import function should return the same * values each time it is called. */ - getEnvironment(): [string, string][]; - export function /** + export function getEnvironment(): [string, string][]; + /** * Get the POSIX-style arguments to the program. */ - getArguments(): string[]; + export function getArguments(): string[]; } diff --git a/packages/preview2-shim/types/imports/cli-base-exit.d.ts b/packages/preview2-shim/types/imports/cli-base-exit.d.ts index 9841ba8a8..ef0e388ee 100644 --- a/packages/preview2-shim/types/imports/cli-base-exit.d.ts +++ b/packages/preview2-shim/types/imports/cli-base-exit.d.ts @@ -1,7 +1,7 @@ export namespace CliBaseExit { - export function /** + /** * Exit the curerent instance and any linked instances. */ - exit(status: Result): void; + export function exit(status: Result): void; } export type Result = { tag: 'ok', val: T } | { tag: 'err', val: E }; diff --git a/packages/preview2-shim/types/imports/cli-base-preopens.d.ts b/packages/preview2-shim/types/imports/cli-base-preopens.d.ts index 23f2b3e5d..a5432ad87 100644 --- a/packages/preview2-shim/types/imports/cli-base-preopens.d.ts +++ b/packages/preview2-shim/types/imports/cli-base-preopens.d.ts @@ -1,8 +1,8 @@ export namespace CliBasePreopens { - export function /** + /** * Return the set of of preopened directories, and their path. */ - getDirectories(): [Descriptor, string][]; + export function getDirectories(): [Descriptor, string][]; } import type { Descriptor } from '../imports/filesystem'; export { Descriptor }; diff --git a/packages/preview2-shim/types/imports/clocks-monotonic-clock.d.ts b/packages/preview2-shim/types/imports/clocks-monotonic-clock.d.ts index f5491d5bf..c9e438f01 100644 --- a/packages/preview2-shim/types/imports/clocks-monotonic-clock.d.ts +++ b/packages/preview2-shim/types/imports/clocks-monotonic-clock.d.ts @@ -1,20 +1,20 @@ export namespace ClocksMonotonicClock { - export function /** + /** * Read the current value of the clock. * * The clock is monotonic, therefore calling this function repeatedly will * produce a sequence of non-decreasing values. */ - now(): Instant; - export function /** + export function now(): Instant; + /** * Query the resolution of the clock. */ - resolution(): Instant; - export function /** + export function resolution(): Instant; + /** * Create a `pollable` which will resolve once the specified time has been * reached. */ - subscribe(when: Instant, absolute: boolean): Pollable; + export function subscribe(when: Instant, absolute: boolean): Pollable; } import type { Pollable } from '../imports/poll'; export { Pollable }; diff --git a/packages/preview2-shim/types/imports/clocks-timezone.d.ts b/packages/preview2-shim/types/imports/clocks-timezone.d.ts index 083612e6e..c134105e0 100644 --- a/packages/preview2-shim/types/imports/clocks-timezone.d.ts +++ b/packages/preview2-shim/types/imports/clocks-timezone.d.ts @@ -1,5 +1,5 @@ export namespace ClocksTimezone { - export function /** + /** * Return information needed to display the given `datetime`. This includes * the UTC offset, the time zone name, and a flag indicating whether * daylight saving time is active. @@ -8,16 +8,16 @@ export namespace ClocksTimezone { * `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight * saving time. */ - display(this: Timezone, when: Datetime): TimezoneDisplay; - export function /** + export function display(this: Timezone, when: Datetime): TimezoneDisplay; + /** * The same as `display`, but only return the UTC offset. */ - utcOffset(this: Timezone, when: Datetime): number; - export function /** + export function utcOffset(this: Timezone, when: Datetime): number; + /** * Dispose of the specified input-stream, after which it may no longer * be used. */ - dropTimezone(this: Timezone): void; + export function dropTimezone(this: Timezone): void; } import type { Datetime } from '../imports/wall-clock'; export { Datetime }; diff --git a/packages/preview2-shim/types/imports/clocks-wall-clock.d.ts b/packages/preview2-shim/types/imports/clocks-wall-clock.d.ts index 9d16dd14c..4faaab18e 100644 --- a/packages/preview2-shim/types/imports/clocks-wall-clock.d.ts +++ b/packages/preview2-shim/types/imports/clocks-wall-clock.d.ts @@ -1,5 +1,5 @@ export namespace ClocksWallClock { - export function /** + /** * Read the current value of the clock. * * This clock is not monotonic, therefore calling this function repeatedly @@ -14,13 +14,13 @@ export namespace ClocksWallClock { * [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 * [Unix Time]: https://en.wikipedia.org/wiki/Unix_time */ - now(): Datetime; - export function /** + export function now(): Datetime; + /** * Query the resolution of the clock. * * The nanoseconds field of the output is always less than 1000000000. */ - resolution(): Datetime; + export function resolution(): Datetime; } /** * A time and date in seconds plus nanoseconds. diff --git a/packages/preview2-shim/types/imports/filesystem-filesystem.d.ts b/packages/preview2-shim/types/imports/filesystem-filesystem.d.ts index 8f4b750a6..d8e492623 100644 --- a/packages/preview2-shim/types/imports/filesystem-filesystem.d.ts +++ b/packages/preview2-shim/types/imports/filesystem-filesystem.d.ts @@ -1,5 +1,5 @@ export namespace FilesystemFilesystem { - export function /** + /** * Return a stream for reading from a file. * * Multiple read, write, and append streams may be active on the same open @@ -7,28 +7,28 @@ export namespace FilesystemFilesystem { * * Note: This allows using `wasi:io/streams.read`, which is similar to `read` in POSIX. */ - readViaStream(this: Descriptor, offset: Filesize): InputStream; - export function /** + export function readViaStream(this: Descriptor, offset: Filesize): InputStream; + /** * Return a stream for writing to a file. * * Note: This allows using `wasi:io/streams.write`, which is similar to `write` in * POSIX. */ - writeViaStream(this: Descriptor, offset: Filesize): OutputStream; - export function /** + export function writeViaStream(this: Descriptor, offset: Filesize): OutputStream; + /** * Return a stream for appending to a file. * * Note: This allows using `wasi:io/streams.write`, which is similar to `write` with * `O_APPEND` in in POSIX. */ - appendViaStream(this: Descriptor): OutputStream; - export function /** + export function appendViaStream(this: Descriptor): OutputStream; + /** * Provide file advisory information on a descriptor. * * This is similar to `posix_fadvise` in POSIX. */ - advise(this: Descriptor, offset: Filesize, length: Filesize, advice: Advice): void; - export function /** + export function advise(this: Descriptor, offset: Filesize, length: Filesize, advice: Advice): void; + /** * Synchronize the data of a file to disk. * * This function succeeds with no effect if the file descriptor is not @@ -36,8 +36,8 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `fdatasync` in POSIX. */ - syncData(this: Descriptor): void; - export function /** + export function syncData(this: Descriptor): void; + /** * Get flags associated with a descriptor. * * Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. @@ -45,8 +45,8 @@ export namespace FilesystemFilesystem { * Note: This returns the value that was the `fs_flags` value returned * from `fdstat_get` in earlier versions of WASI. */ - getFlags(this: Descriptor): DescriptorFlags; - export function /** + export function getFlags(this: Descriptor): DescriptorFlags; + /** * Get the dynamic type of a descriptor. * * Note: This returns the same value as the `type` field of the `fd-stat` @@ -58,23 +58,23 @@ export namespace FilesystemFilesystem { * Note: This returns the value that was the `fs_filetype` value returned * from `fdstat_get` in earlier versions of WASI. */ - getType(this: Descriptor): DescriptorType; - export function /** + export function getType(this: Descriptor): DescriptorType; + /** * Adjust the size of an open file. If this increases the file's size, the * extra bytes are filled with zeros. * * Note: This was called `fd_filestat_set_size` in earlier versions of WASI. */ - setSize(this: Descriptor, size: Filesize): void; - export function /** + export function setSize(this: Descriptor, size: Filesize): void; + /** * Adjust the timestamps of an open file or directory. * * Note: This is similar to `futimens` in POSIX. * * Note: This was called `fd_filestat_set_times` in earlier versions of WASI. */ - setTimes(this: Descriptor, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; - export function /** + export function setTimes(this: Descriptor, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; + /** * Read from a descriptor, without using and updating the descriptor's offset. * * This function returns a list of bytes containing the data that was @@ -87,8 +87,8 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `pread` in POSIX. */ - read(this: Descriptor, length: Filesize, offset: Filesize): [Uint8Array | ArrayBuffer, boolean]; - export function /** + export function read(this: Descriptor, length: Filesize, offset: Filesize): [Uint8Array | ArrayBuffer, boolean]; + /** * Write to a descriptor, without using and updating the descriptor's offset. * * It is valid to write past the end of a file; the file is extended to the @@ -99,8 +99,8 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `pwrite` in POSIX. */ - write(this: Descriptor, buffer: Uint8Array, offset: Filesize): Filesize; - export function /** + export function write(this: Descriptor, buffer: Uint8Array, offset: Filesize): Filesize; + /** * Read directory entries from a directory. * * On filesystems where directories contain entries referring to themselves @@ -111,8 +111,8 @@ export namespace FilesystemFilesystem { * directory. Multiple streams may be active on the same directory, and they * do not interfere with each other. */ - readDirectory(this: Descriptor): DirectoryEntryStream; - export function /** + export function readDirectory(this: Descriptor): DirectoryEntryStream; + /** * Synchronize the data and metadata of a file to disk. * * This function succeeds with no effect if the file descriptor is not @@ -120,30 +120,30 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `fsync` in POSIX. */ - sync(this: Descriptor): void; - export function /** + export function sync(this: Descriptor): void; + /** * Create a directory. * * Note: This is similar to `mkdirat` in POSIX. */ - createDirectoryAt(this: Descriptor, path: string): void; - export function /** + export function createDirectoryAt(this: Descriptor, path: string): void; + /** * Return the attributes of an open file or directory. * * Note: This is similar to `fstat` in POSIX. * * Note: This was called `fd_filestat_get` in earlier versions of WASI. */ - stat(this: Descriptor): DescriptorStat; - export function /** + export function stat(this: Descriptor): DescriptorStat; + /** * Return the attributes of a file or directory. * * Note: This is similar to `fstatat` in POSIX. * * Note: This was called `path_filestat_get` in earlier versions of WASI. */ - statAt(this: Descriptor, pathFlags: PathFlags, path: string): DescriptorStat; - export function /** + export function statAt(this: Descriptor, pathFlags: PathFlags, path: string): DescriptorStat; + /** * Adjust the timestamps of a file or directory. * * Note: This is similar to `utimensat` in POSIX. @@ -151,14 +151,14 @@ export namespace FilesystemFilesystem { * Note: This was called `path_filestat_set_times` in earlier versions of * WASI. */ - setTimesAt(this: Descriptor, pathFlags: PathFlags, path: string, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; - export function /** + export function setTimesAt(this: Descriptor, pathFlags: PathFlags, path: string, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; + /** * Create a hard link. * * Note: This is similar to `linkat` in POSIX. */ - linkAt(this: Descriptor, oldPathFlags: PathFlags, oldPath: string, newDescriptor: Descriptor, newPath: string): void; - export function /** + export function linkAt(this: Descriptor, oldPathFlags: PathFlags, oldPath: string, newDescriptor: Descriptor, newPath: string): void; + /** * Open a file or directory. * * The returned descriptor is not guaranteed to be the lowest-numbered @@ -178,8 +178,8 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `openat` in POSIX. */ - openAt(this: Descriptor, pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: DescriptorFlags, modes: Modes): Descriptor; - export function /** + export function openAt(this: Descriptor, pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: DescriptorFlags, modes: Modes): Descriptor; + /** * Read the contents of a symbolic link. * * If the contents contain an absolute or rooted path in the underlying @@ -187,22 +187,22 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `readlinkat` in POSIX. */ - readlinkAt(this: Descriptor, path: string): string; - export function /** + export function readlinkAt(this: Descriptor, path: string): string; + /** * Remove a directory. * * Return `error-code::not-empty` if the directory is not empty. * * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. */ - removeDirectoryAt(this: Descriptor, path: string): void; - export function /** + export function removeDirectoryAt(this: Descriptor, path: string): void; + /** * Rename a filesystem object. * * Note: This is similar to `renameat` in POSIX. */ - renameAt(this: Descriptor, oldPath: string, newDescriptor: Descriptor, newPath: string): void; - export function /** + export function renameAt(this: Descriptor, oldPath: string, newDescriptor: Descriptor, newPath: string): void; + /** * Create a symbolic link (also known as a "symlink"). * * If `old-path` starts with `/`, the function fails with @@ -210,8 +210,8 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `symlinkat` in POSIX. */ - symlinkAt(this: Descriptor, oldPath: string, newPath: string): void; - export function /** + export function symlinkAt(this: Descriptor, oldPath: string, newPath: string): void; + /** * Check accessibility of a filesystem path. * * Check whether the given filesystem path names an object which is @@ -223,15 +223,15 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `faccessat` with the `AT_EACCESS` flag in POSIX. */ - accessAt(this: Descriptor, pathFlags: PathFlags, path: string, type: AccessType): void; - export function /** + export function accessAt(this: Descriptor, pathFlags: PathFlags, path: string, type: AccessType): void; + /** * Unlink a filesystem object that is not a directory. * * Return `error-code::is-directory` if the path refers to a directory. * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. */ - unlinkFileAt(this: Descriptor, path: string): void; - export function /** + export function unlinkFileAt(this: Descriptor, path: string): void; + /** * Change the permissions of a filesystem object that is not a directory. * * Note that the ultimate meanings of these permissions is @@ -239,8 +239,8 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `fchmodat` in POSIX. */ - changeFilePermissionsAt(this: Descriptor, pathFlags: PathFlags, path: string, modes: Modes): void; - export function /** + export function changeFilePermissionsAt(this: Descriptor, pathFlags: PathFlags, path: string, modes: Modes): void; + /** * Change the permissions of a directory. * * Note that the ultimate meanings of these permissions is @@ -252,8 +252,8 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `fchmodat` in POSIX. */ - changeDirectoryPermissionsAt(this: Descriptor, pathFlags: PathFlags, path: string, modes: Modes): void; - export function /** + export function changeDirectoryPermissionsAt(this: Descriptor, pathFlags: PathFlags, path: string, modes: Modes): void; + /** * Request a shared advisory lock for an open file. * * This requests a *shared* lock; more than one shared lock can be held for @@ -275,8 +275,8 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `flock(fd, LOCK_SH)` in Unix. */ - lockShared(this: Descriptor): void; - export function /** + export function lockShared(this: Descriptor): void; + /** * Request an exclusive advisory lock for an open file. * * This requests an *exclusive* lock; no other locks may be held for the @@ -300,8 +300,8 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `flock(fd, LOCK_EX)` in Unix. */ - lockExclusive(this: Descriptor): void; - export function /** + export function lockExclusive(this: Descriptor): void; + /** * Request a shared advisory lock for an open file. * * This requests a *shared* lock; more than one shared lock can be held for @@ -324,8 +324,8 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `flock(fd, LOCK_SH | LOCK_NB)` in Unix. */ - tryLockShared(this: Descriptor): void; - export function /** + export function tryLockShared(this: Descriptor): void; + /** * Request an exclusive advisory lock for an open file. * * This requests an *exclusive* lock; no other locks may be held for the @@ -350,27 +350,27 @@ export namespace FilesystemFilesystem { * * Note: This is similar to `flock(fd, LOCK_EX | LOCK_NB)` in Unix. */ - tryLockExclusive(this: Descriptor): void; - export function /** + export function tryLockExclusive(this: Descriptor): void; + /** * Release a shared or exclusive lock on an open file. * * Note: This is similar to `flock(fd, LOCK_UN)` in Unix. */ - unlock(this: Descriptor): void; - export function /** + export function unlock(this: Descriptor): void; + /** * Dispose of the specified `descriptor`, after which it may no longer * be used. */ - dropDescriptor(this: Descriptor): void; - export function /** + export function dropDescriptor(this: Descriptor): void; + /** * Read a single directory entry from a `directory-entry-stream`. */ - readDirectoryEntry(this: DirectoryEntryStream): DirectoryEntry | null; - export function /** + export function readDirectoryEntry(this: DirectoryEntryStream): DirectoryEntry | null; + /** * Dispose of the specified `directory-entry-stream`, after which it may no longer * be used. */ - dropDirectoryEntryStream(this: DirectoryEntryStream): void; + export function dropDirectoryEntryStream(this: DirectoryEntryStream): void; } import type { InputStream } from '../imports/streams'; export { InputStream }; diff --git a/packages/preview2-shim/types/imports/io-streams.d.ts b/packages/preview2-shim/types/imports/io-streams.d.ts index e083c4611..dd05fd8ec 100644 --- a/packages/preview2-shim/types/imports/io-streams.d.ts +++ b/packages/preview2-shim/types/imports/io-streams.d.ts @@ -1,5 +1,5 @@ export namespace IoStreams { - export function /** + /** * Read bytes from a stream. * * This function returns a list of bytes containing the data that was @@ -19,15 +19,15 @@ export namespace IoStreams { * a buffer as large as that would imply. * FIXME: describe what happens if allocation fails. */ - read(this: InputStream, len: bigint): [Uint8Array | ArrayBuffer, boolean]; - export function /** + export function read(this: InputStream, len: bigint): [Uint8Array | ArrayBuffer, boolean]; + /** * Read bytes from a stream, with blocking. * * This is similar to `read`, except that it blocks until at least one * byte can be read. */ - blockingRead(this: InputStream, len: bigint): [Uint8Array | ArrayBuffer, boolean]; - export function /** + export function blockingRead(this: InputStream, len: bigint): [Uint8Array | ArrayBuffer, boolean]; + /** * Skip bytes from a stream. * * This is similar to the `read` function, but avoids copying the @@ -41,54 +41,54 @@ export namespace IoStreams { * indicating whether the end of the stream was reached. The returned * value will be at most `len`; it may be less. */ - skip(this: InputStream, len: bigint): [bigint, boolean]; - export function /** + export function skip(this: InputStream, len: bigint): [bigint, boolean]; + /** * Skip bytes from a stream, with blocking. * * This is similar to `skip`, except that it blocks until at least one * byte can be consumed. */ - blockingSkip(this: InputStream, len: bigint): [bigint, boolean]; - export function /** + export function blockingSkip(this: InputStream, len: bigint): [bigint, boolean]; + /** * Create a `pollable` which will resolve once either the specified stream * has bytes available to read or the other end of the stream has been * closed. */ - subscribeToInputStream(this: InputStream): Pollable; - export function /** + export function subscribeToInputStream(this: InputStream): Pollable; + /** * Dispose of the specified `input-stream`, after which it may no longer * be used. */ - dropInputStream(this: InputStream): void; - export function /** + export function dropInputStream(this: InputStream): void; + /** * Write bytes to a stream. * * This function returns a `u64` indicating the number of bytes from * `buf` that were written; it may be less than the full list. */ - write(this: OutputStream, buf: Uint8Array): bigint; - export function /** + export function write(this: OutputStream, buf: Uint8Array): bigint; + /** * Write bytes to a stream, with blocking. * * This is similar to `write`, except that it blocks until at least one * byte can be written. */ - blockingWrite(this: OutputStream, buf: Uint8Array): bigint; - export function /** + export function blockingWrite(this: OutputStream, buf: Uint8Array): bigint; + /** * Write multiple zero bytes to a stream. * * This function returns a `u64` indicating the number of zero bytes * that were written; it may be less than `len`. */ - writeZeroes(this: OutputStream, len: bigint): bigint; - export function /** + export function writeZeroes(this: OutputStream, len: bigint): bigint; + /** * Write multiple zero bytes to a stream, with blocking. * * This is similar to `write-zeroes`, except that it blocks until at least * one byte can be written. */ - blockingWriteZeroes(this: OutputStream, len: bigint): bigint; - export function /** + export function blockingWriteZeroes(this: OutputStream, len: bigint): bigint; + /** * Read from one stream and write to another. * * This function returns the number of bytes transferred; it may be less @@ -97,15 +97,15 @@ export namespace IoStreams { * Unlike other I/O functions, this function blocks until all the data * read from the input stream has been written to the output stream. */ - splice(this: OutputStream, src: InputStream, len: bigint): [bigint, boolean]; - export function /** + export function splice(this: OutputStream, src: InputStream, len: bigint): [bigint, boolean]; + /** * Read from one stream and write to another, with blocking. * * This is similar to `splice`, except that it blocks until at least * one byte can be read. */ - blockingSplice(this: OutputStream, src: InputStream, len: bigint): [bigint, boolean]; - export function /** + export function blockingSplice(this: OutputStream, src: InputStream, len: bigint): [bigint, boolean]; + /** * Forward the entire contents of an input stream to an output stream. * * This function repeatedly reads from the input stream and writes @@ -118,17 +118,17 @@ export namespace IoStreams { * * This function returns the number of bytes transferred. */ - forward(this: OutputStream, src: InputStream): bigint; - export function /** + export function forward(this: OutputStream, src: InputStream): bigint; + /** * Create a `pollable` which will resolve once either the specified stream * is ready to accept bytes or the other end of the stream has been closed. */ - subscribeToOutputStream(this: OutputStream): Pollable; - export function /** + export function subscribeToOutputStream(this: OutputStream): Pollable; + /** * Dispose of the specified `output-stream`, after which it may no longer * be used. */ - dropOutputStream(this: OutputStream): void; + export function dropOutputStream(this: OutputStream): void; } import type { Pollable } from '../imports/poll'; export { Pollable }; diff --git a/packages/preview2-shim/types/imports/logging-handler.d.ts b/packages/preview2-shim/types/imports/logging-handler.d.ts index a5ed00d21..7052f0dac 100644 --- a/packages/preview2-shim/types/imports/logging-handler.d.ts +++ b/packages/preview2-shim/types/imports/logging-handler.d.ts @@ -1,5 +1,5 @@ export namespace LoggingHandler { - export function /** + /** * Emit a log message. * * A log message has a `level` describing what kind of message is being @@ -7,7 +7,7 @@ export namespace LoggingHandler { * consumers group similar messages, and a string containing the message * text. */ - log(level: Level, context: string, message: string): void; + export function log(level: Level, context: string, message: string): void; } /** * A log level, describing a kind of message. diff --git a/packages/preview2-shim/types/imports/poll-poll.d.ts b/packages/preview2-shim/types/imports/poll-poll.d.ts index 329891d28..ff65dab76 100644 --- a/packages/preview2-shim/types/imports/poll-poll.d.ts +++ b/packages/preview2-shim/types/imports/poll-poll.d.ts @@ -1,10 +1,10 @@ export namespace PollPoll { - export function /** + /** * Dispose of the specified `pollable`, after which it may no longer * be used. */ - dropPollable(this: Pollable): void; - export function /** + export function dropPollable(this: Pollable): void; + /** * Poll for completion on a set of pollables. * * The "oneoff" in the name refers to the fact that this function must do a @@ -20,7 +20,7 @@ export namespace PollPoll { * for details. For now, we use zero to mean "not ready" and non-zero to * mean "ready". */ - pollOneoff(in: Uint32Array): Uint8Array | ArrayBuffer; + export function pollOneoff(in: Uint32Array): Uint8Array | ArrayBuffer; } /** * A "pollable" handle. diff --git a/packages/preview2-shim/types/imports/random-insecure-seed.d.ts b/packages/preview2-shim/types/imports/random-insecure-seed.d.ts index cf34f3dac..a5673336f 100644 --- a/packages/preview2-shim/types/imports/random-insecure-seed.d.ts +++ b/packages/preview2-shim/types/imports/random-insecure-seed.d.ts @@ -1,5 +1,5 @@ export namespace RandomInsecureSeed { - export function /** + /** * Return a 128-bit value that may contain a pseudo-random value. * * The returned value is not required to be computed from a CSPRNG, and may @@ -18,5 +18,5 @@ export namespace RandomInsecureSeed { * called multiple times and potentially used for purposes other than DoS * protection. */ - insecureSeed(): [bigint, bigint]; + export function insecureSeed(): [bigint, bigint]; } diff --git a/packages/preview2-shim/types/imports/random-insecure.d.ts b/packages/preview2-shim/types/imports/random-insecure.d.ts index 9e5cadb92..05911a7d1 100644 --- a/packages/preview2-shim/types/imports/random-insecure.d.ts +++ b/packages/preview2-shim/types/imports/random-insecure.d.ts @@ -1,5 +1,5 @@ export namespace RandomInsecure { - export function /** + /** * Return `len` insecure pseudo-random bytes. * * This function is not cryptographically secure. Do not use it for @@ -9,12 +9,12 @@ export namespace RandomInsecure { * implementations are encouraged to return evenly distributed values with * a long period. */ - getInsecureRandomBytes(len: bigint): Uint8Array | ArrayBuffer; - export function /** + export function getInsecureRandomBytes(len: bigint): Uint8Array | ArrayBuffer; + /** * Return an insecure pseudo-random `u64` value. * * This function returns the same type of pseudo-random data as * `get-insecure-random-bytes`, represented as a `u64`. */ - getInsecureRandomU64(): bigint; + export function getInsecureRandomU64(): bigint; } diff --git a/packages/preview2-shim/types/imports/random-random.d.ts b/packages/preview2-shim/types/imports/random-random.d.ts index 63ef6023a..6fcc78545 100644 --- a/packages/preview2-shim/types/imports/random-random.d.ts +++ b/packages/preview2-shim/types/imports/random-random.d.ts @@ -1,5 +1,5 @@ export namespace RandomRandom { - export function /** + /** * Return `len` cryptographically-secure pseudo-random bytes. * * This function must produce data from an adequately seeded @@ -11,12 +11,12 @@ export namespace RandomRandom { * environments must omit this function, rather than implementing it with * deterministic data. */ - getRandomBytes(len: bigint): Uint8Array | ArrayBuffer; - export function /** + export function getRandomBytes(len: bigint): Uint8Array | ArrayBuffer; + /** * Return a cryptographically-secure pseudo-random `u64` value. * * This function returns the same type of pseudo-random data as * `get-random-bytes`, represented as a `u64`. */ - getRandomU64(): bigint; + export function getRandomU64(): bigint; } diff --git a/packages/preview2-shim/types/imports/sockets-instance-network.d.ts b/packages/preview2-shim/types/imports/sockets-instance-network.d.ts index c514e0d1d..1d5a6974b 100644 --- a/packages/preview2-shim/types/imports/sockets-instance-network.d.ts +++ b/packages/preview2-shim/types/imports/sockets-instance-network.d.ts @@ -1,8 +1,8 @@ export namespace SocketsInstanceNetwork { - export function /** + /** * Get a handle to the default network. */ - instanceNetwork(): Network; + export function instanceNetwork(): Network; } import type { Network } from '../imports/network'; export { Network }; diff --git a/packages/preview2-shim/types/imports/sockets-ip-name-lookup.d.ts b/packages/preview2-shim/types/imports/sockets-ip-name-lookup.d.ts index c45af8d09..4374f8438 100644 --- a/packages/preview2-shim/types/imports/sockets-ip-name-lookup.d.ts +++ b/packages/preview2-shim/types/imports/sockets-ip-name-lookup.d.ts @@ -1,5 +1,5 @@ export namespace SocketsIpNameLookup { - export function /** + /** * Resolve an internet host name to a list of IP addresses. * * See the wasi-socket proposal README.md for a comparison with getaddrinfo. @@ -31,8 +31,8 @@ export namespace SocketsIpNameLookup { * - * - */ - resolveAddresses(network: Network, name: string, addressFamily: IpAddressFamily | null, includeUnavailable: boolean): ResolveAddressStream; - export function /** + export function resolveAddresses(network: Network, name: string, addressFamily: IpAddressFamily | null, includeUnavailable: boolean): ResolveAddressStream; + /** * Returns the next address from the resolver. * * This function should be called multiple times. On each call, it will @@ -48,20 +48,20 @@ export namespace SocketsIpNameLookup { * - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) * - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) */ - resolveNextAddress(this: ResolveAddressStream): IpAddress | null; - export function /** + export function resolveNextAddress(this: ResolveAddressStream): IpAddress | null; + /** * Dispose of the specified `resolve-address-stream`, after which it may no longer be used. * * Note: this function is scheduled to be removed when Resources are natively supported in Wit. */ - dropResolveAddressStream(this: ResolveAddressStream): void; - export function /** + export function dropResolveAddressStream(this: ResolveAddressStream): void; + /** * Create a `pollable` which will resolve once the stream is ready for I/O. * * Note: this function is here for WASI Preview2 only. * It's planned to be removed when `future` is natively supported in Preview3. */ - subscribe(this: ResolveAddressStream): Pollable; + export function subscribe(this: ResolveAddressStream): Pollable; } import type { Pollable } from '../imports/poll'; export { Pollable }; diff --git a/packages/preview2-shim/types/imports/sockets-network.d.ts b/packages/preview2-shim/types/imports/sockets-network.d.ts index 7288285bf..54b70ea8a 100644 --- a/packages/preview2-shim/types/imports/sockets-network.d.ts +++ b/packages/preview2-shim/types/imports/sockets-network.d.ts @@ -1,10 +1,10 @@ export namespace SocketsNetwork { - export function /** + /** * Dispose of the specified `network`, after which it may no longer be used. * * Note: this function is scheduled to be removed when Resources are natively supported in Wit. */ - dropNetwork(this: Network): void; + export function dropNetwork(this: Network): void; } /** * An opaque resource that represents access to (a subset of) the network. diff --git a/packages/preview2-shim/types/imports/sockets-tcp-create-socket.d.ts b/packages/preview2-shim/types/imports/sockets-tcp-create-socket.d.ts index 609b5477f..660ca1fa2 100644 --- a/packages/preview2-shim/types/imports/sockets-tcp-create-socket.d.ts +++ b/packages/preview2-shim/types/imports/sockets-tcp-create-socket.d.ts @@ -1,5 +1,5 @@ export namespace SocketsTcpCreateSocket { - export function /** + /** * Create a new TCP socket. * * Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. @@ -21,7 +21,7 @@ export namespace SocketsTcpCreateSocket { * - * - */ - createTcpSocket(addressFamily: IpAddressFamily): TcpSocket; + export function createTcpSocket(addressFamily: IpAddressFamily): TcpSocket; } import type { Network } from '../imports/network'; export { Network }; diff --git a/packages/preview2-shim/types/imports/sockets-tcp.d.ts b/packages/preview2-shim/types/imports/sockets-tcp.d.ts index 07d2ae528..4ca834de9 100644 --- a/packages/preview2-shim/types/imports/sockets-tcp.d.ts +++ b/packages/preview2-shim/types/imports/sockets-tcp.d.ts @@ -1,5 +1,5 @@ export namespace SocketsTcp { - export function /** + /** * Bind the socket to a specific network on the provided IP address and port. * * If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which @@ -29,9 +29,9 @@ export namespace SocketsTcp { * - * - */ - startBind(this: TcpSocket, network: Network, localAddress: IpSocketAddress): void; + export function startBind(this: TcpSocket, network: Network, localAddress: IpSocketAddress): void; export function finishBind(this: TcpSocket): void; - export function /** + /** * Connect to a remote endpoint. * * On success: @@ -62,9 +62,9 @@ export namespace SocketsTcp { * - * - */ - startConnect(this: TcpSocket, network: Network, remoteAddress: IpSocketAddress): void; + export function startConnect(this: TcpSocket, network: Network, remoteAddress: IpSocketAddress): void; export function finishConnect(this: TcpSocket): [InputStream, OutputStream]; - export function /** + /** * Start listening for new connections. * * Transitions the socket into the Listener state. @@ -88,9 +88,9 @@ export namespace SocketsTcp { * - * - */ - startListen(this: TcpSocket, network: Network): void; + export function startListen(this: TcpSocket, network: Network): void; export function finishListen(this: TcpSocket): void; - export function /** + /** * Accept a new client socket. * * The returned socket is bound and in the Connection state. @@ -110,8 +110,8 @@ export namespace SocketsTcp { * - * - */ - accept(this: TcpSocket): [TcpSocket, InputStream, OutputStream]; - export function /** + export function accept(this: TcpSocket): [TcpSocket, InputStream, OutputStream]; + /** * Get the bound local address. * * # Typical errors @@ -123,8 +123,8 @@ export namespace SocketsTcp { * - * - */ - localAddress(this: TcpSocket): IpSocketAddress; - export function /** + export function localAddress(this: TcpSocket): IpSocketAddress; + /** * Get the bound remote address. * * # Typical errors @@ -136,14 +136,14 @@ export namespace SocketsTcp { * - * - */ - remoteAddress(this: TcpSocket): IpSocketAddress; - export function /** + export function remoteAddress(this: TcpSocket): IpSocketAddress; + /** * Whether this is a IPv4 or IPv6 socket. * * Equivalent to the SO_DOMAIN socket option. */ - addressFamily(this: TcpSocket): IpAddressFamily; - export function /** + export function addressFamily(this: TcpSocket): IpAddressFamily; + /** * Whether IPv4 compatibility (dual-stack) mode is disabled or not. * * Equivalent to the IPV6_V6ONLY socket option. @@ -154,33 +154,33 @@ export namespace SocketsTcp { * - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) */ - ipv6Only(this: TcpSocket): boolean; + export function ipv6Only(this: TcpSocket): boolean; export function setIpv6Only(this: TcpSocket, value: boolean): void; - export function /** + /** * Hints the desired listen queue size. Implementations are free to ignore this. * * # Typical errors * - `already-connected`: (set) The socket is already in the Connection state. * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) */ - setListenBacklogSize(this: TcpSocket, value: bigint): void; - export function /** + export function setListenBacklogSize(this: TcpSocket, value: bigint): void; + /** * Equivalent to the SO_KEEPALIVE socket option. * * # Typical errors * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) */ - keepAlive(this: TcpSocket): boolean; + export function keepAlive(this: TcpSocket): boolean; export function setKeepAlive(this: TcpSocket, value: boolean): void; - export function /** + /** * Equivalent to the TCP_NODELAY socket option. * * # Typical errors * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) */ - noDelay(this: TcpSocket): boolean; + export function noDelay(this: TcpSocket): boolean; export function setNoDelay(this: TcpSocket, value: boolean): void; - export function /** + /** * Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. * * # Typical errors @@ -188,9 +188,9 @@ export namespace SocketsTcp { * - `already-listening`: (set) The socket is already in the Listener state. * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) */ - unicastHopLimit(this: TcpSocket): number; + export function unicastHopLimit(this: TcpSocket): number; export function setUnicastHopLimit(this: TcpSocket, value: number): void; - export function /** + /** * The kernel buffer space reserved for sends/receives on this socket. * * Note #1: an implementation may choose to cap or round the buffer size when setting the value. @@ -207,18 +207,18 @@ export namespace SocketsTcp { * - `already-listening`: (set) The socket is already in the Listener state. * - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) */ - receiveBufferSize(this: TcpSocket): bigint; + export function receiveBufferSize(this: TcpSocket): bigint; export function setReceiveBufferSize(this: TcpSocket, value: bigint): void; export function sendBufferSize(this: TcpSocket): bigint; export function setSendBufferSize(this: TcpSocket, value: bigint): void; - export function /** + /** * Create a `pollable` which will resolve once the socket is ready for I/O. * * Note: this function is here for WASI Preview2 only. * It's planned to be removed when `future` is natively supported in Preview3. */ - subscribe(this: TcpSocket): Pollable; - export function /** + export function subscribe(this: TcpSocket): Pollable; + /** * Initiate a graceful shutdown. * * - receive: the socket is not expecting to receive any more data from the peer. All subsequent read @@ -239,15 +239,15 @@ export namespace SocketsTcp { * - * - */ - shutdown(this: TcpSocket, shutdownType: ShutdownType): void; - export function /** + export function shutdown(this: TcpSocket, shutdownType: ShutdownType): void; + /** * Dispose of the specified `tcp-socket`, after which it may no longer be used. * * Similar to the POSIX `close` function. * * Note: this function is scheduled to be removed when Resources are natively supported in Wit. */ - dropTcpSocket(this: TcpSocket): void; + export function dropTcpSocket(this: TcpSocket): void; } import type { InputStream } from '../imports/streams'; export { InputStream }; diff --git a/packages/preview2-shim/types/imports/sockets-udp-create-socket.d.ts b/packages/preview2-shim/types/imports/sockets-udp-create-socket.d.ts index 8c8d1d0fb..4cc43618f 100644 --- a/packages/preview2-shim/types/imports/sockets-udp-create-socket.d.ts +++ b/packages/preview2-shim/types/imports/sockets-udp-create-socket.d.ts @@ -1,5 +1,5 @@ export namespace SocketsUdpCreateSocket { - export function /** + /** * Create a new UDP socket. * * Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. @@ -21,7 +21,7 @@ export namespace SocketsUdpCreateSocket { * - * - */ - createUdpSocket(addressFamily: IpAddressFamily): UdpSocket; + export function createUdpSocket(addressFamily: IpAddressFamily): UdpSocket; } import type { Network } from '../imports/network'; export { Network }; diff --git a/packages/preview2-shim/types/imports/sockets-udp.d.ts b/packages/preview2-shim/types/imports/sockets-udp.d.ts index b421606f8..1b02c771e 100644 --- a/packages/preview2-shim/types/imports/sockets-udp.d.ts +++ b/packages/preview2-shim/types/imports/sockets-udp.d.ts @@ -1,5 +1,5 @@ export namespace SocketsUdp { - export function /** + /** * Bind the socket to a specific network on the provided IP address and port. * * If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which @@ -28,9 +28,9 @@ export namespace SocketsUdp { * - * - */ - startBind(this: UdpSocket, network: Network, localAddress: IpSocketAddress): void; + export function startBind(this: UdpSocket, network: Network, localAddress: IpSocketAddress): void; export function finishBind(this: UdpSocket): void; - export function /** + /** * Set the destination address. * * The local-address is updated based on the best network path to `remote-address`. @@ -61,9 +61,9 @@ export namespace SocketsUdp { * - * - */ - startConnect(this: UdpSocket, network: Network, remoteAddress: IpSocketAddress): void; + export function startConnect(this: UdpSocket, network: Network, remoteAddress: IpSocketAddress): void; export function finishConnect(this: UdpSocket): void; - export function /** + /** * Receive a message. * * Returns: @@ -84,8 +84,8 @@ export namespace SocketsUdp { * - * - */ - receive(this: UdpSocket): Datagram; - export function /** + export function receive(this: UdpSocket): Datagram; + /** * Send a message to a specific destination address. * * The remote address option is required. To send a message to the "connected" peer, @@ -110,8 +110,8 @@ export namespace SocketsUdp { * - * - */ - send(this: UdpSocket, datagram: Datagram): void; - export function /** + export function send(this: UdpSocket, datagram: Datagram): void; + /** * Get the current bound address. * * # Typical errors @@ -123,8 +123,8 @@ export namespace SocketsUdp { * - * - */ - localAddress(this: UdpSocket): IpSocketAddress; - export function /** + export function localAddress(this: UdpSocket): IpSocketAddress; + /** * Get the address set with `connect`. * * # Typical errors @@ -136,14 +136,14 @@ export namespace SocketsUdp { * - * - */ - remoteAddress(this: UdpSocket): IpSocketAddress; - export function /** + export function remoteAddress(this: UdpSocket): IpSocketAddress; + /** * Whether this is a IPv4 or IPv6 socket. * * Equivalent to the SO_DOMAIN socket option. */ - addressFamily(this: UdpSocket): IpAddressFamily; - export function /** + export function addressFamily(this: UdpSocket): IpAddressFamily; + /** * Whether IPv4 compatibility (dual-stack) mode is disabled or not. * * Equivalent to the IPV6_V6ONLY socket option. @@ -154,17 +154,17 @@ export namespace SocketsUdp { * - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) * - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) */ - ipv6Only(this: UdpSocket): boolean; + export function ipv6Only(this: UdpSocket): boolean; export function setIpv6Only(this: UdpSocket, value: boolean): void; - export function /** + /** * Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. * * # Typical errors * - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) */ - unicastHopLimit(this: UdpSocket): number; + export function unicastHopLimit(this: UdpSocket): number; export function setUnicastHopLimit(this: UdpSocket, value: number): void; - export function /** + /** * The kernel buffer space reserved for sends/receives on this socket. * * Note #1: an implementation may choose to cap or round the buffer size when setting the value. @@ -181,23 +181,23 @@ export namespace SocketsUdp { * # Typical errors * - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) */ - receiveBufferSize(this: UdpSocket): bigint; + export function receiveBufferSize(this: UdpSocket): bigint; export function setReceiveBufferSize(this: UdpSocket, value: bigint): void; export function sendBufferSize(this: UdpSocket): bigint; export function setSendBufferSize(this: UdpSocket, value: bigint): void; - export function /** + /** * Create a `pollable` which will resolve once the socket is ready for I/O. * * Note: this function is here for WASI Preview2 only. * It's planned to be removed when `future` is natively supported in Preview3. */ - subscribe(this: UdpSocket): Pollable; - export function /** + export function subscribe(this: UdpSocket): Pollable; + /** * Dispose of the specified `udp-socket`, after which it may no longer be used. * * Note: this function is scheduled to be removed when Resources are natively supported in Wit. */ - dropUdpSocket(this: UdpSocket): void; + export function dropUdpSocket(this: UdpSocket): void; } import type { Pollable } from '../imports/poll'; export { Pollable };