diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 3eb582d1b..80e26dffc 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -115,7 +115,7 @@ jobs: continue-on-error: true - name: Run Prettiers - run: npx prettier --tab-width 4 --print-width 100 --trailing-comma es5 --ignore-path "$(CURDIR)/node_modules/*" --write JS/ + run: npx prettier --tab-width 4 --print-width 100 --trailing-comma es5 --ignore-path "$(CURDIR)/.prettierignore" --write JS/ - name: Commit changes uses: stefanzweifel/git-auto-commit-action@v4 with: diff --git a/.github/workflows/webassembly-pr-build.yml b/.github/workflows/webassembly-pr-build.yml new file mode 100644 index 000000000..92b8d0390 --- /dev/null +++ b/.github/workflows/webassembly-pr-build.yml @@ -0,0 +1,101 @@ +name: Webassembly PR Build + +on: + pull_request_target: + types: + - opened + - reopened + - synchronize + paths: + - 'JS/wasm/**' + +jobs: + changes: + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + wasm: ${{ steps.filter.outputs.wasm }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: dorny/paths-filter@v2 + id: filter + with: + filters: | + wasm: + - 'JS/wasm/**' + + wasm: + needs: changes + if: ${{ needs.changes.outputs.wasm == 'true' }} + permissions: + contents: write + pull-requests: write + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Setup Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Cache wasmjs-engine + uses: actions/cache@v2 + with: + path: ./JS/wasm/crates/wasmjs-engine/target + key: wasmjs-engine + restore-keys: | + wasmjs-engine + + - name: Check if wasmjs-engine is cached + id: wasmjs-engine-hit + run: | + echo "cache-hit=$(test -d ./JS/wasm/crates/wasmjs-engine/target && echo true || echo false)" >> $GITHUB_OUTPUT + + - name: Build wasmjs-engine + if: steps.wasmjs-engine-hit.outputs.cache-hit != 'true' + working-directory: ./JS/wasm/crates/wasmjs-engine + run: | + npm install -g @go-task/cli + task build + + - name: Cache wasmjs-runtime + uses: actions/cache@v2 + with: + path: ./JS/wasm/crates/wasmjs-runtime/target + key: wasmjs-runtime + restore-keys: | + wasmjs-runtime + + - name: Check if wasmjs-runtime is cached + id: wasmjs-runtime-hit + run: | + echo "cache-hit=$(test -d ./JS/wasm/crates/wasmjs-runtime/target && echo true || echo false)" >> $GITHUB_OUTPUT + + - name: Build wasmjs-runtime + if: steps.wasmjs-runtime-hit.outputs.cache-hit != 'true' + working-directory: ./JS/wasm/crates/wasmjs-runtime + run: | + cargo build --release + + - name: Build ec-wasmjs-hono + working-directory: ./JS/wasm/examples/ec-wasmjs-hono + run: | + npm install + npm run build + + - name: Run ec-wasmjs-hono + working-directory: ./JS/wasm/crates/wasmjs-runtime + run: | + ./target/release/wasmjs-runtime run ../../examples/ec-wasmjs-hono/bin \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..1f2c2b602 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +**/node_modules +JS/wasm/* \ No newline at end of file diff --git a/JS/wasm/assets/wasmjs/wit/arakoo-geo.witx b/JS/wasm/assets/wasmjs/wit/arakoo-geo.witx new file mode 100644 index 000000000..ef5d9e13e --- /dev/null +++ b/JS/wasm/assets/wasmjs/wit/arakoo-geo.witx @@ -0,0 +1,29 @@ +(typename $arakoo_status + (enum (@witx tag u32) + $ok + $error + $inval + $badf + $buflen + $unsupported + $badalign + $httpinvalid + $httpuser + $httpincomplete + $none + $httpheadtoolarge + $httpinvalidstatus + $limitexceeded + $again + )) + +(module $arakoo_geo + (@interface func (export "lookup") + (param $addr_octets (@witx const_pointer (@witx char8))) + (param $addr_len (@witx usize)) + (param $buf (@witx pointer (@witx char8))) + (param $buf_len (@witx usize)) + (param $nwritten_out (@witx pointer (@witx usize))) + (result $err (expected (error $arakoo_status))) + ) +) \ No newline at end of file diff --git a/JS/wasm/assets/wasmjs/wit/http-types.wit b/JS/wasm/assets/wasmjs/wit/http-types.wit new file mode 100644 index 000000000..2f5f9d9a6 --- /dev/null +++ b/JS/wasm/assets/wasmjs/wit/http-types.wit @@ -0,0 +1,41 @@ +type uri = string +type http-status = u16 +type http-header = tuple +type http-headers = list +enum http-method { + get, + post, + put, + patch, + delete, + options, + head +} +type http-param = tuple +type http-params = list +type http-body = list +record http-request { + body: option, + headers: http-headers, + method: http-method, + params: http-params, + uri: uri, +} +record http-request-error { + error: http-error, + message: string +} +record http-response { + body: option, + headers: http-headers, + status: http-status, +} +enum http-error { + invalid-request, + invalid-request-body, + invalid-response-body, + not-allowed, + internal-error, + timeout, + redirect-loop, +} \ No newline at end of file diff --git a/JS/wasm/assets/wasmjs/wit/http.wit b/JS/wasm/assets/wasmjs/wit/http.wit new file mode 100644 index 000000000..36450be50 --- /dev/null +++ b/JS/wasm/assets/wasmjs/wit/http.wit @@ -0,0 +1,2 @@ +use * from http-types +send-http-request: func(request: http-request) -> expected diff --git a/JS/wasm/crates/wasmjs-engine/.gitignore b/JS/wasm/crates/wasmjs-engine/.gitignore new file mode 100644 index 000000000..a404254d7 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/.gitignore @@ -0,0 +1,2 @@ +target/ +**/node_modules/ \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/Cargo.lock b/JS/wasm/crates/wasmjs-engine/Cargo.lock new file mode 100644 index 000000000..36caa85ca --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/Cargo.lock @@ -0,0 +1,1205 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bindgen" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "futures-channel" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" + +[[package]] +name = "futures-task" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" + +[[package]] +name = "futures-util" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951dfc2e32ac02d67c90c0d65bd27009a635dc9b381a2cc7d284ab01e3a0150d" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92445bc9cc14bfa0a3ce56817dc3b5bcc227a168781a356b702410789cec0d10" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75264b2003a3913f118d35c586e535293b3e22e41f074930762929d071e092" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "tracing", + "want", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "javy" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82bea151527df24f765b795a87a80d144f8cc35ef99acaed99465f1206a4501e" +dependencies = [ + "anyhow", + "quickjs-wasm-rs", + "serde-transcode", + "serde_json", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pulldown-cmark" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + +[[package]] +name = "quickjs-wasm-rs" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "174a4fb51c14dcb82f1f98ec1ec3c2f26f2cd6e40f200d86c5f6eecacf40b409" +dependencies = [ + "anyhow", + "once_cell", + "quickjs-wasm-sys", + "serde", +] + +[[package]] +name = "quickjs-wasm-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1af9a5eb35f2df97dc9816b711e681b0c3cc8f5c18fdd7df0021ec4f60bbcb4" +dependencies = [ + "anyhow", + "bindgen", + "bytes", + "cc", + "futures-core", + "futures-task", + "futures-util", + "http-body-util", + "hyper", + "mio", + "native-tls", + "openssl-macros", + "tokio", + "tokio-macros", + "tokio-native-tls", + "walkdir", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-transcode" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "590c0e25c2a5bb6e85bf5c1bce768ceb86b316e7a01bdf07d2cb4ec2271990e2" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasmjs-engine" +version = "0.1.0" +dependencies = [ + "anyhow", + "javy", + "regex", + "serde_json", + "wit-bindgen-rust", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "wit-bindgen-gen-core" +version = "0.2.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?tag=v0.2.0#cb871cfa1ee460b51eb1d144b175b9aab9c50aba" +dependencies = [ + "anyhow", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-gen-rust" +version = "0.2.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?tag=v0.2.0#cb871cfa1ee460b51eb1d144b175b9aab9c50aba" +dependencies = [ + "heck", + "wit-bindgen-gen-core", +] + +[[package]] +name = "wit-bindgen-gen-rust-wasm" +version = "0.2.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?tag=v0.2.0#cb871cfa1ee460b51eb1d144b175b9aab9c50aba" +dependencies = [ + "heck", + "wit-bindgen-gen-core", + "wit-bindgen-gen-rust", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.2.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?tag=v0.2.0#cb871cfa1ee460b51eb1d144b175b9aab9c50aba" +dependencies = [ + "async-trait", + "bitflags 1.3.2", + "wit-bindgen-rust-impl", +] + +[[package]] +name = "wit-bindgen-rust-impl" +version = "0.2.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?tag=v0.2.0#cb871cfa1ee460b51eb1d144b175b9aab9c50aba" +dependencies = [ + "proc-macro2", + "syn 1.0.109", + "wit-bindgen-gen-core", + "wit-bindgen-gen-rust-wasm", +] + +[[package]] +name = "wit-parser" +version = "0.2.0" +source = "git+https://github.com/bytecodealliance/wit-bindgen?tag=v0.2.0#cb871cfa1ee460b51eb1d144b175b9aab9c50aba" +dependencies = [ + "anyhow", + "id-arena", + "pulldown-cmark", + "unicode-normalization", + "unicode-xid", +] diff --git a/JS/wasm/crates/wasmjs-engine/Cargo.toml b/JS/wasm/crates/wasmjs-engine/Cargo.toml new file mode 100644 index 000000000..07bf9c246 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "wasmjs-engine" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +javy = { version = "2.1.0", features = ["json"] } +regex = "1" +serde_json = "1.0.108" +# wit-bindgen-rust = "0.13.1" +wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", tag = "v0.2.0" } + +[profile.release] +lto = "thin" diff --git a/JS/wasm/crates/wasmjs-engine/build.rs b/JS/wasm/crates/wasmjs-engine/build.rs new file mode 100644 index 000000000..506027b3f --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/build.rs @@ -0,0 +1,19 @@ +use std::process::Command; + +fn main() { + Command::new("npm") + .current_dir("shims") + .arg("install") + .status() + .unwrap(); + + Command::new("npm") + .current_dir("shims") + .args(["run", "build"]) + .status() + .unwrap(); + + println!("cargo:rerun-if-changed=shims/package.json"); + println!("cargo:rerun-if-changed=shims/build.js"); + println!("cargo:rerun-if-changed=shims/src/*.js"); +} diff --git a/JS/wasm/crates/wasmjs-engine/package-lock.json b/JS/wasm/crates/wasmjs-engine/package-lock.json new file mode 100644 index 000000000..706944852 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "wasmjs-engine", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/.gitignore b/JS/wasm/crates/wasmjs-engine/shims/.gitignore new file mode 100644 index 000000000..77738287f --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/.gitignore @@ -0,0 +1 @@ +dist/ \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/shims/build.js b/JS/wasm/crates/wasmjs-engine/shims/build.js new file mode 100644 index 000000000..f9a2bd8ce --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/build.js @@ -0,0 +1,55 @@ +import { build } from 'esbuild' + +// Build for index.js +build({ + entryPoints: ['src/index.js'], + bundle: true, + outfile: 'dist/index.js', + format: "esm", + target: "esnext", + platform: "node", + treeShaking: false +}).catch((error) => { + console.error(error) + process.exit(1) +}) + +// Build for buffer.js +build({ + entryPoints: ['src/buffer.js'], + bundle: true, + outfile: 'dist/buffer.js', + format: "esm", + target: "esnext", + platform: "node", + treeShaking: false +}).catch((error) => { + console.error(error) + process.exit(1) +}) + +build({ + entryPoints: ['src/path.js'], + bundle: true, + outfile: 'dist/path.js', + format: "esm", + target: "esnext", + platform: "node", + treeShaking: false +}).catch((error) => { + console.error(error) + process.exit(1) +}) + +build({ + entryPoints: ['src/crypto.ts'], + bundle: true, + outfile: 'dist/crypto.js', + format: "esm", + target: "esnext", + platform: "node", + treeShaking: false +}).catch((error) => { + console.error(error) + process.exit(1) +}) diff --git a/JS/wasm/crates/wasmjs-engine/shims/package-lock.json b/JS/wasm/crates/wasmjs-engine/shims/package-lock.json new file mode 100644 index 000000000..7bb9dc38c --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/package-lock.json @@ -0,0 +1,567 @@ +{ + "name": "shims", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "shims", + "version": "1.0.0", + "dependencies": { + "@sinonjs/text-encoding": "^0.7", + "@types/node": "^20.10.3", + "@ungap/url-search-params": "^0.2", + "base64-js": "^1.5.1", + "buffer": "^6.0.3", + "esbuild": "^0.19", + "http-status": "^1.7", + "query-string": "^7.1.1", + "sjcl": "^1.0.8", + "url-parse": "^1.5.10" + }, + "devDependencies": { + "@types/sjcl": "^1.0.34" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", + "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", + "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", + "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", + "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", + "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", + "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", + "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", + "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", + "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", + "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", + "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", + "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", + "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", + "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", + "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", + "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", + "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", + "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", + "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", + "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", + "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", + "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==" + }, + "node_modules/@types/node": { + "version": "20.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz", + "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/sjcl": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/@types/sjcl/-/sjcl-1.0.34.tgz", + "integrity": "sha512-bQHEeK5DTQRunIfQeUMgtpPsNNCcZyQ9MJuAfW1I7iN0LDunTc78Fu17STbLMd7KiEY/g2zHVApippa70h6HoQ==", + "dev": true + }, + "node_modules/@ungap/url-search-params": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@ungap/url-search-params/-/url-search-params-0.2.2.tgz", + "integrity": "sha512-qQsguKXZVKdCixOHX9jqnX/K/1HekPDpGKyEcXHT+zR6EjGA7S4boSuelL4uuPv6YfhN0n8c4UxW+v/Z3gM2iw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esbuild": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", + "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.5", + "@esbuild/android-arm64": "0.19.5", + "@esbuild/android-x64": "0.19.5", + "@esbuild/darwin-arm64": "0.19.5", + "@esbuild/darwin-x64": "0.19.5", + "@esbuild/freebsd-arm64": "0.19.5", + "@esbuild/freebsd-x64": "0.19.5", + "@esbuild/linux-arm": "0.19.5", + "@esbuild/linux-arm64": "0.19.5", + "@esbuild/linux-ia32": "0.19.5", + "@esbuild/linux-loong64": "0.19.5", + "@esbuild/linux-mips64el": "0.19.5", + "@esbuild/linux-ppc64": "0.19.5", + "@esbuild/linux-riscv64": "0.19.5", + "@esbuild/linux-s390x": "0.19.5", + "@esbuild/linux-x64": "0.19.5", + "@esbuild/netbsd-x64": "0.19.5", + "@esbuild/openbsd-x64": "0.19.5", + "@esbuild/sunos-x64": "0.19.5", + "@esbuild/win32-arm64": "0.19.5", + "@esbuild/win32-ia32": "0.19.5", + "@esbuild/win32-x64": "0.19.5" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-status": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-status/-/http-status-1.7.3.tgz", + "integrity": "sha512-GS8tL1qHT2nBCMJDYMHGkkkKQLNkIAHz37vgO68XKvzv+XyqB4oh/DfmMHdtRzfqSJPj1xKG2TaELZtlCz6BEQ==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/sjcl": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.8.tgz", + "integrity": "sha512-LzIjEQ0S0DpIgnxMEayM1rq9aGwGRG4OnZhCdjx7glTaJtf4zRfpg87ImfjSJjoW9vKpagd82McDOwbRT5kQKQ==", + "engines": { + "node": "*" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + } + } +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/package.json b/JS/wasm/crates/wasmjs-engine/shims/package.json new file mode 100644 index 000000000..6d90997f4 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/package.json @@ -0,0 +1,24 @@ +{ + "name": "shims", + "version": "1.0.0", + "type": "module", + "main": "bin/index.js", + "scripts": { + "build": "node ./build.js" + }, + "dependencies": { + "@sinonjs/text-encoding": "^0.7", + "@types/node": "^20.10.3", + "@ungap/url-search-params": "^0.2", + "base64-js": "^1.5.1", + "buffer": "^6.0.3", + "esbuild": "^0.19", + "http-status": "^1.7", + "query-string": "^7.1.1", + "sjcl": "^1.0.8", + "url-parse": "^1.5.10" + }, + "devDependencies": { + "@types/sjcl": "^1.0.34" + } +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/buffer.js b/JS/wasm/crates/wasmjs-engine/shims/src/buffer.js new file mode 100644 index 000000000..1dd28191f --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/buffer.js @@ -0,0 +1,24 @@ + +import { + constants, + kMaxLength, + kStringMaxLength, + Buffer, + SlowBuffer, +} from './internal/internal_buffer'; + +export { + constants, + kMaxLength, + kStringMaxLength, + Buffer, + SlowBuffer, +}; + +export default { + constants, + kMaxLength, + kStringMaxLength, + Buffer, + SlowBuffer, +} \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/crypto.ts b/JS/wasm/crates/wasmjs-engine/shims/src/crypto.ts new file mode 100644 index 000000000..0d504c138 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/crypto.ts @@ -0,0 +1,297 @@ +import { + ERR_METHOD_NOT_IMPLEMENTED +} from './internal/internal_errors'; + +// export const getRandomValues = crypto.getRandomValues; +// export const subtle = crypto.subtle; +// export const timingSafeEqual = (crypto as any).timingSafeEqual; +// export const webcrypto = crypto; + +import { + DiffieHellman, + DiffieHellmanGroup, + createDiffieHellman, + createDiffieHellmanGroup, + getDiffieHellman, +} from './internal/crypto_dh'; + +import { + randomBytes, + randomFillSync, + randomFill, + randomInt, + randomUUID, + PrimeNum, + GeneratePrimeOptions, + CheckPrimeOptions, + generatePrime, + generatePrimeSync, + checkPrime, + checkPrimeSync, +} from './internal/crypto_random'; + +import { + createHash, + createHmac, + Hash, + HashOptions, + Hmac, +} from './internal/crypto_hash'; + +import { + hkdf, + hkdfSync, +} from './internal/crypto_hkdf'; + +import { + pbkdf2, + pbkdf2Sync, + ArrayLike, +} from './internal/crypto_pbkdf2'; + +import { + KeyObject, + PublicKeyObject, + PrivateKeyObject, + SecretKeyObject, + generateKey, + generateKeyPair, + generateKeyPairSync, + generateKeySync, + createPrivateKey, + createPublicKey, + createSecretKey, +} from './internal/crypto_keys'; + +export { + // DH + DiffieHellman, + DiffieHellmanGroup, + createDiffieHellman, + createDiffieHellmanGroup, + getDiffieHellman, + // Random + randomBytes, + randomFillSync, + randomFill, + randomInt, + randomUUID, + // Primes + PrimeNum as primeNum, + GeneratePrimeOptions as generatePrimeOptions, + CheckPrimeOptions as checkPrimeOptions, + generatePrime, + generatePrimeSync, + checkPrime, + checkPrimeSync, + // Hash and Hmac + createHash, + createHmac, + Hash, + HashOptions, + Hmac, + // Hkdf + hkdf, + hkdfSync, + // Pbkdf2 + pbkdf2, + pbkdf2Sync, + ArrayLike as arrayLike, + // Keys + KeyObject, + PublicKeyObject, + PrivateKeyObject, + SecretKeyObject, + generateKey, + generateKeyPair, + generateKeyPairSync, + generateKeySync, + createPrivateKey, + createPublicKey, + createSecretKey, +} + +export function getCiphers() { + return ["aes-128-cbc", "aes-192-cbc", "aes-256-cbc", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", + "aes-128-ecb", "aes-192-ecb", "aes-256-ecb", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", + "aes-128-ofb", "aes-192-ofb", "aes-256-ofb", "des-ecb", "des-ede", "des-ede-cbc", "rc2-cbc"]; +} + +export function getCurves() { + // Hardcoded list of supported curves. Note that prime256v1 is equivalent to secp256r1, we follow + // OpenSSL's and bssl's nomenclature here. + return ['secp224r1', 'prime256v1', 'secp384r1', 'secp521r1']; +} + +export function getHashes() { + // Hardcoded list of hashes supported in boringssl, node's approach looks pretty clunky. This is + // expected to change infrequently based of bssl's stability-focused approach. + return ['md4', 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'md5-sha1', 'RSA-MD5', + 'RSA-SHA1', 'RSA-SHA224', 'RSA-SHA256', 'RSA-SHA384', 'RSA-SHA512', 'DSA-SHA', + 'DSA-SHA1', 'ecdsa-with-SHA1']; +} + +// We do not implement the openssl secure heap. +export function secureHeapUsed() { + return { + total: 0, + used: 0, + utilization: 0, + min: 0, + } +} + +// We do not allow users to set the engine used. +export function setEngine(_1 : string, _2?: number) { + throw new ERR_METHOD_NOT_IMPLEMENTED('setEngine'); +} + +// We do not allow users to modify the FIPS enablement. +export function setFips(_: boolean) { + throw new ERR_METHOD_NOT_IMPLEMENTED('setFips'); +} + +// We always run in FIPS mode. +export const fips = true; +export function getFips() { return fips; } + +export default { + // DH + DiffieHellman, + DiffieHellmanGroup, + createDiffieHellman, + createDiffieHellmanGroup, + getDiffieHellman, + // Keys, + KeyObject, + PublicKeyObject, + PrivateKeyObject, + SecretKeyObject, + generateKey, + generateKeyPair, + generateKeyPairSync, + generateKeySync, + createPrivateKey, + createPublicKey, + createSecretKey, + // Random + // getRandomValues, + randomBytes, + randomFillSync, + randomFill, + randomInt, + randomUUID, + generatePrime, + generatePrimeSync, + checkPrime, + checkPrimeSync, + // Hash and Hmac + Hash, + Hmac, + createHash, + createHmac, + getHashes, + // Hkdf + hkdf, + hkdfSync, + // Pbkdf2 + pbkdf2, + pbkdf2Sync, + // Misc + getCiphers, + getCurves, + secureHeapUsed, + setEngine, + // timingSafeEqual, + // Fips + getFips, + setFips, + get fips() { return getFips(); }, + set fips(_: boolean) { setFips(_); }, + // WebCrypto + // subtle, + // webcrypto, +}; + +// Classes +// * [ ] crypto.Certificate +// * [ ] crypto.Cipher +// * [ ] crypto.Decipher +// * [x] crypto.DiffieHellman +// * [x] crypto.DiffieHellmanGroup +// * [ ] crypto.ECDH +// * [x] crypto.Hash +// * [x] crypto.Hmac +// * [ ] crypto.KeyObject +// * [ ] crypto.Sign +// * [ ] crypto.Verify +// * [ ] crypto.X509Certificate +// * [ ] crypto.constants +// * [ ] crypto.DEFAULT_ENCODING +// * Primes +// * [x] crypto.checkPrime(candidate[, options], callback) +// * [x] crypto.checkPrimeSync(candidate[, options]) +// * [x] crypto.generatePrime(size[, options[, callback]]) +// * [x] crypto.generatePrimeSync(size[, options]) +// * Ciphers +// * [ ] crypto.createCipher(algorithm, password[, options]) +// * [ ] crypto.createCipheriv(algorithm, key, iv[, options]) +// * [ ] crypto.createDecipher(algorithm, password[, options]) +// * [ ] crypto.createDecipheriv(algorithm, key, iv[, options]) +// * [ ] crypto.privateDecrypt(privateKey, buffer) +// * [ ] crypto.privateEncrypt(privateKey, buffer) +// * [ ] crypto.publicDecrypt(key, buffer) +// * [ ] crypto.publicEncrypt(key, buffer) +// * DiffieHellman +// * [x] crypto.createDiffieHellman(prime[, primeEncoding][, generator][, generatorEncoding]) +// * [x] crypto.createDiffieHellman(primeLength[, generator]) +// * [x] crypto.createDiffieHellmanGroup(name) +// * [ ] crypto.createECDH(curveName) +// * [ ] crypto.diffieHellman(options) +// * [x] crypto.getDiffieHellman(groupName) +// * Hash +// * [x] crypto.createHash(algorithm[, options]) +// * [x] crypto.createHmac(algorithm, key[, options]) +// * [x] crypto.getHashes() +// * Keys +// * [ ] crypto.createPrivateKey(key) +// * [ ] crypto.createPublicKey(key) +// * [x] crypto.createSecretKey(key[, encoding]) +// * [x] crypto.generateKey(type, options, callback) +// * [x] crypto.generateKeyPair(type, options, callback) +// * [x] crypto.generateKeyPairSync(type, options) +// * [x] crypto.generateKeySync(type, options) +// * Sign/Verify +// * [ ] crypto.createSign(algorithm[, options]) +// * [ ] crypto.createVerify(algorithm[, options]) +// * [ ] crypto.sign(algorithm, data, key[, callback]) +// * [ ] crypto.verify(algorithm, data, key, signature[, callback]) +// * Misc +// * [ ] crypto.getCipherInfo(nameOrNid[, options]) +// * [x] crypto.getCiphers() +// * [x] crypto.getCurves() +// * [x] crypto.secureHeapUsed() +// * [x] crypto.setEngine(engine[, flags]) +// * [x] crypto.timingSafeEqual(a, b) +// * Fips +// * [x] crypto.getFips() +// * [x] crypto.fips +// * [x] crypto.setFips(bool) +// * Random +// * [x] crypto.getRandomValues(typedArray) +// * [x] crypto.randomBytes(size[, callback]) +// * [x] crypto.randomFillSync(buffer[, offset][, size]) +// * [x] crypto.randomFill(buffer[, offset][, size], callback) +// * [x] crypto.randomInt([min, ]max[, callback]) +// * [x] crypto.randomUUID([options]) +// * Key Derivation +// * [.] crypto.hkdf(digest, ikm, salt, info, keylen, callback) (* still needs KeyObject support) +// * [.] crypto.hkdfSync(digest, ikm, salt, info, keylen) (* still needs KeyObject support) +// * [x] crypto.pbkdf2(password, salt, iterations, keylen, digest, callback) +// * [x] crypto.pbkdf2Sync(password, salt, iterations, keylen, digest) +// * [ ] crypto.scrypt(password, salt, keylen[, options], callback) +// * [ ] crypto.scryptSync(password, salt, keylen[, options]) +// * WebCrypto +// * [x] crypto.subtle +// * [x] crypto.webcrypto + diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/events.ts b/JS/wasm/crates/wasmjs-engine/shims/src/events.ts new file mode 100644 index 000000000..938ed3c44 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/events.ts @@ -0,0 +1,7 @@ +// Copyright (c) 2017-2022 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 +// + +export * from './internal/events'; +export { default } from './internal/events'; diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/index.js b/JS/wasm/crates/wasmjs-engine/shims/src/index.js new file mode 100644 index 000000000..9c29c339f --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/index.js @@ -0,0 +1,375 @@ +// import { URLSearchParams } from "@ungap/url-search-params"; +import { TextEncoder, TextDecoder } from "@sinonjs/text-encoding"; +import httpStatus from "http-status"; +import Url from 'url-parse' +import _queryString from 'query-string'; + +class URL { + + constructor(urlStr, base = undefined) { + let url = Url(urlStr, base) + this.url = url + this.protocol = url.protocol + this.slashes = url.slashes + this.auth = url.auth + this.username = url.username + this.password = url.password + this.host = url.host + this.port = url.port + this.pathname = url.pathname + this.search = url.query + this.searchParams = new URLSearchParams(this.search) + this.hash = url.hash + this.href = url.origin + this.origin = url.origin + } + + set(key, value) { + this.url.set(key, value) + } + + toString() { + return this.url.toString() + } + + toJson() { + return this.url.toString() + } + +} + +class URLSearchParams { + queryParams = {} + + constructor(val) { + this.queryParams = { + ..._queryString.parse(val) + } + } + append(key, val) { + this.queryParams[key] = val + } + delete(key) { + delete this.queryParams[key] + } + entries() { + let arr = [] + Object.entries(this.queryParams).map(o => { + if (Array.isArray(o[1])) { + o[1].map(k => { + arr.push([o[0], k]) + }) + } else { + arr.push([o[0], o[1]]) + } + }) + let iterLength = arr.length + let iterIndex = 0 + return { + next: function () { + return iterIndex < iterLength ? + { value: arr[iterIndex++], done: false } : + { done: true }; + } + } + } + get(key) { + let val = this.queryParams[key] + if (val) { + if (typeof (val) == "object") { + return val[0] + } + return val + } + return null + } + getAll(key) { + let val = this.queryParams[key] + if (val) { + return val + } + return null + } + has(key) { + return this.queryParams[key] != undefined ? true : false + } + keys() { + return Object.keys(this.queryParams) + } + set(key, val) { + this.queryParams[key] = val + } + toString() { + return _queryString.stringify(this.queryParams) + } + values() { + return Object.keys(this.queryParams).map(k => this.queryParams[k]) + } + [Symbol.iterator]() { + return this.entries() + } +} + +globalThis.URL = URL; +globalThis.URLSearchParams = URLSearchParams; + +function atob(b64) { + return Buffer.from(b64, "base64").toString() +} + +function btoa(data) { + return Buffer.from(data).toString('base64') +} + +globalThis.btoa = btoa; +globalThis.atob = atob; + +function require(path) { + return globalThis[path]; +} + +globalThis.require = require; +class Headers { + constructor(initialHeaders) { + let headers = {}; + + for (const key in initialHeaders) { + let value = initialHeaders[key]; + + if (typeof value === "string") { + headers[key] = value; + } + } + + this.headers = headers; + } + + append(key, value) { + this.headers[key] = value; + return value; + } + + set(key, value) { + this.append(key, value); + return value; + } + + delete(key) { + let dropValue = delete this.headers[key]; + return dropValue; + } + + get(key) { + return this.headers[key]; + } + + toJSON() { + return this.headers; + } +} + +class Request { + constructor(input) { + this.url = input.url; + this.method = input.method; + this.headers = new Headers(input.headers || {}); + this.body = input.body; + this.params = input.params || {}; + this.geo = input.geo || {}; + } + + text() { + return this.body; + } +} + +class Response { + constructor(body, options = {}) { + if (body instanceof String) { + this.body = body.toString(); + } else { + this.body = body; + } + + if (options.headers instanceof Headers) { + this.headers = options.headers; + } else if (options.headers instanceof Object) { + this.headers = new Headers(options.headers); + } else { + this.headers = new Headers({}); + } + + this.status = options.status || 200; + this.statusText = options.statusText || httpStatus[this.status]; + } + + static redirect(url, status = 307) { + return new Response(`Redirecting to ${url}`, { + status, + headers: { + Location: url + } + }) + } + + get ok() { + return this.status >= 200 && this.status < 300; + } + + defaultEncoding() { + return "utf-8"; + } + + arrayBuffer() { + let parsedBody = this.body; + + if (typeof this.body === "string") { + try { + parsedBody = new TextEncoder().encode(this.body); + } catch (e) { + return Promise.reject(`err: ${e}`); + } + } + + return parsedBody; + } + + json() { + let parsedBody = this.body; + + if (typeof this.body !== "string") { + try { + parsedBody = new TextDecoder(this.defaultEncoding()).decode(this.body); + } catch (e) { + return Promise.reject(`err: ${e}`); + } + } + + try { + return Promise.resolve(JSON.parse(parsedBody)); + } catch (e) { + return Promise.reject(`err: ${e}`); + } + } + + text() { + let parsedBody = this.body; + + if (typeof this.body !== "string") { + try { + parsedBody = new TextDecoder(this.defaultEncoding()).decode(this.body); + } catch (e) { + return Promise.reject(`err: ${e}`); + } + } + + return parsedBody; + } + + toString() { + return this.body; + } +} + +(function () { + const __send_http_request = globalThis.__send_http_request; + const __console_log = globalThis.__console_log; + + globalThis.fetch = (uri, opts) => { + let optsWithDefault = { + method: "GET", + headers: {}, + body: null, + ...opts + }; + + if (optsWithDefault.body !== null && typeof optsWithDefault.body !== "string") { + try { + optsWithDefault.body = new TextEncoder().encode(optsWithDefault.body); + } catch (e) { + return Promise.reject(`err: ${e}`) + } + } + + let result = __send_http_request(uri, optsWithDefault); + + if (result.error === true) { + return Promise.reject(new Error(`[${result.type}] ${result.message}`)); + } else { + let response = new Response(result.body, { + headers: result.headers, + status: result.status, + }) + + return Promise.resolve(response); + } + } + + + globalThis.console = { + error(msg) { + this.log(msg); + }, + log(msg) { + __console_log(msg); + }, + info(msg) { + this.log(msg); + }, + debug(msg) { + this.log(msg); + }, + warn(msg) { + this.log(msg); + }, + trace(msg) { + this.log(msg); + } + } + + Reflect.deleteProperty(globalThis, "__send_http_request"); + Reflect.deleteProperty(globalThis, "__console_log"); +})(); + +globalThis.TextEncoder = TextEncoder; +globalThis.TextDecoder = TextDecoder; + +let handlerFunction; + +globalThis.addEventListener = (_eventName, handler) => { + handlerFunction = handler; +}; + +const requestToHandler = input => { + const request = new Request(input); + const event = { + request, + response: {}, + respondWith(res) { + this.response = res; + } + }; + + try { + handlerFunction(event); + + Promise.resolve( + event.response + ).then(res => { + result = { + data: res.body, + headers: res.headers.headers, + status: res.status, + }; + }) + .catch((err) => { + error = `err: \n${err}`; + }); + } catch (err) { + error = `err: ${err}\n${err.stack}`; + } +}; + +globalThis.entrypoint = requestToHandler; +globalThis.result = {}; +globalThis.error = null diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/async_hooks.d.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/async_hooks.d.ts new file mode 100644 index 000000000..4e97d10a0 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/async_hooks.d.ts @@ -0,0 +1,22 @@ +// Type definitions for c++ implementation. + +export interface AsyncResourceOptions { + triggerAsyncId?: number; +} + +export class AsyncResource { + public constructor(type: string, options?: AsyncResourceOptions); + public runInAsyncScope(fn: (...args: unknown[]) => R, ...args: unknown[]): R; + + public bind unknown>( + fn: Func): Func & { asyncResource: AsyncResource; }; + + public static bind unknown, ThisArg>( + fn: Func, type?: string, thisArg?: ThisArg): Func & { asyncResource: AsyncResource; }; +} + +export class AsyncLocalStorage { + public run(store: T, fn: (...args: unknown[]) => R, ...args: unknown[]): R; + public exit(fn: (...args: unknown[]) => R, ...args: unknown[]): R; + public getStore(): T; +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/async_hooks.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/async_hooks.ts new file mode 100644 index 000000000..a78e2a726 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/async_hooks.ts @@ -0,0 +1,26 @@ +export interface AsyncResourceOptions { + triggerAsyncId?: number; +} + +export class AsyncResource { + // @ts-ignore + public constructor(type: string, options?: AsyncResourceOptions); + // @ts-ignore + public runInAsyncScope(fn: (...args: unknown[]) => R, ...args: unknown[]): R; + + public bind unknown>( + fn: Func): Func & { asyncResource: AsyncResource; }; + + // @ts-ignore + public static bind unknown, ThisArg>( + fn: Func, type?: string, thisArg?: ThisArg): Func & { asyncResource: AsyncResource; }; +} + +export class AsyncLocalStorage { + // @ts-ignore + public run(store: T, fn: (...args: unknown[]) => R, ...args: unknown[]): R; + // @ts-ignore + public exit(fn: (...args: unknown[]) => R, ...args: unknown[]): R; + // @ts-ignore + public getStore(): T; +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/buffer.d.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/buffer.d.ts new file mode 100644 index 000000000..d387b6aa6 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/buffer.d.ts @@ -0,0 +1,36 @@ + +export interface CompareOptions { + aStart?: number; + aEnd?: number; + bStart?: number; + bEnd?: number +} + +type BufferSource = ArrayBufferView | ArrayBuffer; + +export function byteLength(value: string): number; +export function compare(a: Uint8Array, b: Uint8Array, options?: CompareOptions): number; +export function concat(list: Uint8Array[], length: number): ArrayBuffer; +export function decodeString(value: string, encoding: string): ArrayBuffer; +export function fillImpl(buffer: Uint8Array, + value: string | BufferSource, + start: number, + end: number, + encoding?: string): void; +export function indexOf(buffer: Uint8Array, + value: string | Uint8Array, + byteOffset?: number, + encoding?: string, + findLast?: boolean): number | undefined; +export function swap(buffer: Uint8Array, size: 16|32|64): void; +export function toString(buffer: Uint8Array, + start: number, + end: number, + encoding: string): string; +export function write(buffer: Uint8Array, + value: string, + offset: number, + length: number, + encoding: string): void; +export function decode(buffer: Uint8Array, state: Uint8Array): string; +export function flush(state: Uint8Array): string; \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/buffer.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/buffer.ts new file mode 100644 index 000000000..8ea27b939 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/buffer.ts @@ -0,0 +1,539 @@ +import {isInstance} from "./internal_buffer"; +import * as base64 from "base64-js"; +import { TextEncoder, TextDecoder } from "@sinonjs/text-encoding"; + +const hexCharValueTable = { + "0": 0, + "1": 1, + "2": 2, + "3": 3, + "4": 4, + "5": 5, + "6": 6, + "7": 7, + "8": 8, + "9": 9, + a: 10, + b: 11, + c: 12, + d: 13, + e: 14, + f: 15, + A: 10, + B: 11, + C: 12, + D: 13, + E: 14, + F: 15, +}; + +export function byteLength(string, encoding) { + if (Buffer.isBuffer(string)) { + return string.length; + } + if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { + return string.byteLength; + } + if (typeof string !== "string") { + throw new TypeError( + 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + + "Received type " + + typeof string + ); + } + + const len = string.length; + const mustMatch = arguments.length > 2 && arguments[2] === true; + if (!mustMatch && len === 0) return 0; + + // Use a for loop to avoid recursion + let loweredCase = false; + for (;;) { + switch (encoding) { + case "ascii": + case "latin1": + case "binary": + return len; + case "utf8": + case "utf-8": + return utf8ToBytes(string).length; + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return len * 2; + case "hex": + return len >>> 1; + case "base64": + return base64ToBytes(string).length; + default: + if (loweredCase) { + return mustMatch ? -1 : utf8ToBytes(string).length; // assume utf8 + } + encoding = ("" + encoding).toLowerCase(); + loweredCase = true; + } + } +} + +// @ts-ignore + +export function compare(a: Uint8Array, b: Uint8Array, options?): number { + const aStart = options?.aStart ?? 0; + const aEnd = options?.aEnd ?? a.length; + const bStart = options?.bStart ?? 0; + const bEnd = options?.bEnd ?? b.length; + + const sliceA = a.slice(aStart, aEnd); + const sliceB = b.slice(bStart, bEnd); + + let x = sliceA.length; + let y = sliceB.length; + + for (let i = 0, len = Math.min(x, y); i < len; ++i) { + if (sliceA[i] !== sliceB[i]) { + x = sliceA[i]; + y = sliceB[i]; + break; + } + } + + if (x < y) return -1; + if (y < x) return 1; + return 0; +} + +export function concat(list: Uint8Array[], length: number): ArrayBuffer { + const result = new Uint8Array(length); + let offset = 0; + for (let i = 0; i < list.length; i++) { + result.set(list[i], offset); + offset += list[i].length; + } + return result.buffer; +} + +export function toString( + buffer: Uint8Array, + start: number, + end: number, + encoding: string +): string { + const slice = buffer.slice(start, end); + const decoder = new TextDecoder(encoding); + return decoder.decode(slice); +} + +export function swap(buffer: Uint8Array, size: 16 | 32 | 64): void { + const length = buffer.length; + if (length % size !== 0) { + throw new RangeError("Buffer size must be a multiple of " + size); + } + + for (let i = 0; i < length; i += size) { + let left = i; + let right = i + size - 1; + while (left < right) { + const temp = buffer[left]; + buffer[left] = buffer[right]; + buffer[right] = temp; + left++; + right--; + } + } +} + +export function decodeString(value: string, encoding: string): ArrayBuffer { + const encoder = new TextEncoder(); + return encoder.encode(value).buffer; +} + +export function write( + buffer: Uint8Array, + value: string, + offset: number, + length: number, + encoding: string +): void { + let loweredCase = false; + for (;;) { + switch (encoding) { + case "hex": + return hexWrite(buffer, value, offset, length); + + case "utf8": + case "utf-8": + return utf8Write(buffer, value, offset, length); + + case "ascii": + case "latin1": + case "binary": + return asciiWrite(buffer, value, offset, length); + + case "base64": + // Warning: maxLength not taken into account in base64Write + return base64Write(buffer, value, offset, length); + + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return ucs2Write(buffer, value, offset, length); + + default: + if (loweredCase) throw new TypeError("Unknown encoding: " + encoding); + encoding = ("" + encoding).toLowerCase(); + loweredCase = true; + } + } +} + +export function fillImpl( + buffer: Uint8Array, + val: string | BufferSource | number | boolean, + start: number, + end: number, + encoding?: string +): void { + let i; + if (typeof val === "number") { + for (i = start; i < end; ++i) { + buffer[i] = val; + } + } else { + const bytes = Buffer.isBuffer(val) + ? val + : Buffer.from(val as string, encoding); + const len = bytes.length; + for (i = 0; i < end - start; ++i) { + buffer[i + start] = bytes[i % len]; + } + } +} + +export function indexOf(buffer, val, byteOffset, encoding, dir) { +// Empty buffer means no match + if (buffer.length === 0) return -1; + + // Normalize byteOffset + if (typeof byteOffset === "string") { + encoding = byteOffset; + byteOffset = 0; + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff; + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000; + } + byteOffset = +byteOffset; // Coerce to Number. + if (numberIsNaN(byteOffset)) { + // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer + byteOffset = dir ? 0 : buffer.length - 1; + } + + // Normalize byteOffset: negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = buffer.length + byteOffset; + if (byteOffset >= buffer.length) { + if (dir) return -1; + else byteOffset = buffer.length - 1; + } else if (byteOffset < 0) { + if (dir) byteOffset = 0; + else return -1; + } + + // Normalize val + if (typeof val === "string") { + val = Buffer.from(val, encoding); + } + + // Finally, search either indexOf (if dir is true) or lastIndexOf + if (Buffer.isBuffer(val)) { + // Special case: looking for empty string/buffer always fails + if (val.length === 0) { + return -1; + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir); + } else if (typeof val === "number") { + val = val & 0xff; // Search for a byte value [0-255] + if (typeof Uint8Array.prototype.indexOf === "function") { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset); + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset); + } + } + return arrayIndexOf(buffer, [val], byteOffset, encoding, dir); + } + + throw new TypeError("val must be string, number or Buffer"); +} + +function arrayIndexOf(arr, val, byteOffset, encoding, dir) { + let indexSize = 1; + let arrLength = arr.length; + let valLength = val.length; + + if (encoding !== undefined) { + encoding = String(encoding).toLowerCase(); + if ( + encoding === "ucs2" || + encoding === "ucs-2" || + encoding === "utf16le" || + encoding === "utf-16le" + ) { + if (arr.length < 2 || val.length < 2) { + return -1; + } + indexSize = 2; + arrLength /= 2; + valLength /= 2; + byteOffset /= 2; + } + } + + function read(buf, i) { + if (indexSize === 1) { + return buf[i]; + } else { + return buf.readUInt16BE(i * indexSize); + } + } + + let i; + if (dir) { + let foundIndex = -1; + for (i = byteOffset; i < arrLength; i++) { + if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { + if (foundIndex === -1) foundIndex = i; + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize; + } else { + if (foundIndex !== -1) i -= i - foundIndex; + foundIndex = -1; + } + } + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength; + for (i = byteOffset; i >= 0; i--) { + let found = true; + for (let j = 0; j < valLength; j++) { + if (read(arr, i + j) !== read(val, j)) { + found = false; + break; + } + } + if (found) return i; + } + } + + return -1; +} + +function utf8ToBytes(string, units?) { + units = units || Infinity; + let codePoint; + const length = string.length; + let leadSurrogate = null; + const bytes = []; + + for (let i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i); + + // is surrogate component + if (codePoint > 0xd7ff && codePoint < 0xe000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xdbff) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd); + continue; + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd); + continue; + } + + // valid lead + leadSurrogate = codePoint; + + continue; + } + + // 2 leads in a row + if (codePoint < 0xdc00) { + if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd); + leadSurrogate = codePoint; + continue; + } + + // valid surrogate pair + codePoint = + (((leadSurrogate - 0xd800) << 10) | (codePoint - 0xdc00)) + 0x10000; + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd); + } + + leadSurrogate = null; + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break; + bytes.push(codePoint); + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break; + bytes.push((codePoint >> 0x6) | 0xc0, (codePoint & 0x3f) | 0x80); + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break; + bytes.push( + (codePoint >> 0xc) | 0xe0, + ((codePoint >> 0x6) & 0x3f) | 0x80, + (codePoint & 0x3f) | 0x80 + ); + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break; + bytes.push( + (codePoint >> 0x12) | 0xf0, + ((codePoint >> 0xc) & 0x3f) | 0x80, + ((codePoint >> 0x6) & 0x3f) | 0x80, + (codePoint & 0x3f) | 0x80 + ); + } else { + throw new Error("Invalid code point"); + } + } + + return bytes; +} + +function hexWrite(buf, string, offset, length) { + offset = Number(offset) || 0; + const remaining = buf.length - offset; + if (!length) { + length = remaining; + } else { + length = Number(length); + if (length > remaining) { + length = remaining; + } + } + + const strLen = string.length; + + if (length > strLen / 2) { + length = strLen / 2; + } + let i; + for (i = 0; i < length; ++i) { + const a = hexCharValueTable[string[i * 2]]; + const b = hexCharValueTable[string[i * 2 + 1]]; + if (a === undefined || b === undefined) { + return i; + } + buf[offset + i] = (a << 4) | b; + } + return i; +} + +function base64ToBytes(str) { + return base64.toByteArray(base64clean(str)); +} + +function utf8Write(buf, string, offset, length) { + return blitBuffer( + utf8ToBytes(string, buf.length - offset), + buf, + offset, + length + ); +} + +function asciiWrite(buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length); +} + +function base64Write(buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length); +} + +function ucs2Write(buf, string, offset, length) { + return blitBuffer( + utf16leToBytes(string, buf.length - offset), + buf, + offset, + length + ); +} + +function asciiToBytes(str) { + const byteArray = []; + for (let i = 0; i < str.length; ++i) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xff); + } + return byteArray; +} + +function utf16leToBytes(str, units) { + let c, hi, lo; + const byteArray = []; + for (let i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) break; + + c = str.charCodeAt(i); + hi = c >> 8; + lo = c % 256; + byteArray.push(lo); + byteArray.push(hi); + } + + return byteArray; +} + +const INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g; + +function base64clean(str) { + // Node takes equal signs as end of the Base64 encoding + str = str.split("=")[0]; + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = str.trim().replace(INVALID_BASE64_RE, ""); + // Node converts strings with length < 2 to '' + if (str.length < 2) return ""; + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + "="; + } + return str; +} + +function blitBuffer(src, dst, offset, length) { + let i; + for (i = 0; i < length; ++i) { + if (i + offset >= dst.length || i >= src.length) break; + dst[i + offset] = src[i]; + } + return i; +} + +function numberIsNaN(obj) { + // For IE11 support + return obj !== obj; // eslint-disable-line no-self-compare +} + +export function flush(state: Uint8Array): string { + // Create a new Uint8Array object from the state. + const buffer = new Uint8Array(state); + return String.fromCharCode.apply(null, buffer); +} + +export function decode(buffer: Uint8Array, state: Uint8Array): string { + let result = ""; + for (let i = 0; i < buffer.length; i++) { + const byte = buffer[i]; + const char = state[byte]; + if (char === undefined) { + throw new Error("Invalid byte"); + } + result += char; + } + return result; +} \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/constants.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/constants.ts new file mode 100644 index 000000000..88ec50573 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/constants.ts @@ -0,0 +1,45 @@ + +export const CHAR_UPPERCASE_A = 65; /* A */ +export const CHAR_LOWERCASE_A = 97; /* a */ +export const CHAR_UPPERCASE_Z = 90; /* Z */ +export const CHAR_LOWERCASE_Z = 122; /* z */ +export const CHAR_UPPERCASE_C = 67; /* C */ +export const CHAR_LOWERCASE_B = 98; /* b */ +export const CHAR_LOWERCASE_E = 101; /* e */ +export const CHAR_LOWERCASE_N = 110; /* n */ +export const CHAR_DOT = 46; /* . */ +export const CHAR_FORWARD_SLASH = 47; /* / */ +export const CHAR_BACKWARD_SLASH = 92; /* \ */ +export const CHAR_VERTICAL_LINE = 124; /* | */ +export const CHAR_COLON = 58; /* : */ +export const CHAR_QUESTION_MARK = 63; /* ? */ +export const CHAR_UNDERSCORE = 95; /* _ */ +export const CHAR_LINE_FEED = 10; /* \n */ +export const CHAR_CARRIAGE_RETURN = 13; /* \r */ +export const CHAR_TAB = 9; /* \t */ +export const CHAR_FORM_FEED = 12; /* \f */ +export const CHAR_EXCLAMATION_MARK = 33; /* ! */ +export const CHAR_HASH = 35; /* # */ +export const CHAR_SPACE = 32; /* */ +export const CHAR_NO_BREAK_SPACE = 160; /* \u00A0 */ +export const CHAR_ZERO_WIDTH_NOBREAK_SPACE = 65279; /* \uFEFF */ +export const CHAR_LEFT_SQUARE_BRACKET = 91; /* [ */ +export const CHAR_RIGHT_SQUARE_BRACKET = 93; /* ] */ +export const CHAR_LEFT_ANGLE_BRACKET = 60; /* < */ +export const CHAR_RIGHT_ANGLE_BRACKET = 62; /* > */ +export const CHAR_LEFT_CURLY_BRACKET = 123; /* { */ +export const CHAR_RIGHT_CURLY_BRACKET = 125; /* } */ +export const CHAR_HYPHEN_MINUS = 45; /* - */ +export const CHAR_PLUS = 43; /* + */ +export const CHAR_DOUBLE_QUOTE = 34; /* " */ +export const CHAR_SINGLE_QUOTE = 39; /* ' */ +export const CHAR_PERCENT = 37; /* % */ +export const CHAR_SEMICOLON = 59; /* ; */ +export const CHAR_CIRCUMFLEX_ACCENT = 94; /* ^ */ +export const CHAR_GRAVE_ACCENT = 96; /* ` */ +export const CHAR_AT = 64; /* @ */ +export const CHAR_AMPERSAND = 38; /* & */ +export const CHAR_EQUAL = 61; /* = */ +export const CHAR_0 = 48; /* 0 */ +export const CHAR_9 = 57; /* 9 */ +export const EOL = '\;'; \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto.d.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto.d.ts new file mode 100644 index 000000000..6fb967959 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto.d.ts @@ -0,0 +1,232 @@ +import { + Buffer, +} from './internal_buffer'; + +// random +export function checkPrimeSync(candidate: ArrayBufferView, num_checks: number): boolean; +export function randomPrime(size: number, safe: boolean, add?: ArrayBufferView|undefined, + rem?: ArrayBufferView|undefined): ArrayBuffer; + +// Hash and Hmac +export class HashHandle { + public constructor(algorithm: string, xofLen: number); + public update(data: Buffer | ArrayBufferView): number; + public digest(): ArrayBuffer; + public copy(xofLen: number): HashHandle; +} + +export type ArrayLike = ArrayBuffer|string|Buffer|ArrayBufferView; + +export class HmacHandle { + public constructor(algorithm: string, key: ArrayLike | CryptoKey); + public update(data: Buffer | ArrayBufferView): number; + public digest(): ArrayBuffer; +} + +// hkdf +export function getHkdf(hash: string, key: ArrayLike, salt: ArrayLike, info: ArrayLike, + length: number): ArrayBuffer; + +// pbkdf2 +export function getPbkdf(password: ArrayLike, salt: ArrayLike, iterations: number, keylen: number, + digest: string): ArrayBuffer; + +// Keys +export function exportKey(key: CryptoKey, options?: InnerExportOptions): KeyExportResult; +export function equals(key: CryptoKey, otherKey: CryptoKey): boolean; +export function getAsymmetricKeyDetail(key: CryptoKey): AsymmetricKeyDetails; +export function getAsymmetricKeyType(key: CryptoKey): AsymmetricKeyType; +export function createSecretKey(key: ArrayBuffer | ArrayBufferView): CryptoKey; +export function createPrivateKey(key: InnerCreateAsymmetricKeyOptions): CryptoKey; +export function createPublicKey(key: InnerCreateAsymmetricKeyOptions): CryptoKey; + +export type KeyData = string | ArrayBuffer | ArrayBufferView; +// +// export interface RsaKeyAlgorithm { +// name: 'rsa' | 'rsa-pss'; +// modulusLength: number; +// publicExponent: Uint8Array; +// hash?: string; +// } +// +// export interface EcKeyAlgorithm { +// name: 'ec'; +// namedCurve: string; +// } +// +// export interface DhKeyAlgorithm { +// name: 'dh'; +// prime: Uint8Array; +// generator: Uint8Array; +// } +// +// export interface DsaKeyAlgorithm { +// name: 'dsa'; +// prime: Uint8Array; +// divisorLength: number; +// } +// +// export interface HmacKeyAlgorithm { +// name: 'hmac'; +// hash: string; +// } +// +// export interface AesKeyAlgorithm { +// name: 'aes'; +// length: number; +// } +// +// export type KeyAlgorithm = RsaKeyAlgorithm | +// EcKeyAlgorithm | +// DhKeyAlgorithm | +// DsaKeyAlgorithm | +// HmacKeyAlgorithm | +// AesKeyAlgorithm; +// +// export interface CryptoKey { +// algorithm: KeyAlgorithm; +// extractable: boolean; +// type: KeyObjectType; +// usages: string[]; +// } +// +// export interface RsaOtherPrimesInfo { +// d?: string; +// r?: string; +// t?: string; +// } +// +// export interface JsonWebKey { +// alg?: string; +// crv?: string; +// d?: string; +// dp?: string; +// dq?: string; +// e?: string; +// ext?: boolean; +// k?: string; +// key_ops?: string[]; +// kty?: string; +// n?: string; +// oth?: Array; +// p?: string; +// q?: string; +// qi?: string; +// use?: string; +// x?: string; +// y?: string; +// } +// +// export interface CryptoKeyPair { +// privateKey: CryptoKey; +// publicKey: CryptoKey; +// } +// +// export type KeyObjectType = 'secret' | 'public' | 'private'; +// +// export type KeyExportResult = string | Buffer | JsonWebKey; +// +// export type SecretKeyFormat = 'buffer' | 'jwk'; +// export type AsymmetricKeyFormat = 'pem' | 'der' | 'jwk'; +// export type PublicKeyEncoding = 'pkcs1' | 'spki'; +// export type PrivateKeyEncoding = 'pkcs1' | 'pkcs8' | 'sec1'; +// export type AsymmetricKeyType = 'rsa' | 'rsa-pss' | 'dsa' | 'ec' | 'x25519' | 'ed25519' | 'dh'; +// export type SecretKeyType = 'hmac' | 'aes'; +// export type ParamEncoding = 'named' | 'explicit'; +// +// export interface SecretKeyExportOptions { +// format?: SecretKeyFormat; +// } +// +// export interface PublicKeyExportOptions { +// type?: PublicKeyEncoding; +// format?: AsymmetricKeyFormat; +// } +// +// export interface PrivateKeyExportOptions { +// type?: PrivateKeyEncoding; +// format?: AsymmetricKeyFormat; +// cipher?: string; +// passphrase?: string | Uint8Array; +// encoding?: string; +// } +// +// export interface InnerPrivateKeyExportOptions { +// type?: PrivateKeyEncoding; +// format?: AsymmetricKeyFormat; +// cipher?: string; +// passphrase?: Uint8Array; +// } +// +// export type ExportOptions = SecretKeyExportOptions | +// PublicKeyExportOptions | +// PrivateKeyExportOptions; +// +// export type InnerExportOptions = SecretKeyExportOptions | +// PublicKeyExportOptions | +// InnerPrivateKeyExportOptions; +// +// export interface AsymmetricKeyDetails { +// modulusLength?: number; +// publicExponent?: bigint; +// hashAlgorithm?: string; +// mgf1HashAlgorithm?: string; +// saltLength?: number; +// divisorLength?: number; +// namedCurve?: string; +// } +// +// export interface CreateAsymmetricKeyOptions { +// key: string | ArrayBuffer | ArrayBufferView | JsonWebKey; +// format?: AsymmetricKeyFormat; +// type?: PublicKeyEncoding | PrivateKeyEncoding; +// passphrase?: string | Uint8Array; +// encoding?: string; +// } +// +// export interface InnerCreateAsymmetricKeyOptions { +// key?: ArrayBuffer | ArrayBufferView | JsonWebKey | CryptoKey; +// format?: AsymmetricKeyFormat; +// type?: PublicKeyEncoding | PrivateKeyEncoding; +// passphrase?: Uint8Array; +// } +// +// export interface GenerateKeyOptions { +// length: number; +// } +// +// export interface GenerateKeyPairOptions { +// modulusLength?: number; +// publicExponent?: number|bigint; +// hashAlgorithm?: string; +// mgf1HashAlgorithm?: string; +// saltLength?: number; +// divisorLength?: number; +// namedCurve?: string; +// prime?: Uint8Array; +// primeLength?: number; +// generator?: number; +// groupName?: string; +// paramEncoding?: ParamEncoding; +// publicKeyEncoding?: PublicKeyExportOptions; +// privateKeyEncoding?: PrivateKeyExportOptions; +// } + +// DiffieHellman +export class DiffieHellmanHandle { + public constructor(sizeOrKey: number | ArrayBuffer | ArrayBufferView, + generator: number | ArrayBuffer | ArrayBufferView); + public setPublicKey(data: ArrayBuffer | ArrayBufferView | Buffer): void; + public setPrivateKey(data: ArrayBuffer | ArrayBufferView | Buffer): void; + public getPublicKey(): ArrayBuffer; + public getPrivateKey(): ArrayBuffer; + public getGenerator(): ArrayBuffer; + public getPrime(): ArrayBuffer; + + public computeSecret(key: ArrayBuffer|ArrayBufferView): ArrayBuffer; + public generateKeys(): ArrayBuffer; + + public getVerifyError(): number; +} + +export function DiffieHellmanGroupHandle(name: string): DiffieHellmanHandle; diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto.ts new file mode 100644 index 000000000..088a1d7db --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto.ts @@ -0,0 +1,484 @@ +import * as sjcl from 'sjcl'; +import {Buffer} from "./internal_buffer"; + +export type ArrayLike = ArrayBuffer|string|Buffer|ArrayBufferView; +export type KeyData = string | ArrayBuffer | ArrayBufferView; + + +/** + * Checks if a number represented by an ArrayBufferView is prime. + * + * @param {ArrayBufferView} candidate - The ArrayBufferView representing the number to check. + * @param {number} num_checks - The number of checks to perform. + * @returns {boolean} - Returns true if the number is prime, false otherwise. + */ +export function checkPrimeSync(candidate: ArrayBufferView, num_checks: number): boolean { + // Convert ArrayBufferView to number + let num = new Uint32Array(candidate.buffer)[0]; + + // Check if num is less than 2 (not a prime number) + if (num < 2) return false; + + // Check if num is divisible by any number up to its square root + for (let i = 2, sqrt = Math.sqrt(num); i <= sqrt; i++) { + if (num % i === 0) return false; + } + + // If no factors found, num is a prime number + return true; +} + +/** + * Generates a random prime number of a given size. + * + * @param {number} size - The size of the prime number to generate. + * @param {boolean} safe - If true, generates a safe prime (a prime number that is 2 less than another prime number). + * @param {ArrayBufferView} [add] - An ArrayBufferView representing a number to add to the generated prime number. + * @param {ArrayBufferView} [rem] - An ArrayBufferView representing a number to take the remainder of the generated prime number. + * @returns {ArrayBuffer} - Returns an ArrayBuffer representing the generated prime number. + */ +export function randomPrime(size: number, safe: boolean, add?: ArrayBufferView, rem?: ArrayBufferView): ArrayBuffer { + let prime: number; + do { + prime = sjcl.random.randomWords(1, 0)[0]; + prime = Math.abs(prime) % (2 ** size); + if (safe) { + prime = 2 * prime + 1; + } + if (add) { + prime += new Uint32Array(add.buffer)[0]; + } + if (rem) { + prime %= new Uint32Array(rem.buffer)[0]; + } + } while (!checkPrimeSync(new Uint32Array([prime]), 10)); + + return new Uint32Array([prime]).buffer; +} + +// hkdf +export function getHkdf(hash: string, key: ArrayLike, salt: ArrayLike, info: ArrayLike, length: number): ArrayBuffer { + // Convert key, salt, and info to bitArrays + let keyBits = sjcl.codec.utf8String.toBits(key.toString()); + let saltBits = sjcl.codec.utf8String.toBits(salt.toString()); + let infoBits = sjcl.codec.utf8String.toBits(info.toString()); + + // Use sjcl.misc.hkdf to generate the key + // @ts-ignore + let derivedKeyBits = sjcl.misc.hkdf(keyBits, length, saltBits, hash, infoBits); + + // Convert the derived key to an ArrayBuffer and return it + return sjcl.codec.arrayBuffer.fromBits(derivedKeyBits); +} + +// pbkdf2 +export function getPbkdf(password: ArrayLike, salt: ArrayLike, iterations: number, keylen: number, digest: string): ArrayBuffer { + // Convert password and salt to bitArrays + let passwordBits = sjcl.codec.utf8String.toBits(password.toString()); + let saltBits = sjcl.codec.utf8String.toBits(salt.toString()); + + // Use sjcl.misc.pbkdf2 to generate the key + // @ts-ignore + let derivedKeyBits = sjcl.misc.pbkdf2(passwordBits, saltBits, iterations, keylen * 8, digest); + + // Convert the derived key to an ArrayBuffer and return it + return sjcl.codec.arrayBuffer.fromBits(derivedKeyBits); +} + + +export class HashHandle { + private hash: sjcl.SjclHash; + + public constructor(algorithm: string, xofLen: number) { + switch (algorithm) { + case 'sha1': + this.hash = new sjcl.hash.sha1(); + break; + case 'sha256': + this.hash = new sjcl.hash.sha256(); + break; + case 'sha512': + this.hash = new sjcl.hash.sha512(); + break; + default: + throw new Error(`Unsupported hash algorithm: ${algorithm}`); + } + + if (xofLen !== 0) { + throw new Error(`Unsupported xofLen: ${xofLen}`); + } + } + + public update(data: Buffer | ArrayBufferView): number { + let dataBits = sjcl.codec.utf8String.toBits(data.toString()); + this.hash.update(dataBits); + return this.hash.finalize().length; + } + + public digest(): ArrayBuffer { + let digestBits = this.hash.finalize(); + return sjcl.codec.arrayBuffer.fromBits(digestBits); + } + + public copy(xofLen: number): HashHandle { + let algo =""; + let hash = this.hash; + switch (true) { + case hash instanceof sjcl.hash.sha1: + algo = 'sha1'; + break; + case hash instanceof sjcl.hash.sha256: + algo = 'sha256'; + break; + case hash instanceof sjcl.hash.sha512: + algo = 'sha512'; + break; + default: + throw new Error(`Unsupported hash algorithm: ${algo}`); + } + let copy = new HashHandle( algo , xofLen); // Replace 'sha256' with the actual algorithm + copy.hash = this.hash + return copy; + } +} + +export class HmacHandle { + private hmac: sjcl.SjclHMAC; + + public constructor(algorithm: string, key: ArrayLike | CryptoKey) { + let keyBits = sjcl.codec.utf8String.toBits(key.toString()); + switch (algorithm) { + case 'sha1': + this.hmac = new sjcl.misc.hmac(keyBits, sjcl.hash.sha1); + break; + case 'sha256': + this.hmac = new sjcl.misc.hmac(keyBits, sjcl.hash.sha256); + break; + case 'sha512': + this.hmac = new sjcl.misc.hmac(keyBits, sjcl.hash.sha512); + break; + default: + throw new Error(`Unsupported hash algorithm: ${algorithm}`); + } + } + + public update(data: Buffer | ArrayBufferView): number { + let dataBits = sjcl.codec.utf8String.toBits(data.toString()); + this.hmac.update(dataBits); + return this.hmac.digest().length; + } + + public digest(): ArrayBuffer { + let digestBits = this.hmac.digest(); + return sjcl.codec.arrayBuffer.fromBits(digestBits); + } +} + +export interface RsaKeyAlgorithm { + name: 'rsa' | 'rsa-pss'; + modulusLength: number; + publicExponent: Uint8Array; + hash?: string; +} + +export interface EcKeyAlgorithm { + name: 'ec'; + namedCurve: string; +} + +export interface DhKeyAlgorithm { + name: 'dh'; + prime: Uint8Array; + generator: Uint8Array; +} + +export interface DsaKeyAlgorithm { + name: 'dsa'; + prime: Uint8Array; + divisorLength: number; +} + +export interface HmacKeyAlgorithm { + name: 'hmac'; + hash: string; +} + +export interface AesKeyAlgorithm { + name: 'aes'; + length: number; +} + +export type KeyAlgorithm = RsaKeyAlgorithm | + EcKeyAlgorithm | + DhKeyAlgorithm | + DsaKeyAlgorithm | + HmacKeyAlgorithm | + AesKeyAlgorithm; + +export interface CryptoKey { + algorithm: KeyAlgorithm; + extractable: boolean; + type: KeyObjectType; + usages: string[]; +} + +export interface RsaOtherPrimesInfo { + d?: string; + r?: string; + t?: string; +} + +export interface JsonWebKey { + alg?: string; + crv?: string; + d?: string; + dp?: string; + dq?: string; + e?: string; + ext?: boolean; + k?: string; + key_ops?: string[]; + kty?: string; + n?: string; + oth?: Array; + p?: string; + q?: string; + qi?: string; + use?: string; + x?: string; + y?: string; +} + +export interface CryptoKeyPair { + privateKey: CryptoKey; + publicKey: CryptoKey; +} + +export type KeyObjectType = 'secret' | 'public' | 'private'; + +export type KeyExportResult = string | Buffer | JsonWebKey; + +export type SecretKeyFormat = 'buffer' | 'jwk'; +export type AsymmetricKeyFormat = 'pem' | 'der' | 'jwk'; +export type PublicKeyEncoding = 'pkcs1' | 'spki'; +export type PrivateKeyEncoding = 'pkcs1' | 'pkcs8' | 'sec1'; +export type AsymmetricKeyType = 'rsa' | 'rsa-pss' | 'dsa' | 'ec' | 'x25519' | 'ed25519' | 'dh'; +export type SecretKeyType = 'hmac' | 'aes'; +export type ParamEncoding = 'named' | 'explicit'; + +export interface SecretKeyExportOptions { + format?: SecretKeyFormat; +} + +export interface PublicKeyExportOptions { + type?: PublicKeyEncoding; + format?: AsymmetricKeyFormat; +} + +export interface PrivateKeyExportOptions { + type?: PrivateKeyEncoding; + format?: AsymmetricKeyFormat; + cipher?: string; + passphrase?: string | Uint8Array; + encoding?: string; +} + +export interface InnerPrivateKeyExportOptions { + type?: PrivateKeyEncoding; + format?: AsymmetricKeyFormat; + cipher?: string; + passphrase?: Uint8Array; +} + +export type ExportOptions = SecretKeyExportOptions | + PublicKeyExportOptions | + PrivateKeyExportOptions; + +export type InnerExportOptions = SecretKeyExportOptions | + PublicKeyExportOptions | + InnerPrivateKeyExportOptions; + +export interface AsymmetricKeyDetails { + modulusLength?: number; + publicExponent?: bigint; + hashAlgorithm?: string; + mgf1HashAlgorithm?: string; + saltLength?: number; + divisorLength?: number; + namedCurve?: string; +} + +export interface CreateAsymmetricKeyOptions { + key: string | ArrayBuffer | ArrayBufferView | JsonWebKey; + format?: AsymmetricKeyFormat; + type?: PublicKeyEncoding | PrivateKeyEncoding; + passphrase?: string | Uint8Array; + encoding?: string; +} + +export interface InnerCreateAsymmetricKeyOptions { + key?: ArrayBuffer | ArrayBufferView | JsonWebKey | CryptoKey; + format?: AsymmetricKeyFormat; + type?: PublicKeyEncoding | PrivateKeyEncoding; + passphrase?: Uint8Array; +} + +export interface GenerateKeyOptions { + length: number; +} + +export interface GenerateKeyPairOptions { + modulusLength?: number; + publicExponent?: number|bigint; + hashAlgorithm?: string; + mgf1HashAlgorithm?: string; + saltLength?: number; + divisorLength?: number; + namedCurve?: string; + prime?: Uint8Array; + primeLength?: number; + generator?: number; + groupName?: string; + paramEncoding?: ParamEncoding; + publicKeyEncoding?: PublicKeyExportOptions; + privateKeyEncoding?: PrivateKeyExportOptions; +} + +export function exportKey(key: CryptoKey, options?: InnerExportOptions): KeyExportResult { + // SJCL does not provide a direct method to export keys. + throw new Error("Function exportKey is not implemented yet"); +} + +export function equals(key: CryptoKey, otherKey: CryptoKey): boolean { + // SJCL does not provide a direct method to compare keys. + throw new Error("Function equals is not implemented yet"); +} + +export function getAsymmetricKeyDetail(key: CryptoKey): AsymmetricKeyDetails { + // SJCL does not provide a direct method to get asymmetric key details. + throw new Error("Function getAsymmetricKeyDetail is not implemented yet"); +} + +export function getAsymmetricKeyType(key: CryptoKey): AsymmetricKeyType { + // SJCL does not provide a direct method to get asymmetric key type. + throw new Error("Function getAsymmetricKeyType is not implemented yet"); +} + +export function createSecretKey(key: ArrayBuffer | ArrayBufferView): CryptoKey { + let keyArray: Uint8Array; + if (key instanceof ArrayBuffer) { + keyArray = new Uint8Array(key); + } else { + keyArray = new Uint8Array(key.buffer, key.byteOffset, key.byteLength); + } + + let keyBits = sjcl.codec.bytes.toBits(Array.from(keyArray)); + + let cipher = new sjcl.cipher.aes(keyBits); + + return { + algorithm: { + name: 'aes', + length: key.byteLength * 8 + }, + extractable: true, + type: 'secret', + usages: ['encrypt', 'decrypt'] + }; +} + +export function createPrivateKey(key: InnerCreateAsymmetricKeyOptions): CryptoKey { + // SJCL does not provide a direct method to create private keys. + throw new Error("Function createPrivateKey is not implemented yet"); +} + +export function createPublicKey(key: InnerCreateAsymmetricKeyOptions): CryptoKey { + // SJCL does not provide a direct method to create public keys. + throw new Error("Function createPublicKey is not implemented yet"); +} + + +export class DiffieHellmanHandle { + private prime: sjcl.BigNumber; + private generator: sjcl.BigNumber; + private privateKey: sjcl.BigNumber; + private publicKey: sjcl.BigNumber; + + public constructor(sizeOrKey: number | ArrayBuffer | ArrayBufferView, + generator: number | ArrayBuffer | ArrayBufferView) { + // Convert sizeOrKey and generator to sjcl.bn + this.prime = new sjcl.bn(sizeOrKey.toString()); + this.generator = new sjcl.bn(generator.toString()); + + // Generate a random private key + this.privateKey = sjcl.bn.random(this.prime.sub(2), 10).add(1); + + // Calculate the public key + this.publicKey = this.generator.powermod(this.privateKey, this.prime); + } + + public setPublicKey(data: ArrayBuffer | ArrayBufferView | Buffer): void { + this.publicKey = new sjcl.bn(data.toString()); + } + + public setPrivateKey(data: ArrayBuffer | ArrayBufferView | Buffer): void { + this.privateKey = new sjcl.bn(data.toString()); + } + + public getPublicKey(): ArrayBuffer { + return sjcl.codec.arrayBuffer.fromBits(this.publicKey.toBits()); + } + + public getPrivateKey(): ArrayBuffer { + return sjcl.codec.arrayBuffer.fromBits(this.privateKey.toBits()); + } + + public getGenerator(): ArrayBuffer { + return sjcl.codec.arrayBuffer.fromBits(this.generator.toBits()); + } + + public getPrime(): ArrayBuffer { + return sjcl.codec.arrayBuffer.fromBits(this.prime.toBits()); + } + + public computeSecret(key: ArrayBuffer|ArrayBufferView): ArrayBuffer { + let otherPublicKey = new sjcl.bn(key.toString()); + let secret = otherPublicKey.powermod(this.privateKey, this.prime); + return sjcl.codec.arrayBuffer.fromBits(secret.toBits()); + } + + public generateKeys(): ArrayBuffer { + // Generate a new private key + this.privateKey = sjcl.bn.random(this.prime.sub(2),10).add(1); + + // Calculate the new public key + this.publicKey = this.generator.powermod(this.privateKey, this.prime); + + return this.getPublicKey(); + } + + public getVerifyError(): number { + // This method is not applicable to the Diffie-Hellman protocol + throw new Error("Method getVerifyError is not applicable to the Diffie-Hellman protocol"); + } +} + + +export function DiffieHellmanGroupHandle(name: string): DiffieHellmanHandle { + // Define some named groups with their prime and generator values + const groups: { [name: string]: { prime: number, generator: number } } = { + 'modp1': { prime: 2, generator: 2 }, + 'modp2': { prime: 3, generator: 2 }, + 'modp5': { prime: 5, generator: 2 }, + // Add more named groups here + }; + + // Get the named group + const group = groups[name]; + if (!group) { + throw new Error(`Unknown group name: ${name}`); + } + + // Create a DiffieHellmanHandle with the prime and generator of the named group + return new DiffieHellmanHandle(group.prime, group.generator); +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_dh.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_dh.ts new file mode 100644 index 000000000..1ce519c6d --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_dh.ts @@ -0,0 +1,202 @@ + +/* eslint-disable */ + +'use strict'; + +import { + Buffer, +} from './internal_buffer'; + +import * as cryptoImpl from './crypto'; +type ArrayLike = cryptoImpl.ArrayLike; + +import { + ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY, + ERR_INVALID_ARG_TYPE, +} from './internal_errors'; + +import { + validateInt32, +} from './validators'; + +import { + isArrayBufferView, + isAnyArrayBuffer +} from './internal_types'; + +import { + getArrayBufferOrView, + toBuf, + kHandle, +} from './crypto_util'; + +const DH_GENERATOR = 2; + +interface DiffieHellman { + [kHandle]: cryptoImpl.DiffieHellmanHandle; +} + +let DiffieHellman = function (this: DiffieHellman, sizeOrKey: number|ArrayLike, + keyEncoding?: number|string, generator?: number|ArrayLike, + genEncoding?: string): DiffieHellman { + if (!(this instanceof DiffieHellman)) + return new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding); + if (typeof sizeOrKey !== 'number' && + typeof sizeOrKey !== 'string' && + !isArrayBufferView(sizeOrKey) && + !isAnyArrayBuffer(sizeOrKey)) { + throw new ERR_INVALID_ARG_TYPE( + 'sizeOrKey', + ['number', 'string', 'ArrayBuffer', 'Buffer', 'TypedArray', 'DataView'], + sizeOrKey, + ); + } + + // Sizes < 0 don't make sense but they _are_ accepted (and subsequently + // rejected with ERR_OSSL_BN_BITS_TOO_SMALL) by OpenSSL. The glue code + // in node_crypto.cc accepts values that are IsInt32() for that reason + // and that's why we do that here too. + if (typeof sizeOrKey === 'number') + validateInt32(sizeOrKey, 'sizeOrKey'); + + if (keyEncoding && !Buffer.isEncoding(keyEncoding) && keyEncoding !== 'buffer') { + genEncoding = generator as any; + generator = keyEncoding; + keyEncoding = "utf-8"; // default encoding + } + + keyEncoding ??= 'utf-8'; + genEncoding ??= 'utf-8'; + + if (typeof sizeOrKey !== 'number') + sizeOrKey = toBuf(sizeOrKey, keyEncoding as string); + + if (!generator) { + generator = DH_GENERATOR; + } else if (typeof generator === 'number') { + validateInt32(generator, 'generator'); + } else if (typeof generator === 'string') { + generator = toBuf(generator, genEncoding); + } else if (!isArrayBufferView(generator) && !isAnyArrayBuffer(generator)) { + throw new ERR_INVALID_ARG_TYPE( + 'generator', + ['number', 'string', 'ArrayBuffer', 'Buffer', 'TypedArray', 'DataView'], + generator, + ); + } + + this[kHandle] = new cryptoImpl.DiffieHellmanHandle(sizeOrKey as any, generator as any); + Object.defineProperty(DiffieHellman.prototype, "verifyError", { + get: function () { + return this[kHandle].getVerifyError(); + }, + configurable: true, + enumerable: true, + }); + return this; +} as any as { new (sizeOrKey: number|ArrayLike, keyEncoding?: number|string, + generator?: number|ArrayLike, genEncoding?: string): DiffieHellman; }; + +interface DiffieHellmanGroup { + [kHandle]: cryptoImpl.DiffieHellmanHandle; +} + +let DiffieHellmanGroup = function(this: DiffieHellmanGroup, name: string): DiffieHellmanGroup { + if (!(this instanceof DiffieHellmanGroup)) + return new DiffieHellmanGroup(name); + + // The C++-based handle is shared between both classes, so DiffieHellmanGroupHandle() is merely + // a different constructor for a DiffieHellmanHandle. + this[kHandle] = cryptoImpl.DiffieHellmanGroupHandle(name); + Object.defineProperty(DiffieHellmanGroup.prototype, "verifyError", { + get: function () { + return this[kHandle].getVerifyError(); + }, + configurable: true, + enumerable: true, + }); + return this; +} as any as { new (name: string): DiffieHellmanGroup; }; + +DiffieHellmanGroup.prototype.generateKeys = DiffieHellman.prototype.generateKeys = + dhGenerateKeys; +DiffieHellmanGroup.prototype.computeSecret = DiffieHellman.prototype.computeSecret = + dhComputeSecret; +DiffieHellmanGroup.prototype.getPrime = DiffieHellman.prototype.getPrime = dhGetPrime; +DiffieHellmanGroup.prototype.getGenerator = DiffieHellman.prototype.getGenerator = + dhGetGenerator; +DiffieHellmanGroup.prototype.getPublicKey = DiffieHellman.prototype.getPublicKey = + dhGetPublicKey; +DiffieHellmanGroup.prototype.getPrivateKey = DiffieHellman.prototype.getPrivateKey = + dhGetPrivateKey; +DiffieHellman.prototype.setPublicKey = dhSetPublicKey; +DiffieHellman.prototype.setPrivateKey = dhSetPrivateKey; + +export {DiffieHellman, DiffieHellmanGroup}; + +type DHLike = DiffieHellman|DiffieHellmanGroup; +function dhGenerateKeys(this: DHLike, encoding?: string): Buffer|string { + const keys = this[kHandle].generateKeys(); + return encode(keys, encoding); +} + +function dhComputeSecret(this: DHLike, key: ArrayLike, inEnc?: string, + outEnc?: string): Buffer|string { + key = getArrayBufferOrView(key, 'key', inEnc); + const ret = this[kHandle].computeSecret(key); + if (typeof ret === 'string') + throw new ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY(); + return encode(ret, outEnc); +} + +function dhGetPrime(this: DHLike, encoding?: string): Buffer|string { + const prime = this[kHandle].getPrime(); + return encode(prime, encoding); +} + +function dhGetGenerator(this: DHLike, encoding?: string): Buffer|string { + const generator = this[kHandle].getGenerator(); + return encode(generator, encoding); +} + +function dhGetPublicKey(this: DHLike, encoding?: string): Buffer|string { + const key = this[kHandle].getPublicKey(); + return encode(key, encoding); +} + +function dhGetPrivateKey(this: DHLike, encoding?: string): Buffer|string { + const key = this[kHandle].getPrivateKey(); + return encode(key, encoding); +} + +function dhSetPublicKey(this: DiffieHellman, key: ArrayLike, encoding?: string): DiffieHellman { + key = getArrayBufferOrView(key, 'key', encoding); + this[kHandle].setPublicKey(key); + return this; +} + +function dhSetPrivateKey(this: DiffieHellman, key: ArrayLike, encoding?: string): DiffieHellman { + key = getArrayBufferOrView(key, 'key', encoding); + this[kHandle].setPrivateKey(key); + return this; +} + +function encode(buffer: ArrayBuffer, encoding?: string): Buffer|string { + if (encoding && encoding !== 'buffer') + return Buffer.from(buffer).toString(encoding); + return Buffer.from(buffer); +} + +export function createDiffieHellman(sizeOrKey: number|ArrayLike, + keyEncoding?: number|string, generator?: number|ArrayLike, + genEncoding?: string): DiffieHellman { + return new DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding); +} + +export function createDiffieHellmanGroup(name: string): DiffieHellmanGroup { + return new DiffieHellmanGroup(name); +} + +export function getDiffieHellman(name: string): DiffieHellmanGroup { + return createDiffieHellmanGroup(name); +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_hash.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_hash.ts new file mode 100644 index 000000000..160fd63a6 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_hash.ts @@ -0,0 +1,235 @@ +/* eslint-disable */ + +import * as cryptoImpl from './crypto'; +type ArrayLike = cryptoImpl.ArrayLike; + +import { + kFinalized, + kHandle, + kState, + getArrayBufferOrView, + getStringOption, +} from './crypto_util'; + +import { + Buffer +} from './internal_buffer'; + +import { + ERR_CRYPTO_HASH_FINALIZED, + ERR_CRYPTO_HASH_UPDATE_FAILED, + ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE, + ERR_INVALID_ARG_TYPE, +} from './internal_errors'; + +import { + validateEncoding, + validateString, + validateUint32, +} from './validators'; + +import { + normalizeEncoding +} from './internal_utils'; + +import { + isArrayBufferView, + isCryptoKey, + isAnyArrayBuffer, +} from './internal_types'; + +import { + Transform, + TransformOptions, + TransformCallback, +} from './streams_transform'; + +import { + KeyObject, +} from './crypto_keys'; + +export interface HashOptions extends TransformOptions { + outputLength?: number; +} + +interface _kState { + [kFinalized]: boolean; +} + +interface Hash extends Transform { + [kHandle]: cryptoImpl.HashHandle; + [kState]: _kState; +} + +// These helper functions are needed because the constructors can +// use new, in which case V8 cannot inline the recursive constructor call +export function createHash(algorithm: string, options?: HashOptions): Hash { + return new Hash(algorithm, options); +} + +let Hash = function(this: Hash, algorithm: string | cryptoImpl.HashHandle, + options?: HashOptions): Hash { + if (!(this instanceof Hash)) + return new Hash(algorithm, options); + + const xofLen = typeof options === 'object' ? options.outputLength : undefined; + if (xofLen !== undefined) + validateUint32(xofLen, 'options.outputLength'); + if (algorithm instanceof cryptoImpl.HashHandle) { + this[kHandle] = algorithm.copy(xofLen as number); + } else { + validateString(algorithm, 'algorithm'); + this[kHandle] = new cryptoImpl.HashHandle(algorithm, xofLen as number); + } + this[kState] = { + [kFinalized]: false, + }; + + Transform.call(this, options); + return this; +} as any as { new (algorithm: string | cryptoImpl.HashHandle, options?: HashOptions): Hash; }; + +Object.setPrototypeOf(Hash.prototype, Transform.prototype); +Object.setPrototypeOf(Hash, Transform); + +Hash.prototype.copy = function(this: Hash, options?: HashOptions): Hash { + const state = this[kState]; + if (state[kFinalized]) + throw new ERR_CRYPTO_HASH_FINALIZED(); + + return new Hash(this[kHandle], options); +} + +Hash.prototype._transform = function(this: Hash | Hmac, chunk: string | Buffer | ArrayBufferView, + encoding: string, callback: TransformCallback): void { + if (typeof chunk === 'string') { + encoding ??= 'utf-8'; + validateEncoding(chunk, encoding); + encoding = normalizeEncoding(encoding)!; + chunk = Buffer.from(chunk, encoding); + } + this[kHandle].update(chunk); + callback(); +} + +Hash.prototype._flush = function(this: Hash | Hmac, callback: TransformCallback): void { + this.push(Buffer.from(this[kHandle].digest())); + callback(); +} + +Hash.prototype.update = function(this: Hash | Hmac, data: string | Buffer | ArrayBufferView, + encoding?: string): Hash | Hmac { + encoding ??= 'utf8'; + if (encoding === 'buffer') { + encoding = undefined; + } + + const state = this[kState]; + if (state[kFinalized]) + throw new ERR_CRYPTO_HASH_FINALIZED(); + + if (typeof data === 'string') { + validateEncoding(data, encoding!); + encoding = normalizeEncoding(encoding); + data = Buffer.from(data, encoding); + } else if (!isArrayBufferView(data)) { + throw new ERR_INVALID_ARG_TYPE( + 'data', ['string', 'Buffer', 'TypedArray', 'DataView'], data); + } + + if (!this[kHandle].update(data)) + throw new ERR_CRYPTO_HASH_UPDATE_FAILED(); + return this; +} + +Hash.prototype.digest = function(this: Hash, outputEncoding?: string): Buffer | string { + const state = this[kState]; + if (state[kFinalized]) + throw new ERR_CRYPTO_HASH_FINALIZED(); + + // Explicit conversion for backward compatibility. + const ret = Buffer.from(this[kHandle].digest()); + state[kFinalized] = true; + if (outputEncoding !== undefined && outputEncoding !== 'buffer') { + return ret.toString(outputEncoding); + } else { + return ret; + } +} + +/////////////////////////// + +interface Hmac extends Transform { + [kHandle]: cryptoImpl.HmacHandle; + [kState]: _kState; +} + +export function createHmac(hmac: string, key: ArrayLike | KeyObject | CryptoKey, + options?: TransformOptions): Hmac { + return new Hmac(hmac, key, options); +} + +let Hmac = function(this: Hmac, hmac: string, key: ArrayLike | KeyObject | cryptoImpl.CryptoKey, + options?: TransformOptions): Hmac { + if (!(this instanceof Hmac)) { + return new Hmac(hmac, key, options); + } + validateString(hmac, 'hmac'); + const encoding = getStringOption(options, 'encoding'); + + if (key instanceof KeyObject) { + if (key.type !== 'secret') { + throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'secret'); + } + this[kHandle] = new cryptoImpl.HmacHandle(hmac, key[kHandle]); + } else if (isCryptoKey(key)) { + if ((key as cryptoImpl.CryptoKey).type !== 'secret') { + throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE((key as cryptoImpl.CryptoKey).type, 'secret'); + } + this[kHandle] = new cryptoImpl.HmacHandle(hmac, key); + } else if (typeof key !== 'string' && + !isArrayBufferView(key) && + !isAnyArrayBuffer(key)) { + throw new ERR_INVALID_ARG_TYPE( + 'key', + [ 'ArrayBuffer', 'Buffer', 'ArrayBufferView', 'string', 'KeyObject', 'CryptoKey'], + key); + } else { + this[kHandle] = new cryptoImpl.HmacHandle(hmac, getArrayBufferOrView(key as ArrayLike, + 'key', encoding)); + } + + this[kState] = { + [kFinalized]: false, + }; + Transform.call(this, options); + return this; +} as any as { new (hmac: string, key: ArrayLike | KeyObject | CryptoKey, + options?: TransformOptions): Hmac; }; + +Object.setPrototypeOf(Hmac.prototype, Transform.prototype); +Object.setPrototypeOf(Hmac, Transform); + +Hmac.prototype.update = Hash.prototype.update; + +Hmac.prototype.digest = function(this: Hmac, outputEncoding?: string): Buffer | string { + const state = this[kState]; + if (state[kFinalized]) { + const buf = Buffer.from(''); + return outputEncoding === 'buffer' ? buf : buf.toString(outputEncoding); + } + + // Explicit conversion for backward compatibility. + const ret = Buffer.from(this[kHandle].digest()); + state[kFinalized] = true; + if (outputEncoding !== undefined && outputEncoding !== 'buffer') { + return ret.toString(outputEncoding); + } else { + return ret; + } +}; + +Hmac.prototype._flush = Hash.prototype._flush; +Hmac.prototype._transform = Hash.prototype._transform; + +export {Hash, Hmac}; diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_hkdf.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_hkdf.ts new file mode 100644 index 000000000..3c48a2849 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_hkdf.ts @@ -0,0 +1,124 @@ +/* eslint-disable */ + +'use strict'; + +import * as cryptoImpl from './crypto'; + +import { + validateFunction, + validateInteger, + validateString, +} from './validators'; + +import { + KeyObject, +} from './crypto_keys'; + +type ArrayLike = cryptoImpl.ArrayLike; + +import { + kMaxLength, +} from './internal_buffer'; + +import { + toBuf, + validateByteSource, +} from './crypto_util'; + +import { + isAnyArrayBuffer, + isArrayBufferView, +} from './internal_types'; + +import { + NodeError, + ERR_INVALID_ARG_TYPE, + ERR_OUT_OF_RANGE, +} from './internal_errors'; + +function validateParameters(hash: string, key: ArrayLike | KeyObject, salt: ArrayLike, + info: ArrayLike, length: number) { + // TODO(soon): Add support for KeyObject input. + if (key instanceof KeyObject) { + throw new NodeError("ERR_METHOD_NOT_IMPLEMENTED", "KeyObject support for hkdf() and " + + "hkdfSync() is not yet implemented. Use ArrayBuffer, TypedArray, " + + "DataView, or Buffer instead."); + } + + validateString(hash, 'digest'); + key = prepareKey(key as unknown as ArrayLike); + salt = validateByteSource(salt, 'salt'); + info = validateByteSource(info, 'info'); + + validateInteger(length, 'length', 0, kMaxLength); + + if (info.byteLength > 1024) { + throw new ERR_OUT_OF_RANGE( + 'info', + 'must not contain more than 1024 bytes', + info.byteLength); + } + + return { + hash, + key, + salt, + info, + length, + }; +} + +function prepareKey(key: ArrayLike): ArrayLike { + key = toBuf(key); + + if (!isAnyArrayBuffer(key) && !isArrayBufferView(key)) { + throw new ERR_INVALID_ARG_TYPE( + 'ikm', + [ + 'string', + 'SecretKeyObject', + 'ArrayBuffer', + 'TypedArray', + 'DataView', + 'Buffer', + ], + key); + } + + return key; +} + +export function hkdf(hash: string, key: ArrayLike | KeyObject, salt: ArrayLike, info: ArrayLike, + length: number, + callback: (err: Error|null, derivedKey?: ArrayBuffer) => void): void { + ({ + hash, + key, + salt, + info, + length, + } = validateParameters(hash, key, salt, info, length)); + + validateFunction(callback, 'callback'); + + new Promise((res, rej) => { + try { + res(cryptoImpl.getHkdf(hash, key as ArrayLike, salt, info, length)); + } catch(err) { + rej(err); + } + }).then((val: ArrayBuffer) => callback(null, val), (err) => callback(err)); +} + +export function hkdfSync(hash: string, key: ArrayLike | KeyObject, salt: ArrayLike, + info: ArrayLike, length: number): ArrayBuffer { + ({ + hash, + key, + salt, + info, + length, + } = validateParameters(hash, key, salt, info, length)); + + return cryptoImpl.getHkdf(hash, key, salt, info, length); +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_keys.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_keys.ts new file mode 100644 index 000000000..aa549f57d --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_keys.ts @@ -0,0 +1,384 @@ +/* eslint-disable */ + +import { Buffer } from './internal_buffer'; + +import * as cryptoImpl from './crypto'; + +const +{ + CryptoKey, + KeyData, + KeyObjectType, + KeyExportResult, + SecretKeyType, + SecretKeyExportOptions, + PublicKeyExportOptions, + PrivateKeyExportOptions, + ExportOptions, + AsymmetricKeyDetails, + AsymmetricKeyType, + CreateAsymmetricKeyOptions, + GenerateKeyOptions, + GenerateKeyPairOptions, + InnerExportOptions, + // TODO(soon): Uncomment these once createPrivateKey/createPublicKey are implemented. + // JsonWebKey, + // InnerCreateAsymmetricKeyOptions, +} + = cryptoImpl; + + + +import { + arrayBufferToUnsignedBigInt, + kHandle, +} from './crypto_util'; + +import { + isAnyArrayBuffer, + isArrayBuffer, + isArrayBufferView, + isUint8Array, + // TODO(soon): Uncomment these once createPrivateKey/createPublicKey are implemented. + // isSharedArrayBuffer, +} from './internal_types'; + +import { + ERR_INVALID_ARG_TYPE, + ERR_METHOD_NOT_IMPLEMENTED, + // TODO(soon): Uncomment these once createPrivateKey/createPublicKey are implemented. + // ERR_INVALID_ARG_VALUE, +} from './internal_errors'; + +import { + validateObject, + validateString, +} from './validators'; + +export abstract class KeyObject { + [kHandle]: CryptoKey; + + constructor() { + // KeyObjects cannot be created with new ... use one of the + // create or generate methods, or use from to get from a + // CryptoKey. + throw new Error('Illegal constructor'); + } + + static from(key: CryptoKey) : KeyObject { + if (!(key instanceof CryptoKey)) { + throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key); + } + switch (key.type) { + case 'secret': + return Reflect.construct(function(this: SecretKeyObject) { + this[kHandle] = key; + }, [], SecretKeyObject); + case 'private': + return Reflect.construct(function(this: PrivateKeyObject) { + this[kHandle] = key; + }, [], PrivateKeyObject); + case 'public': + return Reflect.construct(function(this: PublicKeyObject) { + this[kHandle] = key; + }, [], PublicKeyObject); + } + } + + export(options: ExportOptions = {}) : KeyExportResult { + validateObject(options, 'options', {}); + + // Yes, converting to any is a bit of a cheat, but it allows us to check + // each option individually without having to do a bunch of type guards. + const opts = options as any; + if (opts.format !== undefined) validateString(opts.format, 'options.format'); + if (opts.type !== undefined) validateString(opts.type, 'options.type'); + if (this.type === 'private') { + if (opts.cipher !== undefined) { + validateString(opts.cipher, 'options.cipher'); + if (typeof opts.passphrase === 'string') { + opts.passphrase = Buffer.from(opts.passphrase, opts.encoding); + } + if (!isUint8Array(opts.passphrase)) { + throw new ERR_INVALID_ARG_TYPE('options.passphrase', [ + 'string', 'Uint8Array' + ], opts.passphrase); + } + } + } + + const ret = cryptoImpl.exportKey(this[kHandle], options as InnerExportOptions); + if (typeof ret === 'string') return ret; + if (isUint8Array(ret)) { + return Buffer.from((ret as Uint8Array).buffer, ret.byteOffset, ret.byteLength) as KeyExportResult; + } else if (isArrayBuffer(ret)) { + return Buffer.from(ret as ArrayBuffer, 0, (ret as ArrayBuffer).byteLength); + } + return ret; + } + + equals(otherKeyObject: KeyObject) : boolean { + if (this === otherKeyObject || + this[kHandle] === otherKeyObject[kHandle]) return true; + if (this.type !== otherKeyObject.type) return false; + if (!(otherKeyObject[kHandle] instanceof CryptoKey)) { + throw new ERR_INVALID_ARG_TYPE('otherKeyObject', 'KeyObject', otherKeyObject); + } + return cryptoImpl.equals(this[kHandle], otherKeyObject[kHandle]); + } + + abstract get type() : KeyObjectType; +} + +abstract class AsymmetricKeyObject extends KeyObject { + get asymmetricKeyDetails() : AsymmetricKeyDetails { + let detail = cryptoImpl.getAsymmetricKeyDetail(this[kHandle]); + if (isArrayBuffer(detail.publicExponent)) { + detail.publicExponent = arrayBufferToUnsignedBigInt(detail.publicExponent as any); + } + return detail; + } + + get asymmetricKeyType() : AsymmetricKeyType { + return cryptoImpl.getAsymmetricKeyType(this[kHandle]); + } +} + +export class PublicKeyObject extends AsymmetricKeyObject { + override export(options?: PublicKeyExportOptions) : KeyExportResult { + return super.export(options); + } + + get type() : KeyObjectType { return 'public'; } +} + +export class PrivateKeyObject extends AsymmetricKeyObject { + override export(options?: PrivateKeyExportOptions) : KeyExportResult { + return super.export(options); + } + + get type() : KeyObjectType { return 'private'; } +} + +export class SecretKeyObject extends KeyObject { + get symmetricKeySize() : number { + return (this[kHandle].algorithm as any).length | 0 + } + + override export(options?: SecretKeyExportOptions) : KeyExportResult { + return super.export(options); + } + + get type() : KeyObjectType { return 'secret'; } +} + +type ValidateKeyDataOptions = { + allowObject?: boolean; +}; +function validateKeyData(key: unknown, name: string, options : ValidateKeyDataOptions = { + allowObject: false, +}) { + if (key == null || + (typeof key !== 'string' && + (options.allowObject && typeof key !== 'object') && + !isArrayBufferView(key) && + !isAnyArrayBuffer(key))) { + const expected = [ + 'string', + 'ArrayBuffer', + 'TypedArray', + 'DataView' + ]; + if (options.allowObject) expected.push('object'); + throw new ERR_INVALID_ARG_TYPE(name, expected, key); + } +} + +export function createSecretKey(key: string, encoding?: string) : SecretKeyObject; +export function createSecretKey(key: ArrayBuffer | ArrayBufferView) : SecretKeyObject; +export function createSecretKey(key: KeyData, encoding?: string) : SecretKeyObject { + validateKeyData(key, 'key'); + if (typeof key === 'string') key = Buffer.from(key as string, encoding); + return KeyObject.from(cryptoImpl.createSecretKey(key)) as SecretKeyObject; +} + +// TODO(soon): Fully implement createPrivateKey/createPublicKey. These are the +// equivalent of the WebCrypto API's importKey() method but operate synchronously +// and support a range of options not currently supported by WebCrypto. Implementing +// these will require either duplicating or significantly refactoring the current +// import key logic that supports Web Crypto now as the import logic is spread out +// over several locations and makes a number of assumptions that Web Crypto is being +// used. +// +// For now, users can use Web Crypto to import a CryptoKey then convert that into +// a KeyObject using KeyObject.from(). +// +// const kPrivateKey = Symbol('privateKey'); +// const kPublicKey = Symbol('publicKey'); + +// function validateAsymmetricKeyOptions( +// key: CreateAsymmetricKeyOptions | KeyData | CryptoKey | KeyObject, +// type: Symbol) { +// validateKeyData(key, 'key', { allowObject: true }); +// let inner : InnerCreateAsymmetricKeyOptions = {}; +// inner.format = 'pem'; +// if (typeof key === 'string') { +// inner.key = Buffer.from(key as string); +// } else if (isArrayBufferView(key)) { +// inner.key = key as ArrayBufferView; +// } else if (isArrayBuffer(key)) { +// inner.key = key as ArrayBuffer; +// } else if (isSharedArrayBuffer(key)) { +// inner.key = key as SharedArrayBuffer; +// } else if (type === kPublicKey && key instanceof KeyObject) { +// // Covers deriving public key from a private key. +// if (key.type !== 'private') { +// throw new ERR_INVALID_ARG_VALUE('key', key, 'must be a private key'); +// } +// inner.key = (key as KeyObject)[kHandle]; +// } else if (type === kPublicKey && key instanceof CryptoKey) { +// // Covers deriving public key from a private key. +// if ((key as CryptoKey).type !== 'private') { +// throw new ERR_INVALID_ARG_VALUE('key', key, 'must be a private key'); +// } +// inner.key = key as CryptoKey; +// } else { +// const options = key as CreateAsymmetricKeyOptions; +// if (typeof options.key === 'string') { +// inner.key = Buffer.from(options.key as string, options.encoding); +// } else if (isArrayBufferView(options.key)) { +// inner.key = options.key as ArrayBufferView; +// } else if (isArrayBuffer(options.key)) { +// inner.key = options.key as ArrayBuffer; +// } else if (isSharedArrayBuffer(options.key)) { +// inner.key = options.key as SharedArrayBuffer; +// } else if (type === kPublicKey && key instanceof KeyObject) { +// if ((options.key as KeyObject).type !== 'private') { +// throw new ERR_INVALID_ARG_VALUE('options.key', options.key, 'must be a private key'); +// } +// inner.key = (options.key as KeyObject)[kHandle]; +// } else if (type === kPublicKey && key instanceof CryptoKey) { +// if ((options.key as CryptoKey).type !== 'private') { +// throw new ERR_INVALID_ARG_VALUE('options.key', options.key, 'must be a private key'); +// } +// inner.key = options.key as CryptoKey; +// } else { +// inner.key = key as JsonWebKey; +// } +// validateKeyData(inner.key, 'options.key', { allowObject: true }); + +// if (options.format !== undefined) { +// validateString(options.format, 'options.format'); +// inner.format = options.format; +// } +// if (options.type !== undefined) { +// validateString(options.type, 'options.type'); +// inner.type = options.type; +// } +// if (options.passphrase !== undefined) { +// if (typeof options.passphrase === 'string') { +// inner.passphrase = Buffer.from(options.passphrase, options.encoding); +// } else { +// if (!isUint8Array(options.passphrase)) { +// throw new ERR_INVALID_ARG_TYPE('options.passphrase', [ +// 'string', 'Uint8Array' +// ], options.passphrase); +// } +// inner.passphrase = options.passphrase; +// } +// if (inner.passphrase.byteLength > 1024) { +// throw new ERR_INVALID_ARG_VALUE('options.passphrase', options.passphrase.length, '<= 1024'); +// } +// } +// } +// return inner; +// } + +export function createPrivateKey(key: string) : PrivateKeyObject; +export function createPrivateKey(key: ArrayBuffer | ArrayBufferView) : PrivateKeyObject; +export function createPrivateKey(key: CreateAsymmetricKeyOptions) : PrivateKeyObject; +export function createPrivateKey(_key: CreateAsymmetricKeyOptions | KeyData) : PrivateKeyObject { + // The options here are fairly complex. The key data can be a string, + // ArrayBuffer, or ArrayBufferView. The first argument can be one of + // these or an object with a key property that is one of these. If the + // key data is a string, then it will be decoded using an encoding + // (defaults to UTF8). + throw new ERR_METHOD_NOT_IMPLEMENTED('crypto.createPrivateKey'); + // return KeyObject.from(cryptoImpl.createPrivateKey( + // validateAsymmetricKeyOptions(key, kPrivateKey))) as PrivateKeyObject; +} + +export function createPublicKey(key: string) : PublicKeyObject; +export function createPublicKey(key: ArrayBuffer) : PublicKeyObject; +export function createPublicKey(key: ArrayBufferView) : PublicKeyObject; + +export function createPublicKey(key: KeyObject) : PublicKeyObject; +export function createPublicKey(key: CryptoKey) : PublicKeyObject; +export function createPublicKey(key: CreateAsymmetricKeyOptions) : PublicKeyObject; +export function createPublicKey(_key: CreateAsymmetricKeyOptions | KeyData | CryptoKey | KeyObject) + : PublicKeyObject { + // The options here are a bit complicated. The key material itself can + // either be a string, ArrayBuffer, or ArrayBufferView. It is also + // possible to pass a private key in the form of either a CryptoKey + // or KeyObject. The first argument can be one of these, or an object + // whose key value is one of these. If the key data is a string, then + // it will be decoded using an encoding (defaults to UTF8). If a + // CryptoKey or KeyObject is passed, it will be used to derived the + // public key. + throw new ERR_METHOD_NOT_IMPLEMENTED('crypto.createPublicKey'); + // return KeyObject.from(cryptoImpl.createPublicKey( + // validateAsymmetricKeyOptions(key, kPublicKey))) as PublicKeyObject; +} + +// ====================================================================================== + +export type PublicKeyResult = KeyExportResult | PublicKeyObject; +export type PrivateKeyResult = KeyExportResult | PrivateKeyObject; +export type GenerateKeyCallback = (err?: any, key?: KeyObject) => void; +export type GenerateKeyPairCallback = + (err?: any, publicKey?: PublicKeyResult, privateKey?: PrivateKeyResult) => void; + +export interface KeyObjectPair { + publicKey: PublicKeyResult; + privateKey: PrivateKeyResult; +} + +export function generateKey(_type: SecretKeyType, + _options: GenerateKeyOptions, + callback: GenerateKeyCallback) { +// We intentionally have not implemented key generation up to this point. +// The reason is that generation of cryptographically safe keys is a CPU +// intensive operation that can often exceed limits on the amount of CPU +// time a worker is allowed. +callback(new ERR_METHOD_NOT_IMPLEMENTED('crypto.generateKeySync')); +} + +export function generateKeySync(_type: SecretKeyType, + _options: GenerateKeyOptions) { +// We intentionally have not implemented key generation up to this point. +// The reason is that generation of cryptographically safe keys is a CPU +// intensive operation that can often exceed limits on the amount of CPU +// time a worker is allowed. +throw new ERR_METHOD_NOT_IMPLEMENTED('crypto.generateKeySync'); +} + +export function generateKeyPair( + _type : AsymmetricKeyType, + _options: GenerateKeyPairOptions, + callback: GenerateKeyPairCallback) { + // We intentionally have not implemented key generation up to this point. + // The reason is that generation of cryptographically safe keys is a CPU + // intensive operation that can often exceed limits on the amount of CPU + // time a worker is allowed. + callback(new ERR_METHOD_NOT_IMPLEMENTED('crypto.generateKeyPair')); +} + +export function generateKeyPairSync( + _type : AsymmetricKeyType, + _options: GenerateKeyPairOptions) : KeyObjectPair { + // We intentionally have not implemented key generation up to this point. + // The reason is that generation of cryptographically safe keys is a CPU + // intensive operation that can often exceed limits on the amount of CPU + // time a worker is allowed. + throw new ERR_METHOD_NOT_IMPLEMENTED('crypto.generateKeyPairSync'); +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_pbkdf2.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_pbkdf2.ts new file mode 100644 index 000000000..40772a90c --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_pbkdf2.ts @@ -0,0 +1,64 @@ +/* eslint-disable */ + +'use strict'; + +import * as cryptoImpl from './crypto'; +type ArrayLike = cryptoImpl.ArrayLike; +export {ArrayLike}; + +import { + Buffer, +} from './internal_buffer'; + +import { + validateInt32, + validateFunction, + validateString, +} from './validators'; + +import { + getArrayBufferOrView, +} from './crypto_util'; + +export function pbkdf2Sync(password: ArrayLike, salt: ArrayLike, iterations: number, + keylen: number, digest: string): Buffer { + ({ password, salt, iterations, keylen, digest } = + check(password, salt, iterations, keylen, digest)); + + const result = cryptoImpl.getPbkdf(password, salt, iterations, keylen, digest); + return Buffer.from(result); +} + +export type Pbkdf2Callback = (err?: Error|null, result?: Buffer) => void; +export function pbkdf2(password: ArrayLike, salt: ArrayLike, iterations: number, keylen: number, + digest: string, callback: Pbkdf2Callback): void { + if (typeof digest === 'function') { + // Appease node test cases + validateString(undefined, 'digest'); + } + validateFunction(callback, 'callback'); + ({ password, salt, iterations, keylen, digest } = + check(password, salt, iterations, keylen, digest)); + + new Promise((res, rej) => { + try { + res(cryptoImpl.getPbkdf(password, salt, iterations, keylen, digest)); + } catch(err) { + rej(err); + } + }).then((val) => callback(null, Buffer.from(val)), (err) => callback(err)); +} + +function check(password: ArrayLike|ArrayBufferView, salt: ArrayLike|ArrayBufferView, iterations: number, keylen: number, + digest: string): any { + validateString(digest, 'digest'); + + password = getArrayBufferOrView(password, 'password'); + salt = getArrayBufferOrView(salt, 'salt'); + // OpenSSL uses a signed int to represent these values, so we are restricted + // to the 31-bit range here (which is plenty). + validateInt32(iterations, 'iterations', 1); + validateInt32(keylen, 'keylen', 0); + + return { password, salt, iterations, keylen, digest }; +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_random.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_random.ts new file mode 100644 index 000000000..64c0a58a1 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_random.ts @@ -0,0 +1,410 @@ +/* eslint-disable */ + +import * as cryptoImpl from './crypto'; + +import { + validateObject, + validateBoolean, + validateFunction, + validateInt32, + validateInteger, +} from './validators'; + +import { + isAnyArrayBuffer, + isArrayBufferView +} from './internal_types'; + +import { + ERR_INVALID_ARG_TYPE, + ERR_OUT_OF_RANGE, +} from './internal_errors'; + +import { + Buffer, + kMaxLength +} from './internal_buffer'; + +import { + arrayBufferToUnsignedBigInt, +} from './crypto_util'; + +export type RandomBytesCallback = (err: any|null, buffer: Uint8Array) => void; +export function randomBytes(size: number, callback: RandomBytesCallback): void; +export function randomBytes(size: number): Uint8Array; +export function randomBytes(size: number, callback?: RandomBytesCallback) : Uint8Array|void{ + validateInteger(size, 'size', 0, kMaxLength); + const buf = Buffer.alloc(size); + if (callback !== undefined) { + randomFill(buf, callback as RandomFillCallback); + } else { + randomFillSync(buf); + return buf; + } +} + +export function randomFillSync( + buffer: ArrayBufferView|ArrayBuffer, + offset?: number, + size?: number) { + if (!isAnyArrayBuffer(buffer) && !isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE('buffer', [ + 'TypedArray', + 'DataView', + 'ArrayBuffer', + 'SharedArrayBuffer' + ],buffer); + } + const maxLength = (buffer as Uint8Array).length; + if (offset !== undefined) { + validateInteger(offset!, 'offset', 0, kMaxLength); + } else offset = 0; + if (size !== undefined) { + validateInteger(size!, 'size', 0, maxLength - offset); + } else size = maxLength; + if (isAnyArrayBuffer(buffer)) { + buffer = Buffer.from(buffer as ArrayBuffer); + } + buffer = (buffer as Buffer).subarray(offset, offset + size); + return crypto.getRandomValues(buffer as ArrayBufferView); +} + +export type RandomFillCallback = (err: any|null, buf?: ArrayBufferView|ArrayBuffer) => void; +export function randomFill(buffer: ArrayBufferView|ArrayBuffer, + callback?: RandomFillCallback) : void; +export function randomFill(buffer: ArrayBufferView|ArrayBuffer, + offset: number, + callback?: RandomFillCallback) : void; + export function randomFill(buffer: ArrayBufferView|ArrayBuffer, + offset: number, + size: number, + callback?: RandomFillCallback) : void; +export function randomFill(buffer: ArrayBufferView|ArrayBuffer, + offsetOrCallback?: number|RandomFillCallback, + sizeOrCallback?: number|RandomFillCallback, + callback?: RandomFillCallback) { + if (!isAnyArrayBuffer(buffer) && !isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE('buffer', [ + 'TypedArray', + 'DataView', + 'ArrayBuffer', + 'SharedArrayBuffer' + ],buffer); + } + + let offset = 0; + let size = 0; + const maxLength = (buffer as Uint8Array).length; + if (typeof callback === 'function') { + validateInteger(offsetOrCallback, 'offset', 0, maxLength); + offset = offsetOrCallback as number; + + validateInteger(sizeOrCallback, 'size', 0, maxLength - offset); + size = sizeOrCallback as number; + } else if (typeof sizeOrCallback === 'function') { + validateInteger(offsetOrCallback, 'offset', 0, maxLength); + offset = offsetOrCallback as number; + size = maxLength - offset; + callback = sizeOrCallback as RandomFillCallback; + } else if (typeof offsetOrCallback === 'function') { + offset = 0; + size = maxLength; + callback = offsetOrCallback as RandomFillCallback; + } + validateFunction(callback, 'callback'); + + // We're currently not actually implementing the fill itself asynchronously, + // so we defer to randomFillSync here, but we invoke the callback asynchronously. + new Promise((res) => { + randomFillSync(buffer, offset, size); + res(); + }).then(() => callback!(null, buffer), (err: any) => callback!(err)); +} + +const RAND_MAX = 0xFFFF_FFFF_FFFF; +// Cache random data to use in randomInt. The cache size must be evenly +// divisible by 6 because each attempt to obtain a random int uses 6 bytes. +const randomCache = Buffer.alloc(6 * 1024); +let randomCacheOffset = 0; +let initialized = false; + +function getRandomInt(min: number, max: number) { + if (!initialized) { + randomFillSync(randomCache); + initialized = true; + } + // First we generate a random int between [0..range) + const range = max - min; + + if (!(range <= RAND_MAX)) { + throw new ERR_OUT_OF_RANGE(`max${max ? '' : ' - min'}`, `<= ${RAND_MAX}`, range); + } + + // For (x % range) to produce an unbiased value greater than or equal to 0 and + // less than range, x must be drawn randomly from the set of integers greater + // than or equal to 0 and less than randLimit. + const randLimit = RAND_MAX - (RAND_MAX % range); + + // If we don't have a callback, or if there is still data in the cache, we can + // do this synchronously, which is super fast. + while (randomCacheOffset <= randomCache.length) { + if (randomCacheOffset === randomCache.length) { + // This might block the thread for a bit, but we are in sync mode. + randomFillSync(randomCache); + randomCacheOffset = 0; + } + + const x = randomCache.readUIntBE(randomCacheOffset, 6); + randomCacheOffset += 6; + if (x < randLimit) { + const n = (x % range) + min; + return n; + } + } + return 0; // Should be unreachable. +} + +export type RandomIntCallback = (err: any|null, n?: number) => void; +export function randomInt(max: number) : number; +export function randomInt(min: number, max: number) : number; +export function randomInt(max: number, callback: RandomIntCallback) : void; +export function randomInt(min: number, max: number, callback: RandomIntCallback) : void; +export function randomInt(minOrMax: number, + maxOrCallback?: number|RandomIntCallback, + callback?: RandomIntCallback) { + let min = 0; + let max = 0; + if (typeof callback === 'function') { + validateInteger(minOrMax, 'min'); + validateInteger(maxOrCallback, 'max'); + min = minOrMax as number; + max = maxOrCallback as number; + } else if (typeof maxOrCallback === 'function') { + min = 0; + validateInteger(minOrMax, 'max'); + max = minOrMax as number; + callback = maxOrCallback as RandomIntCallback; + } else if (arguments.length === 2) { + validateInteger(minOrMax, 'min'); + validateInteger(maxOrCallback, 'max'); + min = minOrMax as number; + max = maxOrCallback as number; + } else { + min = 0; + validateInteger(minOrMax, 'max'); + max = minOrMax; + } + + if (min > max) { + throw new ERR_OUT_OF_RANGE('min', 'min <= max', min); + } + + if (callback !== undefined) { + new Promise((res) => { + res(getRandomInt(min, max)); + }).then((n: number) => callback!(null, n), (err: any) => callback!(err)); + return; + } else { + return getRandomInt(min, max); + } +} + +export function randomUUID(options?: any) { + // While we do not actually use the entropy cache, we go ahead and validate + // the input parameters as Node.js does. + if (options !== undefined) { + validateObject(options, 'options', options); + if (options.disableEntropyCache !== undefined) { + validateBoolean(options.disableEntropyCache, 'options.disableEntropyCache'); + } + } + return crypto.randomUUID(); +} + +export type PrimeNum = ArrayBuffer | ArrayBufferView | Buffer | bigint; +export interface GeneratePrimeOptions { + add?: PrimeNum; + rem?: PrimeNum; + safe?: boolean; + bigint?: boolean; +} + +export interface CheckPrimeOptions { + checks?: number; +} + +export type GeneratePrimeCallback = (err?: any, prime?: bigint|ArrayBuffer) => void; +export type CheckPrimeCallback = (err?: any, prime?: boolean) => void; + +function processGeneratePrimeOptions(options: GeneratePrimeOptions) : { + add: ArrayBufferView, + rem: ArrayBufferView, + safe: boolean, + bigint: boolean } { + validateObject(options, 'options', {}); + const { + safe = false, + bigint = false, + } = options; + let { + add, + rem, + } = options; + validateBoolean(safe, 'options.safe'); + validateBoolean(bigint, 'options.bigint'); + + if (add !== undefined) { + if (typeof add === 'bigint') { + add = unsignedBigIntToBuffer(add, 'options.add'); + } else if (!isAnyArrayBuffer(add) && !isArrayBufferView(add)) { + throw new ERR_INVALID_ARG_TYPE( + 'options.add', + [ + 'ArrayBuffer', + 'TypedArray', + 'Buffer', + 'DataView', + 'bigint', + ], + add); + } + } + + if (rem !== undefined) { + if (typeof rem === 'bigint') { + rem = unsignedBigIntToBuffer(rem, 'options.rem'); + } else if (!isAnyArrayBuffer(rem) && !isArrayBufferView(rem)) { + throw new ERR_INVALID_ARG_TYPE( + 'options.rem', + [ + 'ArrayBuffer', + 'TypedArray', + 'Buffer', + 'DataView', + 'bigint', + ], + rem); + } + } + + return { + safe, + bigint, + add: add as ArrayBufferView, + rem: rem as ArrayBufferView, + } +} + +export function generatePrimeSync(size: number, options: GeneratePrimeOptions = {}) { + validateInt32(size, 'size', 1); + const { + safe, + bigint, + add, + rem, + } = processGeneratePrimeOptions(options); + + let primeBuf = cryptoImpl.randomPrime(size, safe, add, rem); + return bigint ? arrayBufferToUnsignedBigInt(primeBuf) : primeBuf; +} + +export function generatePrime(size: number, + options: GeneratePrimeOptions, + callback: GeneratePrimeCallback) : void; +export function generatePrime(size: number, callback: GeneratePrimeCallback) : void; +export function generatePrime(size: number, + options: GeneratePrimeOptions|GeneratePrimeCallback, + callback?: GeneratePrimeCallback) : void { + validateInt32(size, 'size', 1); + if (typeof options === 'function') { + callback = options; + options = {}; + } + validateFunction(callback, 'callback'); + + const { + safe, + bigint, + add, + rem + } = processGeneratePrimeOptions(options as GeneratePrimeOptions); + + new Promise((res, rej) => { + try { + const primeBuf = cryptoImpl.randomPrime(size, safe, add, rem); + res(bigint ? arrayBufferToUnsignedBigInt(primeBuf) : primeBuf); + } catch(err) { + rej(err); + } + }).then((val) => callback!(null, val), (err) => callback!(err)); +} + +function unsignedBigIntToBuffer(bigint: bigint, name: string) { + if (bigint < 0) { + throw new ERR_OUT_OF_RANGE(name, '>= 0', bigint); + } + + const hex = bigint.toString(16); + const padded = hex.padStart(hex.length + (hex.length % 2), '0'); + return Buffer.from(padded, 'hex'); +} + +function validateCandidate(candidate: PrimeNum) : Buffer { + if (typeof candidate === 'bigint') + candidate = unsignedBigIntToBuffer(candidate, 'candidate'); + if (!isAnyArrayBuffer(candidate) && !isArrayBufferView(candidate)) { + throw new ERR_INVALID_ARG_TYPE( + 'candidate', + [ + 'ArrayBuffer', + 'TypedArray', + 'Buffer', + 'DataView', + 'bigint', + ], + candidate, + ); + } + return candidate as Buffer; +} + +function validateChecks(options : CheckPrimeOptions) : number { + const { + checks = 0, + } = options; + // The checks option is unsigned but must fit into a signed 32-bit integer for OpenSSL. + validateInt32(checks, 'options.checks', 0); + return checks; +} + +export function checkPrimeSync(candidate: PrimeNum, options: CheckPrimeOptions = {}) { + candidate = validateCandidate(candidate); + validateObject(options, 'options', {}); + const checks = validateChecks(options); + return cryptoImpl.checkPrimeSync(candidate as ArrayBufferView, checks); +} + +export function checkPrime(candidate: PrimeNum, + options: CheckPrimeOptions, + callback: CheckPrimeCallback) : void; +export function checkPrime(candidate: PrimeNum, + callback: CheckPrimeCallback) : void; +export function checkPrime(candidate: PrimeNum, + options: CheckPrimeOptions|CheckPrimeCallback, + callback?: CheckPrimeCallback) : void { + candidate = validateCandidate(candidate); + if (typeof options === 'function') { + callback = options; + options = {}; + } + validateObject(options, 'options', {}); + validateFunction(callback, 'callback'); + const checks = validateChecks(options); + new Promise((res, rej) => { + try { + res(cryptoImpl.checkPrimeSync(candidate as ArrayBufferView, checks)); + } catch(err) { + rej(err); + } + }).then((val) => callback!(null, val), (err) => callback!(err)); +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_util.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_util.ts new file mode 100644 index 000000000..5f52cdedc --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/crypto_util.ts @@ -0,0 +1,118 @@ +/* eslint-disable */ + +'use strict'; + +import { + Buffer, +} from './internal_buffer'; + +import { + isAnyArrayBuffer, + isArrayBufferView, +} from './internal_types'; + +import { + ERR_INVALID_ARG_TYPE, +} from './internal_errors'; + +import { + validateString, +} from './validators'; + +import * as cryptoImpl from './crypto'; +type ArrayLike = cryptoImpl.ArrayLike; + +export const kHandle = Symbol('kHandle'); +export const kFinalized = Symbol('kFinalized'); +export const kState = Symbol('kFinalized'); + +export function getStringOption(options: any, key: string) { + let value; + if (options && (value = options[key]) != null) + validateString(value, `options.${key}`); + return value; +} + +export function getArrayBufferOrView(buffer: Buffer | ArrayBuffer | ArrayBufferView | string, name: string, encoding?: string): Buffer | ArrayBuffer | ArrayBufferView { + if (isAnyArrayBuffer(buffer)) + return buffer as ArrayBuffer; + if (typeof buffer === 'string') { + if (encoding === undefined || encoding === 'buffer') { + encoding = 'utf8'; + } + return Buffer.from(buffer, encoding); + } + if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE( + name, + [ + 'string', + 'ArrayBuffer', + 'Buffer', + 'TypedArray', + 'DataView', + ], + buffer, + ); + } + return buffer; +} + +/** + * 48 is the ASCII code for '0', 97 is the ASCII code for 'a'. + * @param {number} number An integer between 0 and 15. + * @returns {number} corresponding to the ASCII code of the hex representation + * of the parameter. + */ +export const numberToHexCharCode = (number: number): number => (number < 10 ? 48 : 87) + number; + +/** + * @param {ArrayBuffer} buf An ArrayBuffer. + * @return {bigint} + */ +export function arrayBufferToUnsignedBigInt(buf: ArrayBuffer): bigint { + const length = buf.byteLength; + const chars = Array(length * 2); + const view = new DataView(buf); + + for (let i = 0; i < length; i++) { + const val = view.getUint8(i); + chars[2 * i] = numberToHexCharCode(val >> 4); + chars[2 * i + 1] = numberToHexCharCode(val & 0xf); + } + + return BigInt(`0x${String.fromCharCode.apply(null, chars)}`); +} + +// This is here because many functions accepted binary strings without +// any explicit encoding in older versions of node, and we don't want +// to break them unnecessarily. +export function toBuf(val: ArrayLike, encoding?: string): Buffer|ArrayBuffer|ArrayBufferView { + if (typeof val === 'string') { + if (encoding === 'buffer') { + encoding = 'utf8'; + } + return Buffer.from(val, encoding); + } + return val; +} + +export function validateByteSource(val: ArrayLike, + name: string): Buffer|ArrayBuffer|ArrayBufferView { + val = toBuf(val); + + if (isAnyArrayBuffer(val) || isArrayBufferView(val)) { + return val; + } + + throw new ERR_INVALID_ARG_TYPE( + name, + [ + 'string', + 'ArrayBuffer', + 'TypedArray', + 'DataView', + 'Buffer', + ], + val); +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/events.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/events.ts new file mode 100644 index 000000000..f7b92a65d --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/events.ts @@ -0,0 +1,904 @@ +import { + AbortError, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_THIS, + ERR_OUT_OF_RANGE, + ERR_UNHANDLED_ERROR, +} from "./internal_errors"; + +import { + validateAbortSignal, + validateBoolean, + validateFunction, +} from "./validators"; + + +import * as process from "./process"; + +import { spliceOne } from "./internal_utils"; + +import * as async_hooks from "./async_hooks"; +const { AsyncResource } = async_hooks; + +import { inspect } from "./internal_inspect"; + +const kRejection = Symbol.for("nodejs.rejection"); +const kCapture = Symbol("kCapture"); +const kErrorMonitor = Symbol("events.errorMonitor"); +const kMaxEventTargetListeners = Symbol("events.maxEventTargetListeners"); +const kMaxEventTargetListenersWarned = Symbol("events.maxEventTargetListenersWarned"); + + +export interface EventEmitterOptions { + captureRejections? : boolean; +}; + +export type EventName = string|symbol; +export type EventCallback = (...args: any[]) => unknown; +export interface EventEmitter { + addListener(eventName: EventName, listener: EventCallback): EventEmitter; + emit(eventName: EventName, ...args: unknown[]): void; + eventNames() : EventName[]; + getMaxListeners(): number; + listenerCount(eventName: EventName): number; + listeners(eventName: EventName): EventCallback[]; + off(eventName: EventName, listener: EventCallback): EventEmitter; + on(eventName: EventName, listener: EventCallback): EventEmitter; + once(eventName: EventName, listener: EventCallback): EventEmitter; + prependListener(eventName: EventName, listener: EventCallback): EventEmitter; + prependOnceListener(eventName: EventName, listener: EventCallback): EventEmitter; + removeAllListeners(eventName?: EventName): EventEmitter; + removeListener(eventName: EventName, listener: EventCallback): EventEmitter; + setMaxListeners(n: number): EventEmitter; + rawListeners(eventName: EventName): EventCallback[]; + [kRejection](err: unknown, eventName: EventName, ...args: unknown[]) : void; +}; + +type AsyncResource = typeof AsyncResource; + +declare var EventTarget : Function; + +export function EventEmitter(this : EventEmitter, opts? : EventEmitterOptions) { + EventEmitter.init.call(this, opts); +} + +class EventEmitterReferencingAsyncResource extends AsyncResource { + #eventEmitter : EventEmitter; + constructor(emitter : EventEmitter) { + super(''); + this.#eventEmitter = emitter; + } + + get eventEmitter() { + if (this.#eventEmitter === undefined) + throw new ERR_INVALID_THIS('EventEmitterReferencingAsyncResource'); + return this.#eventEmitter; + } +} + +// @ts-ignore -- TODO(soon) Properly handle the extends EventEmitter here +export class EventEmitterAsyncResource extends EventEmitter { + #asyncResource : EventEmitterReferencingAsyncResource; + + constructor(options? : EventEmitterOptions) { + super(options); + // @ts-ignore + this.#asyncResource = new EventEmitterReferencingAsyncResource(this); + } + + get asyncResource() : AsyncResource { + if (this.#asyncResource === undefined) + throw new ERR_INVALID_THIS('EventEmitterAsyncResource'); + // @ts-ignore + return this.#asyncResource; + } + + emit(event : string | symbol, ...args : any[]) : void { + if (this.#asyncResource === undefined) + throw new ERR_INVALID_THIS('EventEmitterAsyncResource'); + args.unshift(super.emit, this, event); + Reflect.apply(this.#asyncResource.runInAsyncScope, + this.#asyncResource, args); + } +} + +export default EventEmitter; +EventEmitter.on = on; +EventEmitter.once = once; +EventEmitter.getEventListeners = getEventListeners; +EventEmitter.setMaxListeners = setMaxListeners; +EventEmitter.listenerCount = listenerCount; +EventEmitter.EventEmitter = EventEmitter; +EventEmitter.usingDomains = false; +EventEmitter.captureRejectionSymbol = kRejection; +EventEmitter.errorMonitor = kErrorMonitor; +EventEmitter.EventEmitterAsyncResource = EventEmitterAsyncResource; + +export const captureRejectionSymbol = EventEmitter.captureRejectionSymbol; +export const errorMonitor = EventEmitter.errorMonitor; +export let defaultMaxListeners = 10; + +Object.defineProperties(EventEmitter, { + captureRejections: { + get() { + return EventEmitter.prototype[kCapture]; + }, + set(value) { + validateBoolean(value, "EventEmitter.captureRejections"); + + EventEmitter.prototype[kCapture] = value; + }, + enumerable: true, + }, + defaultMaxListeners: { + enumerable: true, + get: function () { + return defaultMaxListeners; + }, + set: function (arg) { + if (typeof arg !== "number" || arg < 0 || Number.isNaN(arg)) { + throw new ERR_OUT_OF_RANGE( + "defaultMaxListeners", + "a non-negative number", + arg, + ); + } + defaultMaxListeners = arg; + }, + }, + kMaxEventTargetListeners: { + value: kMaxEventTargetListeners, + enumerable: false, + configurable: false, + writable: false, + }, + kMaxEventTargetListenersWarned: { + value: kMaxEventTargetListenersWarned, + enumerable: false, + configurable: false, + writable: false, + }, +}); + +// The default for captureRejections is false +Object.defineProperty(EventEmitter.prototype, kCapture, { + value: false, + writable: true, + enumerable: false, +}); + +EventEmitter.init = function (this: any, opts? : EventEmitterOptions) { + if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) { + this._events = Object.create(null); + this._eventsCount = 0; + } + + (this as any)._maxListeners ??= undefined; + + if (opts?.captureRejections) { + validateBoolean(opts.captureRejections, "options.captureRejections"); + (this as any)[kCapture] = Boolean(opts.captureRejections); + } else { + // Assigning the kCapture property directly saves an expensive + // prototype lookup in a very sensitive hot path. + (this as any)[kCapture] = EventEmitter.prototype[kCapture]; + } +}; + +export function setMaxListeners( + n = defaultMaxListeners, + ...eventTargets : any[]) { + if (typeof n !== "number" || n < 0 || Number.isNaN(n)) { + throw new ERR_OUT_OF_RANGE("n", "a non-negative number", n); + } + if (eventTargets.length === 0) { + defaultMaxListeners = n; + } else { + for (let i = 0; i < eventTargets.length; i++) { + const target = eventTargets[i]; + if (target instanceof EventTarget) { + (target as any)[kMaxEventTargetListeners] = n; + (target as any)[kMaxEventTargetListenersWarned] = false; + } else if (typeof target.setMaxListeners === "function") { + target.setMaxListeners(n); + } else { + throw new ERR_INVALID_ARG_TYPE( + "eventTargets", + ["EventEmitter", "EventTarget"], + target, + ); + } + } + } +} + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._eventsCount = 0; +EventEmitter.prototype._maxListeners = undefined; + +function addCatch(that : any, promise : Promise, type : string | symbol, args : any[]) { + if (!that[kCapture]) { + return; + } + + // Handle Promises/A+ spec, then could be a getter + // that throws on second use. + try { + const then = promise.then; + + if (typeof then === "function") { + then.call(promise, undefined, function (err) { + // The callback is called with nextTick to avoid a follow-up + // rejection from this promise. + process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args); + }); + } + } catch (err) { + that.emit("error", err); + } +} + +function emitUnhandledRejectionOrErr(ee : any, err : any, type: string | symbol, args : any[]) { + if (typeof ee[kRejection] === "function") { + ee[kRejection](err, type, ...args); + } else { + // We have to disable the capture rejections mechanism, otherwise + // we might end up in an infinite loop. + const prev = ee[kCapture]; + + // If the error handler throws, it is not catcheable and it + // will end up in 'uncaughtException'. We restore the previous + // value of kCapture in case the uncaughtException is present + // and the exception is handled. + try { + ee[kCapture] = false; + ee.emit("error", err); + } finally { + ee[kCapture] = prev; + } + } +} + +EventEmitter.prototype.setMaxListeners = function setMaxListeners(n : number) { + if (typeof n !== "number" || n < 0 || Number.isNaN(n)) { + throw new ERR_OUT_OF_RANGE("n", "a non-negative number", n); + } + this._maxListeners = n; + return this; +}; + +function _getMaxListeners(that : any) { + if (that._maxListeners === undefined) { + return (EventEmitter as any).defaultMaxListeners; + } + return that._maxListeners; +} + +EventEmitter.prototype.getMaxListeners = function getMaxListeners() { + return _getMaxListeners(this); +}; + +EventEmitter.prototype.emit = function emit(type : string | symbol, ...args: any[]) { + let doError = type === "error"; + + const events = this._events; + if (events !== undefined) { + if (doError && events[kErrorMonitor] !== undefined) { + this.emit(kErrorMonitor, ...args); + } + doError = doError && events.error === undefined; + } else if (!doError) { + return false; + } + + // If there is no 'error' event listener then throw. + if (doError) { + let er; + if (args.length > 0) { + er = args[0]; + } + if (er instanceof Error) { + try { + const capture = {}; + (Error as any).captureStackTrace(capture, EventEmitter.prototype.emit); + } catch { + // pass + } + + // Note: The comments on the `throw` lines are intentional, they show + // up in Node's output if this results in an unhandled exception. + throw er; // Unhandled 'error' event + } + + let stringifiedEr; + try { + stringifiedEr = inspect(er); + } catch { + stringifiedEr = er; + } + + // At least give some kind of context to the user + const err = new ERR_UNHANDLED_ERROR(stringifiedEr); + (err as any).context = er; + throw err; // Unhandled 'error' event + } + + const handler = events[type]; + + if (handler === undefined) { + return false; + } + + if (typeof handler === "function") { + const result = handler.apply(this, args); + + // We check if result is undefined first because that + // is the most common case so we do not pay any perf + // penalty + if (result !== undefined && result !== null) { + addCatch(this, result, type, args); + } + } else { + const len = handler.length; + const listeners = arrayClone(handler); + for (let i = 0; i < len; ++i) { + const result = listeners[i].apply(this, args); + + // We check if result is undefined first because that + // is the most common case so we do not pay any perf + // penalty. + // This code is duplicated because extracting it away + // would make it non-inlineable. + if (result !== undefined && result !== null) { + addCatch(this, result, type, args); + } + } + } + + return true; +}; + +function _addListener(target : any, type : string | symbol, listener : unknown, prepend : boolean) { + let m; + let events; + let existing; + + validateFunction(listener, "listener"); + + events = target._events; + if (events === undefined) { + events = target._events = Object.create(null); + target._eventsCount = 0; + } else { + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (events.newListener !== undefined) { + target.emit("newListener", type, (listener as any).listener ?? listener); + + // Re-assign `events` because a newListener handler could have caused the + // this._events to be assigned to a new object + events = target._events; + } + existing = events[type]; + } + + if (existing === undefined) { + // Optimize the case of one listener. Don't need the extra array object. + events[type] = listener; + ++target._eventsCount; + } else { + if (typeof existing === "function") { + // Adding the second element, need to change to array. + existing = events[type] = prepend + ? [listener, existing] + : [existing, listener]; + // If we've already got an array, just append. + } else if (prepend) { + existing.unshift(listener); + } else { + existing.push(listener); + } + + // Check for listener leak + m = _getMaxListeners(target); + if (m > 0 && existing.length > m && !existing.warned) { + existing.warned = true; + console.log( + "Possible EventEmitter memory leak detected. " + + `${existing.length} ${String(type)} listeners ` + + `added to an EventEmitter. Use ` + + "emitter.setMaxListeners() to increase limit", + ); + // TODO(soon): Implement process.emitWarning and inspect + // // No error code for this since it is a Warning + // // eslint-disable-next-line no-restricted-syntax + // const w = new Error( + // "Possible EventEmitter memory leak detected. " + + // `${existing.length} ${String(type)} listeners ` + + // `added to ${inspect(target, { depth: -1 })}. Use ` + + // "emitter.setMaxListeners() to increase limit", + // ); + // w.name = "MaxListenersExceededWarning"; + // w.emitter = target; + // w.type = type; + // w.count = existing.length; + // process.emitWarning(w); + } + } + + return target; +} + +EventEmitter.prototype.addListener = function addListener(type : string | symbol, listener : unknown) { + return _addListener(this, type, listener, false); +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.prependListener = function prependListener( + type : string | symbol, + listener : unknown, +) { + return _addListener(this, type, listener, true); +}; + +function onceWrapper(this: any) { + if (!this.fired) { + this.target.removeListener(this.type, this.wrapFn); + this.fired = true; + if (arguments.length === 0) { + return this.listener.call(this.target); + } + return this.listener.apply(this.target, arguments); + } +} + +function _onceWrap(target : any, type : string | symbol, listener : unknown) { + const state = { fired: false, wrapFn: undefined, target, type, listener }; + const wrapped = onceWrapper.bind(state); + (wrapped as any).listener = listener; + (state as any).wrapFn = wrapped; + return wrapped; +} + +EventEmitter.prototype.once = function once(type : string | symbol, listener : unknown) { + validateFunction(listener, "listener"); + + this.on(type, _onceWrap(this, type, listener)); + return this; +}; + +EventEmitter.prototype.prependOnceListener = function prependOnceListener( + type : string | symbol, + listener : unknown, +) { + validateFunction(listener, "listener"); + + this.prependListener(type, _onceWrap(this, type, listener)); + return this; +}; + +EventEmitter.prototype.removeListener = function removeListener( + type : string | symbol, + listener : unknown, +) { + validateFunction(listener, "listener"); + + const events = this._events; + if (events === undefined) { + return this; + } + + const list = events[type]; + if (list === undefined) { + return this; + } + + if (list === listener || list.listener === listener) { + if (--this._eventsCount === 0) { + this._events = Object.create(null); + } else { + delete events[type]; + if (events.removeListener) { + this.emit("removeListener", type, list.listener || listener); + } + } + } else if (typeof list !== "function") { + let position = -1; + + for (let i = list.length - 1; i >= 0; i--) { + if (list[i] === listener || list[i].listener === listener) { + position = i; + break; + } + } + + if (position < 0) { + return this; + } + + if (position === 0) { + list.shift(); + } else { + spliceOne(list, position); + } + + if (list.length === 1) { + events[type] = list[0]; + } + + if (events.removeListener !== undefined) { + this.emit("removeListener", type, listener); + } + } + + return this; +}; + +EventEmitter.prototype.off = EventEmitter.prototype.removeListener; + +EventEmitter.prototype.removeAllListeners = function removeAllListeners(type : string | symbol) { + const events = this._events; + if (events === undefined) { + return this; + } + + // Not listening for removeListener, no need to emit + if (events.removeListener === undefined) { + if (arguments.length === 0) { + this._events = Object.create(null); + this._eventsCount = 0; + } else if (events[type] !== undefined) { + if (--this._eventsCount === 0) { + this._events = Object.create(null); + } else { + delete events[type]; + } + } + return this; + } + + // Emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (const key of Reflect.ownKeys(events)) { + if (key === "removeListener") continue; + this.removeAllListeners(key); + } + this.removeAllListeners("removeListener"); + this._events = Object.create(null); + this._eventsCount = 0; + return this; + } + + const listeners = events[type]; + + if (typeof listeners === "function") { + this.removeListener(type, listeners); + } else if (listeners !== undefined) { + // LIFO order + for (let i = listeners.length - 1; i >= 0; i--) { + this.removeListener(type, listeners[i]); + } + } + + return this; +}; + +function _listeners(target : any, type : string | symbol, unwrap : boolean) { + const events = target._events; + + if (events === undefined) { + return []; + } + + const evlistener = events[type]; + if (evlistener === undefined) { + return []; + } + + if (typeof evlistener === "function") { + return unwrap ? [evlistener.listener || evlistener] : [evlistener]; + } + + return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener); +} + +EventEmitter.prototype.listeners = function listeners(type : string | symbol) { + return _listeners(this, type, true); +}; + +EventEmitter.prototype.rawListeners = function rawListeners(type : string | symbol) { + return _listeners(this, type, false); +}; + +const _listenerCount = function listenerCount(this : any, type : string | symbol) { + const events = this._events; + + if (events !== undefined) { + const evlistener = events[type]; + + if (typeof evlistener === "function") { + return 1; + } else if (evlistener !== undefined) { + return evlistener.length; + } + } + + return 0; +}; + +EventEmitter.prototype.listenerCount = _listenerCount; + +export function listenerCount(emitter : any, type : string | symbol) { + if (typeof emitter.listenerCount === "function") { + return emitter.listenerCount(type); + } + return _listenerCount.call(emitter, type); +} + +EventEmitter.prototype.eventNames = function eventNames() { + return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; +}; + +function arrayClone(arr : any[]) { + // At least since V8 8.3, this implementation is faster than the previous + // which always used a simple for-loop + switch (arr.length) { + case 2: + return [arr[0], arr[1]]; + case 3: + return [arr[0], arr[1], arr[2]]; + case 4: + return [arr[0], arr[1], arr[2], arr[3]]; + case 5: + return [arr[0], arr[1], arr[2], arr[3], arr[4]]; + case 6: + return [arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]]; + } + return arr.slice(); +} + +function unwrapListeners(arr : any[]) { + const ret = arrayClone(arr); + for (let i = 0; i < ret.length; ++i) { + const orig = ret[i].listener; + if (typeof orig === "function") { + ret[i] = orig; + } + } + return ret; +} + +export function getEventListeners(emitterOrTarget : any, type : string | symbol) { + // First check if EventEmitter + if (typeof emitterOrTarget.listeners === "function") { + return emitterOrTarget.listeners(type); + } + if (emitterOrTarget instanceof EventTarget) { + // Workers does not implement the ability to get the event listeners on an + // EventTarget the way that Node.js does. We simply return empty here. + return []; + } + throw new ERR_INVALID_ARG_TYPE( + "emitter", + ["EventEmitter", "EventTarget"], + emitterOrTarget, + ); +} + +export interface OnceOptions { + signal?: AbortSignal; +}; + +export async function once(emitter : any, name : string | symbol, options : OnceOptions = {}) { + const signal = options?.signal; + validateAbortSignal(signal, "options.signal"); + if (signal?.aborted) { + throw new AbortError(); + } + return new Promise((resolve, reject) => { + const errorListener = (err : any) => { + emitter.removeListener(name, resolver); + if (signal != null) { + eventTargetAgnosticRemoveListener(signal, "abort", abortListener); + } + reject(err); + }; + const resolver = (...args : any[]) => { + if (typeof emitter.removeListener === "function") { + emitter.removeListener("error", errorListener); + } + if (signal != null) { + eventTargetAgnosticRemoveListener(signal, "abort", abortListener); + } + resolve(args); + }; + eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); + if (name !== "error" && typeof emitter.once === "function") { + emitter.once("error", errorListener); + } + function abortListener() { + eventTargetAgnosticRemoveListener(emitter, name, resolver); + eventTargetAgnosticRemoveListener(emitter, "error", errorListener); + reject(new AbortError()); + } + if (signal != null) { + eventTargetAgnosticAddListener( + signal, + "abort", + abortListener, + { once: true }, + ); + } + }); +} + +const AsyncIteratorPrototype = Object.getPrototypeOf( + Object.getPrototypeOf(async function* () {}).prototype, +); + +function createIterResult(value: any, done: boolean) { + return { value, done }; +} + +function eventTargetAgnosticRemoveListener(emitter : any, name : string | symbol, listener : unknown, flags : unknown = undefined) { + if (typeof emitter.removeListener === "function") { + emitter.removeListener(name, listener); + } else if (typeof emitter.removeEventListener === "function") { + emitter.removeEventListener(name, listener, flags); + } else { + throw new ERR_INVALID_ARG_TYPE("emitter", "EventEmitter", emitter); + } +} + +interface AddListenerFlags { + once? : boolean; +} + +function eventTargetAgnosticAddListener(emitter : any, name : string | symbol, listener : unknown, flags : AddListenerFlags = {}) { + if (typeof emitter.on === "function") { + if (flags?.once) { + emitter.once(name, listener); + } else { + emitter.on(name, listener); + } + } else if (typeof emitter.addEventListener === "function") { + // EventTarget does not have `error` event semantics like Node + // EventEmitters, we do not listen to `error` events here. + emitter.addEventListener(name, (arg : unknown) => { + (listener as any)(arg); + }, flags); + } else { + throw new ERR_INVALID_ARG_TYPE("emitter", "EventEmitter", emitter); + } +} + +interface OnOptions { + signal?: AbortSignal; +} + +export function on(emitter : any, event : string | symbol, options : OnOptions = {}) { + const signal = options?.signal; + validateAbortSignal(signal, "options.signal"); + if (signal?.aborted) { + throw new AbortError(); + } + + const unconsumedEvents : any[] = []; + const unconsumedPromises : any[] = []; + let error : any = null; + let finished = false; + + const iterator = Object.setPrototypeOf({ + next() { + // First, we consume all unread events + const value = unconsumedEvents.shift(); + if (value) { + return Promise.resolve(createIterResult(value, false)); + } + + // Then we error, if an error happened + // This happens one time if at all, because after 'error' + // we stop listening + if (error) { + const p = Promise.reject(error); + // Only the first element errors + error = null; + return p; + } + + // If the iterator is finished, resolve to done + if (finished) { + return Promise.resolve(createIterResult(undefined, true)); + } + + // Wait until an event happens + return new Promise(function (resolve, reject) { + unconsumedPromises.push({ resolve, reject }); + }); + }, + + return() { + eventTargetAgnosticRemoveListener(emitter, event, eventHandler); + eventTargetAgnosticRemoveListener(emitter, "error", errorHandler); + + if (signal) { + eventTargetAgnosticRemoveListener( + signal, + "abort", + abortListener, + { once: true }, + ); + } + + finished = true; + + for (const promise of unconsumedPromises) { + promise.resolve(createIterResult(undefined, true)); + } + + return Promise.resolve(createIterResult(undefined, true)); + }, + + throw(err : any) { + if (!err || !(err instanceof Error)) { + throw new ERR_INVALID_ARG_TYPE( + "EventEmitter.AsyncIterator", + "Error", + err, + ); + } + error = err; + eventTargetAgnosticRemoveListener(emitter, event, eventHandler); + eventTargetAgnosticRemoveListener(emitter, "error", errorHandler); + }, + + [Symbol.asyncIterator]() { + return this; + }, + }, AsyncIteratorPrototype); + + eventTargetAgnosticAddListener(emitter, event, eventHandler); + if (event !== "error" && typeof emitter.on === "function") { + emitter.on("error", errorHandler); + } + + if (signal) { + eventTargetAgnosticAddListener( + signal, + "abort", + abortListener, + { once: true }, + ); + } + + return iterator; + + function abortListener() { + errorHandler(new AbortError()); + } + + function eventHandler(...args : any[]) { + const promise = unconsumedPromises.shift(); + if (promise) { + promise.resolve(createIterResult(args, false)); + } else { + unconsumedEvents.push(args); + } + } + + function errorHandler(err : any) { + finished = true; + + const toError = unconsumedPromises.shift(); + + if (toError) { + toError.reject(err); + } else { + // The next time we call next() + error = err; + } + + iterator.return(); + } +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_buffer.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_buffer.ts new file mode 100644 index 000000000..1b3d68e8f --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_buffer.ts @@ -0,0 +1,2279 @@ + +import { + ERR_BUFFER_OUT_OF_BOUNDS, + ERR_OUT_OF_RANGE, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_INVALID_BUFFER_SIZE, + ERR_UNKNOWN_ENCODING, +} from './internal_errors'; + +import * as bufferUtil from './buffer'; + +import { + isAnyArrayBuffer, + isArrayBufferView, + isUint8Array, +} from './internal_types'; + +import { + normalizeEncoding, +} from './internal_utils'; + +import { + validateString, +} from './validators'; + +import * as internalUtil from './util'; +import { + InspectOptionsStylized, + inspect as utilInspect, +} from './internal_inspect'; + +// Temporary buffers to convert numbers. +const float32Array = new Float32Array(1); +const uInt8Float32Array = new Uint8Array(float32Array.buffer); +const float64Array = new Float64Array(1); +const uInt8Float64Array = new Uint8Array(float64Array.buffer); + +// Check endianness. +float32Array[0] = -1; // 0xBF800000 +// Either it is [0, 0, 128, 191] or [191, 128, 0, 0]. It is not possible to +// check this with `os.endianness()` because that is determined at compile time. +export const bigEndian = uInt8Float32Array[3] === 0; + +// Node.js caps it's max length at uint32_t max, we are very intentionally more +// conservative here, capping at int32_t max. +export const kMaxLength = 2147483647; +export const kStringMaxLength = 536870888; +const MAX_UINT32 = 2 ** 32; +const kIsBuffer = Symbol('kIsBuffer'); + +const customInspectSymbol = + typeof Symbol === "function" && typeof Symbol["for"] === "function" + ? Symbol["for"]("nodejs.util.inspect.custom") + : null; + +const INSPECT_MAX_BYTES = 50; + +export const constants = { + MAX_LENGTH: kMaxLength, + MAX_STRING_LENGTH: kStringMaxLength, +}; + +function createBuffer(length: number) : Buffer { + if (length > kMaxLength) { + throw new ERR_OUT_OF_RANGE('The given length is invalid', `0 to ${kMaxLength}`, length); + } + const buf = new Uint8Array(length); + Object.setPrototypeOf(buf, Buffer.prototype); + return buf as Buffer; +} + +type WithImplicitCoercion = | T | { valueOf(): T; }; +type StringLike = WithImplicitCoercion | { [Symbol.toPrimitive](hint: "string"): string; }; +type ArrayBufferLike = WithImplicitCoercion; +type BufferSource = StringLike|ArrayBufferLike|Uint8Array|ReadonlyArray; + +export interface Buffer extends Uint8Array { + readonly buffer: ArrayBuffer; + readonly parent: ArrayBuffer; + readonly byteOffset: number; + readonly length: number; + compare(target: Uint8Array, + targetStart?: number, + targetEnd?: number, + sourceStart?: number, + sourceEnd?: number): number; + copy(target: Uint8Array, + targetStart?: number, + sourceStart?: number, + sourceEnd?: number): number; + equals(other: Uint8Array): boolean; + fill(value: number, offset?: number, end?: number): this; + fill(value: string, encoding?: string): this; + fill(value: string, offset?: number, end?: number, encoding?: string): this; + fill(value: Uint8Array, offset?: number, end?: number): this; + includes(value: number, byteOffset?: number): boolean; + includes(value: string, encoding?: string): boolean; + includes(value: string, byteOffset?: number, encoding?: string): boolean; + includes(value: Uint8Array, byteOffset?: number): boolean; + indexOf(value: number, byteOffset?: number): number; + indexOf(value: string, encoding?: string): number; + indexOf(value: string, byteOffset?: number, encoding?: string): number; + indexOf(value: Uint8Array, byteOffset?: number): number; + lastIndexOf(value: number, byteOffset?: number): number; + lastIndexOf(value: string, encoding?: string): number; + lastIndexOf(value: string, byteOffset?: number, encoding?: string): number; + lastIndexOf(value: Uint8Array, byteOffset?: number): number; + readBigInt64BE(offset?: number) : bigint; + readBigInt64LE(offset?: number) : bigint; + readBigUInt64BE(offset?: number) : bigint; + readBigUInt64LE(offset?: number) : bigint; + readDoubleBE(offset?: number) : number; + readDoubleLE(offset?: number) : number; + readFloatBE(offset?: number) : number; + readFloatLE(offset?: number) : number; + readInt8(offset?: number) : number; + readInt16BE(offset?: number) : number; + readInt16LE(offset?: number) : number; + readInt32BE(offset?: number) : number; + readInt32LE(offset?: number) : number; + readIntBE(offset?: number, byteLength?: number) : number; + readIntLE(offset?: number, byteLength?: number) : number; + readUInt8(offset?: number) : number; + readUInt16BE(offset?: number) : number; + readUInt16LE(offset?: number) : number; + readUInt32BE(offset?: number) : number; + readUInt32LE(offset?: number) : number; + readUIntBE(offset?: number, byteLength?: number) : number; + readUIntLE(offset?: number, byteLength?: number) : number; + swap16(): this; + swap32(): this; + swap64(): this; + toJSON(): {type: 'Buffer', data: number[]}; + toString(encoding?: string, start?: number, end?: number): string; + write(string: string, encoding?: string): number; + write(string: string, offset?: number, encoding?: string): number; + write(string: string, offset?: number, length?: number, encoding?: string): number; + writeBigInt64BE(value: bigint, offset?: number): number; + writeBigInt64LE(value: bigint, offset?: number): number; + writeBigUInt64BE(value: bigint, offset?: number): number; + writeBigUInt64LE(value: bigint, offset?: number): number; + writeDoubleBE(value: number, offset?: number): number; + writeDoubleLE(value: number, offset?: number): number; + writeFloatBE(value: number, offset?: number): number; + writeFloatLE(value: number, offset?: number): number; + writeInt8(value: number, offset?: number): number; + writeInt16BE(value: number, offset?: number): number; + writeInt16LE(value: number, offset?: number): number; + writeInt32BE(value: number, offset?: number): number; + writeInt32LE(value: number, offset?: number): number; + writeIntBE(value: number, offset?: number, byteLength?: number): number; + writeIntLE(value: number, offset?: number, byteLength?: number): number; + writeUInt8(value: number, offset?: number): number; + writeUInt16BE(value: number, offset?: number): number; + writeUInt16LE(value: number, offset?: number): number; + writeUInt32BE(value: number, offset?: number): number; + writeUInt32LE(value: number, offset?: number): number; + writeUIntBE(value: number, offset?: number, byteLength?: number): number; + writeUIntLE(value: number, offset?: number, byteLength?: number): number; + new (array:Iterable): Buffer; + new (arrayBuffer: ArrayBufferLike, byteOffset?: number, length?: number): Buffer; + new (buffer: ArrayBufferView): Buffer; + new (size: number): Buffer; + new (string: string, encoding?: string): Buffer; +}; + +type FillValue = string|number|ArrayBufferView; + +export function Buffer(value: number) : Buffer; +export function Buffer(value: StringLike, encoding?: string) : Buffer; +export function Buffer(value: ArrayBufferLike, byteOffset?: number, length?: number) : Buffer; +export function Buffer(value: Uint8Array|ReadonlyArray, + byteOffset?: number, + length?: number) : Buffer; +export function Buffer(value: StringLike, + encoding?: string) : Buffer; +export function Buffer(value: number|BufferSource, + encodingOrOffset? : string|number, + length?: number) : Buffer { + if (typeof value === "number") { + if (typeof encodingOrOffset === "string") { + throw new ERR_INVALID_ARG_TYPE("string", "string", value); + } + return allocUnsafe(value); + } + + return _from(value, encodingOrOffset, length); +} + +Object.setPrototypeOf(Buffer.prototype, Uint8Array.prototype); +Object.setPrototypeOf(Buffer, Uint8Array); + +Object.defineProperties(Buffer, { + // By default, Node.js allocates Buffers from a shared slab allocation + // as a performance optimization. While this is fast, it's not ideal + // in our environment as it could potentially be used to leak buffer + // data across multiple requests. We always want our Buffer instances + // to be distinct allocations. To signal this, we keep our poolSize + // always set to 0. + poolSize: { + enumerable: true, + value: 0, + writable: false, + } +}); + +Object.defineProperties(Buffer.prototype, { + parent: { + enumerable: true, + get() { + if (!Buffer.isBuffer(this)) { + return void 0; + } + return this.buffer; + }, + }, + offset: { + enumerable: true, + get() { + if (!Buffer.isBuffer(this)) { + return void 0; + } + return this.byteOffset; + }, + }, + [kIsBuffer]: { + enumerable: false, + configurable: false, + value: true, + }, +}); + +function _from(value: BufferSource, + encodingOrOffset? : string|number, + length?: number) : Buffer { + if (typeof value === "string") { + return fromString(value, encodingOrOffset as string | undefined) as Buffer; + } + + if (typeof value === "object" && value != null) { + if (isAnyArrayBuffer(value)) { + return fromArrayBuffer(value as ArrayBufferLike, encodingOrOffset as number, length) as Buffer; + } + + const valueOf = value?.valueOf(); + if (valueOf != null && valueOf !== value && + (typeof valueOf === "string" || typeof valueOf === "object")) { + return _from(valueOf as BufferSource, encodingOrOffset, length); + } + + if ((value as any).length !== undefined || isAnyArrayBuffer((value as any).buffer)) { + if (typeof (value as any).length !== "number") { + return createBuffer(0); + } + + return fromArrayLike(value as any) as Buffer; + } + + if ((value as any).type === "Buffer" && Array.isArray((value as any).data)) { + return fromArrayLike((value as any).data) as Buffer; + } + + const toPrimitive = (value as any)[Symbol.toPrimitive]; + if (typeof toPrimitive === "function") { + const primitive = toPrimitive("string"); + if (typeof primitive === "string") { + return fromString(primitive, encodingOrOffset as string | undefined) as Buffer; + } + } + } + + throw new ERR_INVALID_ARG_TYPE( + "first argument", + [ + "string", + "Buffer", + "TypedArray", + "ArrayBuffer", + "SharedArrayBuffer", + "Array", + "Array-like Object" + ], + value, + ); +} + +function from(value: StringLike, + encoding?: string) : Buffer; +function from(value: ArrayBufferLike, + byteOffset?: number, + length?: number) : Buffer; +function from(value: Uint8Array|ReadonlyArray, + byteOffset?: number, + length?: number) : Buffer; +function from(value: BufferSource, encodingOrOffset?: string|number, length?: number) { + return _from(value, encodingOrOffset, length); +}; + +function fromString(string: StringLike, encoding?: string) { + if (typeof encoding !== "string" || encoding === "") { + encoding = "utf8"; + } + const normalizedEncoding = normalizeEncoding(encoding); + if (!Buffer.isEncoding(normalizedEncoding)) { + throw new ERR_UNKNOWN_ENCODING(encoding); + } + + const ab = bufferUtil.decodeString(`${string}`, normalizedEncoding as string); + if (ab === undefined) { + throw new ERR_INVALID_ARG_VALUE('string', string, + `Unable to decode string using encoding ${encoding}`); + } + return fromArrayBuffer(ab, 0, ab.byteLength); +} + +function fromArrayLike(array: Uint8Array|ReadonlyArray) { + const u8 = Uint8Array.from(array); + return fromArrayBuffer(u8.buffer, u8.byteOffset, u8.byteLength); +} + +function fromArrayBuffer(obj: ArrayBufferLike, + byteOffset: number, + length?: number) { + // Convert byteOffset to integer + if (byteOffset === undefined) { + byteOffset = 0; + } else { + byteOffset = +byteOffset; + if (Number.isNaN(byteOffset)) { + byteOffset = 0; + } + } + + const maxLength = (obj as ArrayBuffer).byteLength - byteOffset; + + if (maxLength < 0) { + throw new ERR_BUFFER_OUT_OF_BOUNDS("offset"); + } + + if (length === undefined) { + length = maxLength; + } else { + // Convert length to non-negative integer. + length = +length; + if (length > 0) { + if (length > maxLength) { + throw new ERR_BUFFER_OUT_OF_BOUNDS("length"); + } + } else { + length = 0; + } + } + + const buffer = new Uint8Array(obj as ArrayBuffer, byteOffset, length); + Object.setPrototypeOf(buffer, Buffer.prototype); + return buffer; +} + +Buffer.from = from; + +function of(...args: number[]) { + const buf = Buffer.alloc(args.length); + for (let k = 0; k < args.length; k++) + buf[k] = args[k]!; + return buf; +} + +Buffer.of = of; + +function alloc(size: number, fill?: FillValue, encoding?: string) : Buffer { + validateNumber(size, "size"); + if (Number.isNaN(size)) { + throw new ERR_INVALID_ARG_VALUE.RangeError('size', size); + } + if (size >= kMaxLength) { + throw new ERR_OUT_OF_RANGE("size", `0 to ${kMaxLength}`, size); + } + + const buffer = createBuffer(size); + if (fill !== undefined) { + if (encoding !== undefined) { + validateString(encoding, 'encoding'); + } + return buffer.fill(fill as any, encoding); + } + return buffer; +} + +Buffer.alloc = alloc; + +function allocUnsafe(size: number) : Buffer { + return alloc(size); +} + +Buffer.allocUnsafe = allocUnsafe; +Buffer.allocUnsafeSlow = allocUnsafe; + +export function SlowBuffer(length: number) { + return alloc(+length); +} + +Object.setPrototypeOf(SlowBuffer.prototype, Uint8Array.prototype); +Object.setPrototypeOf(SlowBuffer, Uint8Array); + +Buffer.isBuffer = function isBuffer(b: unknown) { + return b != null && (b as any)[kIsBuffer] && b !== Buffer.prototype; +}; + +export function compare(a: Buffer|Uint8Array, b: Buffer|Uint8Array) { + if (isInstance(a, Uint8Array)) { + const buf = a as Uint8Array; + a = fromArrayBuffer(buf.buffer, buf.byteOffset, buf.byteLength); + } + if (isInstance(b, Uint8Array)) { + const buf = b as Uint8Array; + b = fromArrayBuffer(buf.buffer, buf.byteOffset, buf.byteLength); + } + if (!Buffer.isBuffer(a)) { + throw new ERR_INVALID_ARG_TYPE('a', ['Buffer', 'Uint8Array'], typeof a); + } + if (!Buffer.isBuffer(b)) { + throw new ERR_INVALID_ARG_TYPE('b', ['Buffer', 'Uint8Array'], typeof b); + } + if (a === b) return 0; + + return bufferUtil.compare(a, b); +} + +Buffer.compare = compare; + +export function isEncoding(encoding: unknown) { + return typeof encoding === "string" && + encoding.length !== 0 && + normalizeEncoding(encoding) !== undefined; +} + +Buffer.isEncoding = isEncoding; + +Buffer.concat = function concat(list: (Buffer|Uint8Array)[], length?: number) { + if (!Array.isArray(list)) { + throw new ERR_INVALID_ARG_TYPE("list", "(Buffer|Uint8Array)[]", list); + } + + if (list.length === 0) return alloc(0); + + if (length === undefined) { + length = 0; + for (let i = 0; i < list.length; i++) { + if (list[i]!.length !== undefined) { + length += list[i]!.length; + } else { + throw new ERR_INVALID_ARG_TYPE('list', '(Buffer|Uint8Array)[]', list[i]); + } + } + } + validateOffset(length, "length"); + + const ab = bufferUtil.concat(list, length as number); + return fromArrayBuffer(ab, 0, length); +}; + +function base64ByteLength(str: string) { + let len = str.length; + if (str.charCodeAt(len - 1) === 0x3d) { + len--; + } if (len > 1 && str.charCodeAt(len - 1) === 0x3d) + len--; + + // Base64 ratio: 3/4 + return (len * 3) >>> 2; +} + +function byteLength(string: string|ArrayBufferView|ArrayBuffer|SharedArrayBuffer, + encoding?: string) { + if (typeof string !== "string") { + if (isArrayBufferView(string) || isAnyArrayBuffer(string)) { + return string.byteLength; + } + + throw new ERR_INVALID_ARG_TYPE("string", ["string", "Buffer", "ArrayBuffer"], string); + } + + string = `${string}`; + let normalizedEncoding = normalizeEncoding(encoding); + if (!Buffer.isEncoding(normalizedEncoding)) { + normalizedEncoding = "utf8"; + } + + switch (normalizedEncoding) { + case 'ascii': + // Fall through + case 'latin1': + return (string as string).length; + case 'utf16le': + return (string as string).length * 2; + case 'base64': + // Fall through + case 'base64url': + return base64ByteLength(string as string); + case 'hex': + return (string as string).length >>> 1; + case 'utf8': + // Fall-through + default: + return bufferUtil.byteLength(string as string, normalizedEncoding as string); + } +} + +Buffer.byteLength = byteLength; + +Buffer.prototype.swap16 = function swap16() { + const len = this.length; + if (len % 2 !== 0) { + throw new ERR_INVALID_BUFFER_SIZE(16); + } + bufferUtil.swap(this, 16); + return this; +}; + +Buffer.prototype.swap32 = function swap32() { + const len = this.length; + if (len % 4 !== 0) { + throw new ERR_INVALID_BUFFER_SIZE(32); + } + bufferUtil.swap(this, 32); + return this; +}; + +Buffer.prototype.swap64 = function swap64() { + const len = this.length; + if (len % 8 !== 0) { + throw new ERR_INVALID_BUFFER_SIZE(64); + } + bufferUtil.swap(this, 64); + return this; +}; + +Buffer.prototype.toString = function toString( + encoding?: string, + start?: number, + end?: number) { + if (arguments.length === 0) { + return bufferUtil.toString(this, 0, this.length, "utf8"); + } + + const len = this.length; + + if (start === undefined || start <= 0) { + start = 0; + } else if (start >= len) { + return ""; + } else { + start |= 0; + } + + if (end === undefined || end > len) { + end = len; + } else { + end |= 0; + } + + if ((end as number) <= start) { + return ""; + } + + const normalizedEncoding = normalizeEncoding(`${encoding}`); + if (!Buffer.isEncoding(normalizedEncoding)) { + throw new ERR_UNKNOWN_ENCODING(encoding as string); + } + + return bufferUtil.toString(this, start as number, end as number, normalizedEncoding as string); +}; + +Buffer.prototype.toLocaleString = Buffer.prototype.toString; + +Buffer.prototype.equals = function equals(b: Buffer|Uint8Array) { + return compare(this, b) === 0; +}; + +Buffer.prototype.inspect = function inspect(_recurseTimes: number, ctx: InspectOptionsStylized) { + let str = ""; + const max = INSPECT_MAX_BYTES; + str = this.toString("hex", 0, max).replace(/(.{2})/g, "$1 ").trim(); + const remaining = this.length - max; + if (remaining > 0) { + str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`; + } + // Inspect special properties as well, if possible. + if (ctx) { + let extras = false; + const filter = ctx.showHidden ? internalUtil.ALL_PROPERTIES : internalUtil.ONLY_ENUMERABLE; + const obj: Record = { __proto__: null }; + internalUtil.getOwnNonIndexProperties(this, filter).forEach( + (key) => { + extras = true; + obj[key] = this[key]; + }); + if (extras) { + if (this.length !== 0) + str += ', '; + // '[Object: null prototype] {'.length === 26 + // This is guarded with a test. + str += utilInspect(obj, { + ...ctx, + breakLength: Infinity, + compact: true, + }).slice(27, -2); + } + } + return ""; +}; + +if (customInspectSymbol) { + Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect; +} + +Buffer.prototype.compare = function compare( + target: Buffer|Uint8Array, + start?: number, + end?: number, + thisStart?: number, + thisEnd?: number, +) { + if (isInstance(target, Uint8Array)) { + target = fromArrayBuffer(target.buffer, target.byteOffset, target.byteLength); + } + if (!Buffer.isBuffer(target)) { + throw new ERR_INVALID_ARG_TYPE("target", ["Buffer", "Uint8Array"], target); + } + + if (start === undefined) { + start = 0; + } else { + validateOffset(start, "targetStart", 0, kMaxLength); + } + + if (end === undefined) { + end = target.length; + } else { + validateOffset(end, "targetEnd", 0, target.length); + } + + if (thisStart === undefined) { + thisStart = 0; + } else { + validateOffset(thisStart as number, "sourceStart", 0, kMaxLength); + } + + if (thisEnd === undefined) { + thisEnd = this.length; + } else { + validateOffset(thisEnd as number, "sourceEnd", 0, this.length); + } + + return bufferUtil.compare(this, target, { + aStart: thisStart as number, + aEnd: thisEnd as number, + bStart: start as number, + bEnd: end as number, + }); +}; + +function includes( + this: Buffer, + val: string|number|Buffer|Uint8Array, + byteOffset?: number, + encoding?: string) { + return this.indexOf(val as any, byteOffset, encoding) !== -1; +} + +Buffer.prototype.includes = includes; + + +// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, +// OR the last index of `val` in `buffer` at offset <= `byteOffset`. +// +// Arguments: +// - buffer - a Buffer to search +// - val - a string, Buffer, or number +// - byteOffset - an index into `buffer`; will be clamped to an int32 +// - encoding - an optional encoding, relevant if val is a string +// - dir - true for indexOf, false for lastIndexOf +function bidirectionalIndexOf( + buffer: Uint8Array, + val: string|number|Buffer|Uint8Array, + byteOffset: number|string|undefined, + encoding: string|undefined, + dir: boolean|undefined) { + + if (Buffer.isBuffer(val) && !isUint8Array(val)) { + throw new ERR_INVALID_ARG_TYPE('val', ['string', 'number', 'Buffer', 'Uint8Array'], val); + } + + if (typeof byteOffset === 'string') { + encoding = byteOffset; + byteOffset = undefined; + } else if (byteOffset as number > 0x7fffffff) { + byteOffset = 0x7fffffff; + } else if (byteOffset as number < -0x80000000) { + byteOffset = -0x80000000; + } + // Coerce to Number. Values like null and [] become 0. + byteOffset = +(byteOffset as number); + // If the offset is undefined, "foo", {}, coerces to NaN, search whole buffer. + if (Number.isNaN(byteOffset)) { + byteOffset = dir ? 0 : (buffer.length || buffer.byteLength); + } + dir = !!dir; // Cast to bool. + + if (typeof val === 'number') { + val = (val >>> 0) & 0xff; + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset); + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset); + } + } + + if (typeof val !== 'string' && !isUint8Array(val) && !Buffer.isBuffer(val)) { + throw new ERR_INVALID_ARG_TYPE('value', ['number', 'string', 'Buffer', 'Uint8Array'], val); + } + + let normalizedEncoding = normalizeEncoding(encoding); + if (!Buffer.isEncoding(normalizedEncoding)) { + throw new ERR_UNKNOWN_ENCODING(encoding as string); + } + + const result = bufferUtil.indexOf(buffer, val, byteOffset, normalizedEncoding, dir); + return result == null ? -1 : result; +} + +Buffer.prototype.indexOf = function indexOf( + val: string|number|Buffer|Uint8Array, + byteOffset?: number|string, + encoding?: string) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true); +}; + +Buffer.prototype.lastIndexOf = function lastIndexOf( + val: string|number|Buffer|Uint8Array, + byteOffset?: number|string, + encoding?: string) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false); +}; + +Buffer.prototype.asciiSlice = function asciiSlice(offset: number, length: number) { + validateOffset(offset, "offset", 0, this.length); + validateOffset(length, "length", 0, this.length - offset); + return bufferUtil.toString(this, offset, offset + length, 'ascii'); +}; + +Buffer.prototype.base64Slice = function base64Slice(offset: number, length: number) { + validateOffset(offset, "offset", 0, this.length); + validateOffset(length, "length", 0, this.length - offset); + return bufferUtil.toString(this, offset, offset + length, 'base64'); +}; + +Buffer.prototype.base64urlSlice = function base64urlSlice(offset: number, length: number) { + validateOffset(offset, "offset", 0, this.length); + validateOffset(length, "length", 0, this.length - offset); + return bufferUtil.toString(this, offset, offset + length, 'base64url'); +}; + +Buffer.prototype.hexSlice = function hexSlice(offset: number, length: number) { + validateOffset(offset, "offset", 0, this.length); + validateOffset(length, "length", 0, this.length - offset); + return bufferUtil.toString(this, offset, offset + length, 'hex'); +}; + +Buffer.prototype.latin1Slice = function latin1Slice(offset: number, + length: number) { + validateOffset(offset, "offset", 0, this.length); + validateOffset(length, "length", 0, this.length - offset); + return bufferUtil.toString(this, offset, offset + length, 'latin1'); +}; + +Buffer.prototype.ucs2Slice = function ucs2Slice(offset: number, length: number) { + validateOffset(offset, "offset", 0, this.length); + validateOffset(length, "length", 0, this.length - offset); + return bufferUtil.toString(this, offset, offset + length, 'utf16le'); +}; + +Buffer.prototype.utf8Slice = function utf8Slice(offset: number, length: number) { + validateOffset(offset, "offset", 0, this.length); + validateOffset(length, "length", 0, this.length - offset); + return bufferUtil.toString(this, offset, offset + length, 'utf8'); +}; + +Buffer.prototype.asciiWrite = function asciiWrite(string: StringLike, + offset?: number, + length?: number) { + offset ??= 0; + length ??= this.length; + validateOffset(offset as number, "offset", 0, this.length); + validateOffset(length as number, "length", 0, this.length - offset); + return bufferUtil.write(this, `${string}`, offset as number, length as number, 'ascii'); +}; + +Buffer.prototype.base64Write = function base64Write(string: StringLike, + offset?: number, + length?: number) { + offset ??= 0; + length ??= this.length; + validateOffset(offset as number, "offset", 0, this.length); + validateOffset(length as number, "length", 0, this.length - offset); + return bufferUtil.write(this, `${string}`, offset as number, length as number, 'base64'); +}; + +Buffer.prototype.base64urlWrite = function base64urlWrite(string: StringLike, + offset?: number, + length?: number) { + offset ??= 0; + length ??= this.length; + validateOffset(offset as number, "offset", 0, this.length); + validateOffset(length as number, "length", 0, this.length - offset); + return bufferUtil.write(this, `${string}`, offset as number, length as number, 'base64url'); +}; + +Buffer.prototype.hexWrite = function hexWrite(string: StringLike, + offset: number, + length: number) { + offset ??= 0; + length ??= this.length; + validateOffset(offset as number, "offset", 0, this.length); + validateOffset(length as number, "length", 0, this.length - offset); + return bufferUtil.write(this, `${string}`, offset as number, length as number, 'hex'); +}; + +Buffer.prototype.latin1Write = function latin1Write(string: StringLike, + offset: number, + length: number) { + offset ??= 0; + length ??= this.length; + validateOffset(offset as number, "offset", 0, this.length); + validateOffset(length as number, "length", 0, this.length - offset); + return bufferUtil.write(this, `${string}`, offset as number, length as number, 'latin1'); +}; + +Buffer.prototype.ucs2Write = function ucs2Write(string: StringLike, + offset: number, + length: number) { + offset ??= 0; + length ??= this.length; + validateOffset(offset as number, "offset", 0, this.length); + validateOffset(length as number, "length", 0, this.length - offset); + return bufferUtil.write(this, `${string}`, offset as number, length as number, 'utf16le'); +}; + +Buffer.prototype.utf8Write = function utf8Write(string: StringLike, + offset: number, + length: number) { + offset ??= 0; + length ??= this.length; + validateOffset(offset as number, "offset", 0, this.length); + validateOffset(length as number, "length", 0, this.length - offset); + return bufferUtil.write(this, `${string}`, offset as number, length as number, 'utf8'); +}; + +Buffer.prototype.write = function write(string: StringLike, + offset?: number | string, + length?: number | string, + encoding?: string) { + string = `${string}`; + if (offset === undefined) { + // Buffer#write(string) + return bufferUtil.write(this, string as string, 0, this.length, "utf8"); + } + + if (length === undefined && typeof offset === 'string') { + // Buffer#write(string, encoding) + encoding = offset; + length = this.length; + offset = 0; + } else { + // Buffer#write(string, offset[, length][, encoding]) + validateOffset(offset as number, 'offset', 0, this.length); + + const remaining = this.length - (offset as number); + + if (length === undefined) { + length = remaining; + } else if (typeof length === 'string') { + encoding = length; + length = remaining; + } else { + validateOffset(length, 'length', 0, this.length); + if (length > remaining) { + length = remaining; + } + } + } + + if (!encoding) { + return bufferUtil.write(this, string as string, offset as number, length as number, "utf8"); + } + + const normalizedEncoding = normalizeEncoding(encoding); + if (!Buffer.isEncoding(normalizedEncoding)) { + throw new ERR_UNKNOWN_ENCODING(encoding as string); + } + + return bufferUtil.write(this, string as string, offset as number, length as number, + normalizedEncoding as string); +}; + +Buffer.prototype.toJSON = function toJSON() { + return { + type: "Buffer", + data: Array.prototype.slice.call(this._arr || this, 0), + }; +}; + +Buffer.prototype.slice = function slice(start: number, end?: number) { + const len = this.length; + start = ~~start; + end = end === void 0 ? len : ~~end; + if (start < 0) { + start += len; + if (start < 0) { + start = 0; + } + } else if (start > len) { + start = len; + } + if (end === undefined) { + end = this.byteLength; + } else if (end < 0) { + end += len; + if (end < 0) { + end = 0; + } + } else if (end > len) { + end = len; + } + if ((end as number) < start) { + end = start; + } + const newBuf = this.subarray(start, end); + Object.setPrototypeOf(newBuf, Buffer.prototype); + return newBuf; +}; + +Buffer.prototype.readUintLE = Buffer.prototype.readUIntLE = function readUIntLE( + offset: number, + byteLength: number) { + if (offset === undefined) { + throw new ERR_INVALID_ARG_TYPE("offset", "number", offset); + } + switch (byteLength) { + case 1: return this.readUInt8(offset); + case 2: return this.readUInt16LE(offset); + case 3: return readUInt24LE(this, offset); + case 4: return this.readUInt32LE(offset); + case 5: return readUInt40LE(this, offset); + case 6: return readUInt48LE(this, offset); + default: + boundsError(byteLength, 6, "byteLength"); + } +}; + +Buffer.prototype.readUintBE = Buffer.prototype.readUIntBE = function readUIntBE( + offset: number, + byteLength: number) { + if (offset === undefined) { + throw new ERR_INVALID_ARG_TYPE("offset", "number", offset); + } + switch (byteLength) { + case 1: return this.readUInt8(offset); + case 2: return this.readUInt16BE(offset); + case 3: return readUInt24BE(this, offset); + case 4: return this.readUInt32BE(offset); + case 5: return readUInt40BE(this, offset); + case 6: return readUInt48BE(this, offset); + default: + boundsError(byteLength, 6, "byteLength"); + } +}; + +Buffer.prototype.readUint8 = Buffer.prototype.readUInt8 = function readUInt8( + offset: number = 0) { + validateOffset(offset, "offset", 0, this.length); + const val = this[offset]; + if (val === undefined) { + boundsError(offset, this.length - 1); + } + + return val; +}; + +Buffer.prototype.readUint16BE = Buffer.prototype.readUInt16BE = readUInt16BE; + +Buffer.prototype.readUint16LE = + Buffer.prototype.readUInt16LE = + function readUInt16LE(offset: number = 0) { + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 1]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 2); + } + + return first + last * 2 ** 8; + }; + +Buffer.prototype.readUint32LE = + Buffer.prototype.readUInt32LE = + function readUInt32LE(this: Buffer, offset: number = 0) { + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 4); + } + + return first + + this[++offset]! * 2 ** 8 + + this[++offset]! * 2 ** 16 + + last * 2 ** 24; + }; + +Buffer.prototype.readUint32BE = Buffer.prototype.readUInt32BE = readUInt32BE; + +Buffer.prototype.readBigUint64LE = + Buffer.prototype.readBigUInt64LE = + function readBigUInt64LE(this: Buffer, offset: number = 0) { + offset = offset >>> 0; + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8); + } + const lo = first + this[++offset]! * 2 ** 8 + + this[++offset]! * 2 ** 16 + + this[++offset]! * 2 ** 24; + const hi = this[++offset]! + this[++offset]! * 2 ** 8 + + this[++offset]! * 2 ** 16 + last * 2 ** 24; + return BigInt(lo) + (BigInt(hi) << BigInt(32)); + }; + +Buffer.prototype.readBigUint64BE = + Buffer.prototype.readBigUInt64BE = + function readBigUInt64BE(this: Buffer, offset: number = 0) { + offset = offset >>> 0; + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8); + } + const hi = first * 2 ** 24 + this[++offset]! * 2 ** 16 + + this[++offset]! * 2 ** 8 + this[++offset]!; + const lo = this[++offset]! * 2 ** 24 + this[++offset]! * 2 ** 16 + + this[++offset]! * 2 ** 8 + last; + return (BigInt(hi) << BigInt(32)) + BigInt(lo); + }; + +Buffer.prototype.readIntLE = function readIntLE( + offset: number, + byteLength: number, +) { + if (offset === undefined) { + throw new ERR_INVALID_ARG_TYPE("offset", "number", offset); + } + switch (byteLength) { + case 1: return this.readInt8(offset); + case 2: return this.readInt16LE(offset); + case 3: return readInt24LE(this, offset); + case 4: return this.readInt32LE(offset); + case 5: return readInt40LE(this, offset); + case 6: return readInt48LE(this, offset); + default: + boundsError(byteLength, 6, "byteLength"); + } +}; + +Buffer.prototype.readIntBE = function readIntBE( + offset: number, + byteLength: number) { + if (offset === undefined) { + throw new ERR_INVALID_ARG_TYPE("offset", "number", offset); + } + switch (byteLength) { + case 1: return this.readInt8(offset); + case 2: return this.readInt16BE(offset); + case 3: return readInt24BE(this, offset); + case 4: return this.readInt32BE(offset); + case 5: return readInt40BE(this, offset); + case 6: return readInt48BE(this, offset); + default: + boundsError(byteLength, 6, "byteLength"); + } +}; + +Buffer.prototype.readInt8 = function readInt8(offset: number = 0) { + validateOffset(offset, "offset", 0, this.length); + const val = this[offset]; + if (val === undefined) { + boundsError(offset, this.length - 1); + } + + return val | (val & 2 ** 7) * 0x1fffffe; +}; + +Buffer.prototype.readInt16LE = function readInt16LE(offset: number = 0) { + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 1]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 2); + } + + const val = first + last * 2 ** 8; + return val | (val & 2 ** 15) * 0x1fffe; +}; + +Buffer.prototype.readInt16BE = function readInt16BE(offset: number = 0) { + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 1]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 2); + } + + const val = first * 2 ** 8 + last; + return val | (val & 2 ** 15) * 0x1fffe; +}; + +Buffer.prototype.readInt32LE = function readInt32LE(offset: number = 0) { + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 4); + } + + return first + + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + + (last << 24); // Overflow +}; + +Buffer.prototype.readInt32BE = function readInt32BE(offset: number = 0) { + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 4); + } + + return (first << 24) + // Overflow + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + last; +}; + +Buffer.prototype.readBigInt64LE = function readBigInt64LE(this: Buffer, offset: number = 0) { + offset = offset >>> 0; + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8); + } + const val = this[offset + 4]! + this[offset + 5]! * 2 ** 8 + + this[offset + 6]! * 2 ** 16 + (last << 24); + return (BigInt(val) << BigInt(32)) + + BigInt( + first + this[++offset]! * 2 ** 8 + this[++offset]! * 2 ** 16 + + this[++offset]! * 2 ** 24, + ); +}; + +Buffer.prototype.readBigInt64BE = function readBigInt64BE(this: Buffer, offset: number = 0) { + offset = offset >>> 0; + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8); + } + const val = (first << 24) + this[++offset]! * 2 ** 16 + + this[++offset]! * 2 ** 8 + this[++offset]!; + return (BigInt(val) << BigInt(32)) + + BigInt( + this[++offset]! * 2 ** 24 + this[++offset]! * 2 ** 16 + + this[++offset]! * 2 ** 8 + last, + ); +}; + +Buffer.prototype.readFloatLE = function readFloatLE(offset: number = 0) { + return bigEndian + ? readFloatBackwards(this, offset) + : readFloatForwards(this, offset); +}; + +Buffer.prototype.readFloatBE = function readFloatBE(offset: number = 0) { + return bigEndian + ? readFloatForwards(this, offset) + : readFloatBackwards(this, offset); +}; + +Buffer.prototype.readDoubleLE = function readDoubleLE(offset: number = 0) { + return bigEndian + ? readDoubleBackwards(this, offset) + : readDoubleForwards(this, offset); +}; + +Buffer.prototype.readDoubleBE = function readDoubleBE(offset: number = 0) { + return bigEndian + ? readDoubleForwards(this, offset) + : readDoubleBackwards(this, offset); +}; + +Buffer.prototype.writeUintLE = + Buffer.prototype.writeUIntLE = + function writeUIntLE(value: number, offset: number, byteLength: number) { + switch (byteLength) { + case 1: return writeU_Int8(this, value, offset, 0, 0xff); + case 2: return writeU_Int16LE(this, value, offset, 0, 0xffff); + case 3: return writeU_Int24LE(this, value, offset, 0, 0xffffff); + case 4: return writeU_Int32LE(this, value, offset, 0, 0xffffffff); + case 5: return writeU_Int40LE(this, value, offset, 0, 0xffffffffff); + case 6: return writeU_Int48LE(this, value, offset, 0, 0xffffffffffff); + default: + boundsError(byteLength, 6, "byteLength"); + } + }; + +Buffer.prototype.writeUintBE = + Buffer.prototype.writeUIntBE = + function writeUIntBE(value: number, offset: number, byteLength: number) { + switch (byteLength) { + case 1: return writeU_Int8(this, value, offset, 0, 0xff); + case 2: return writeU_Int16BE(this, value, offset, 0, 0xffff); + case 3: return writeU_Int24BE(this, value, offset, 0, 0xffffff); + case 4: return writeU_Int32BE(this, value, offset, 0, 0xffffffff); + case 5: return writeU_Int40BE(this, value, offset, 0, 0xffffffffff); + case 6: return writeU_Int48BE(this, value, offset, 0, 0xffffffffffff); + default: + boundsError(byteLength, 6, "byteLength"); + } + }; + +Buffer.prototype.writeUint8 = Buffer.prototype.writeUInt8 = function writeUInt8( + value: number, + offset: number = 0, +) { + return writeU_Int8(this, value, offset, 0, 0xff); +}; + +Buffer.prototype.writeUint16LE = + Buffer.prototype.writeUInt16LE = + function writeUInt16LE(value: number, offset: number = 0) { + return writeU_Int16LE(this, value, offset, 0, 0xffff); + }; + +Buffer.prototype.writeUint16BE = + Buffer.prototype.writeUInt16BE = + function writeUInt16BE(value: number, offset: number = 0) { + return writeU_Int16BE(this, value, offset, 0, 0xffff); + }; + +Buffer.prototype.writeUint32LE = + Buffer.prototype.writeUInt32LE = + function writeUInt32LE(value: number, offset: number = 0) { + return _writeUInt32LE(this, value, offset, 0, 0xffffffff); + }; + +Buffer.prototype.writeUint32BE = + Buffer.prototype.writeUInt32BE = + function writeUInt32BE(value: number, offset: number = 0) { + return _writeUInt32BE(this, value, offset, 0, 0xffffffff); + }; + +function wrtBigUInt64LE( + buf: Buffer, + value: bigint, + offset: number, + min: bigint, + max: bigint) { + checkIntBI(value, min, max, buf, offset, 7); + let lo = Number(value & BigInt(4294967295)); + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + let hi = Number(value >> BigInt(32) & BigInt(4294967295)); + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + return offset; +} + +function wrtBigUInt64BE( + buf: Buffer, + value: bigint, + offset: number, + min: bigint, + max: bigint) { + checkIntBI(value, min, max, buf, offset, 7); + let lo = Number(value & BigInt(4294967295)); + buf[offset + 7] = lo; + lo = lo >> 8; + buf[offset + 6] = lo; + lo = lo >> 8; + buf[offset + 5] = lo; + lo = lo >> 8; + buf[offset + 4] = lo; + let hi = Number(value >> BigInt(32) & BigInt(4294967295)); + buf[offset + 3] = hi; + hi = hi >> 8; + buf[offset + 2] = hi; + hi = hi >> 8; + buf[offset + 1] = hi; + hi = hi >> 8; + buf[offset] = hi; + return offset + 8; +} + +Buffer.prototype.writeBigUint64LE = + Buffer.prototype.writeBigUInt64LE = + function writeBigUInt64LE(this: Buffer, value: bigint, offset: number = 0) { + return wrtBigUInt64LE(this, value, offset, 0n, 0xffffffffffffffffn); + }; + +Buffer.prototype.writeBigUint64BE = + Buffer.prototype.writeBigUInt64BE = + function writeBigUInt64BE(this: Buffer, value: bigint, offset: number = 0) { + return wrtBigUInt64BE(this, value, offset, 0n, 0xffffffffffffffffn); + }; + +Buffer.prototype.writeIntLE = function writeIntLE( + value: number, + offset: number, + byteLength: number) { + switch(byteLength) { + case 1: return writeU_Int8(this, value, offset, -0x80, 0x7f); + case 2: return writeU_Int16LE(this, value, offset, -0x8000, 0x7fff); + case 3: return writeU_Int24LE(this, value, offset, -0x800000, 0x7fffff); + case 4: return writeU_Int32LE(this, value, offset, -0x80000000, 0x7fffffff); + case 5: return writeU_Int40LE(this, value, offset, -0x8000000000, 0x7fffffffff); + case 6: return writeU_Int48LE(this, value, offset, -0x800000000000, 0x7fffffffffff); + default: + boundsError(byteLength, 6, "byteLength"); + } +}; + +Buffer.prototype.writeIntBE = function writeIntBE( + value: number, + offset: number, + byteLength: number) { + switch(byteLength) { + case 1: return writeU_Int8(this, value, offset, -0x80, 0x7f); + case 2: return writeU_Int16BE(this, value, offset, -0x8000, 0x7fff); + case 3: return writeU_Int24BE(this, value, offset, -0x800000, 0x7fffff); + case 4: return writeU_Int32BE(this, value, offset, -0x80000000, 0x7fffffff); + case 5: return writeU_Int40BE(this, value, offset, -0x8000000000, 0x7fffffffff); + case 6: return writeU_Int48BE(this, value, offset, -0x800000000000, 0x7fffffffffff); + default: + boundsError(byteLength, 6, "byteLength"); + } +}; + +Buffer.prototype.writeInt8 = function writeInt8(value: number, offset: number = 0) { + return writeU_Int8(this, value, offset, -0x80, 0x7f); +}; + +Buffer.prototype.writeInt16LE = function writeInt16LE(value: number, offset: number = 0) { + return writeU_Int16LE(this, value, offset, -0x8000, 0x7fff); +}; + +Buffer.prototype.writeInt16BE = function writeInt16BE( + value: number, + offset: number = 0, +) { + return writeU_Int16BE(this, value, offset, -0x8000, 0x7fff); +}; + +Buffer.prototype.writeInt32LE = function writeInt32LE(value: number, offset: number = 0) { + return writeU_Int32LE(this, value, offset, -0x80000000, 0x7fffffff); +}; + +Buffer.prototype.writeInt32BE = function writeInt32BE(value: number, offset: number = 0) { + return writeU_Int32BE(this, value, offset, -0x80000000, 0x7fffffff); +}; + +Buffer.prototype.writeBigInt64LE = + function writeBigInt64LE(this: Buffer, value: bigint, offset: number = 0) { + return wrtBigUInt64LE(this, value, offset, -0x8000000000000000n, 0x7fffffffffffffffn); + }; + +Buffer.prototype.writeBigInt64BE = + function writeBigInt64BE(this: Buffer, value: bigint, offset: number = 0) { + return wrtBigUInt64BE(this, value, offset, -0x8000000000000000n, 0x7fffffffffffffffn); + }; + +Buffer.prototype.writeFloatLE = function writeFloatLE( + value: number, + offset: number, +) { + return bigEndian + ? writeFloatBackwards(this, value, offset) + : writeFloatForwards(this, value, offset); +}; + +Buffer.prototype.writeFloatBE = function writeFloatBE( + value: number, + offset: number, +) { + return bigEndian + ? writeFloatForwards(this, value, offset) + : writeFloatBackwards(this, value, offset); +}; + +Buffer.prototype.writeDoubleLE = function writeDoubleLE( + value: number, + offset: number, +) { + return bigEndian + ? writeDoubleBackwards(this, value, offset) + : writeDoubleForwards(this, value, offset); +}; + +Buffer.prototype.writeDoubleBE = function writeDoubleBE( + value: number, + offset: number, +) { + return bigEndian + ? writeDoubleForwards(this, value, offset) + : writeDoubleBackwards(this, value, offset); +}; + +Buffer.prototype.copy = function copy( + target: Buffer|Uint8Array, + targetStart?: number, + sourceStart?: number, + sourceEnd?: number, +) { + if (!isUint8Array(target)) { + throw new ERR_INVALID_ARG_TYPE("target", ["Buffer", "Uint8Array"], target); + } + + targetStart = toInteger(targetStart, 0); + if ((targetStart as number) < 0) { + throw new ERR_OUT_OF_RANGE("targetStart", ">= 0", targetStart); + } + + sourceStart = toInteger(sourceStart, 0); + if ((sourceStart as number) < 0) { + throw new ERR_OUT_OF_RANGE("sourceStart", ">= 0", sourceStart); + } + if ((sourceStart as number) >= MAX_UINT32) { + throw new ERR_OUT_OF_RANGE("sourceStart", `< ${MAX_UINT32}`, sourceStart); + } + + sourceEnd ??= this.length; + sourceEnd = toInteger(sourceEnd, 0); + if ((sourceEnd as number) < 0) { + throw new ERR_OUT_OF_RANGE("sourceEnd", ">= 0", sourceEnd); + } + if ((sourceEnd as number) >= MAX_UINT32) { + throw new ERR_OUT_OF_RANGE("sourceEnd", `< ${MAX_UINT32}`, sourceEnd); + } + + if ((targetStart as number) >= target.length) { + return 0; + } + + if ((sourceEnd as number) > 0 && (sourceEnd as number) < (sourceStart as number)) { + sourceEnd = sourceStart; + } + if (sourceEnd === sourceStart) { + return 0; + } + if (target.length === 0 || this.length === 0) { + return 0; + } + + if ((sourceEnd as number) > this.length) { + sourceEnd = this.length; + } + + if (target.length - (targetStart as number) < (sourceEnd as number) - (sourceStart as number)) { + sourceEnd = target.length - (targetStart as number) + (sourceStart as number); + } + + const len = (sourceEnd as number) - (sourceStart as number); + if (this === target) { + this.copyWithin(targetStart as number, sourceStart as number, sourceEnd as number); + } else { + const sub = this.subarray(sourceStart, sourceEnd); + target.set(sub, targetStart); + } + + return len; +}; + +Buffer.prototype.fill = function fill( + val: string|number|Buffer|Uint8Array, + start?: number|string, + end?: number, + encoding?: string) { + let normalizedEncoding : string | undefined; + if (typeof val === "string") { + if (typeof start === "string") { + encoding = start; + start = 0; + end = this.length; + } else if (typeof end === "string") { + encoding = end; + end = this.length; + } + normalizedEncoding = normalizeEncoding(encoding); + if (!Buffer.isEncoding(normalizedEncoding)) { + throw new ERR_UNKNOWN_ENCODING(encoding as string); + } + if (val.length === 1) { + const code = val.charCodeAt(0); + if (encoding === "utf8" && code < 128 || encoding === "latin1") { + val = code; + } + } + } + + if (start !== undefined) { + validateNumber(start, 'start'); + } + if (end !== undefined) { + validateNumber(end, 'end'); + } + + if ((end as number) < 0 || (end as number) > this.length) { + throw new ERR_OUT_OF_RANGE('end', `0 to ${this.length}`, end); + } + if ((start as number) < 0 || this.length < (start as number) || this.length < (end as number)) { + throw new ERR_OUT_OF_RANGE('start', '0 to end', start); + } + if ((end as number) <= (start as number)) { + return this; + } + start = (start as number) >>> 0; + end = end === void 0 ? this.length : end >>> 0; + + if (typeof val === "string") { + bufferUtil.fillImpl(this, + val as string, + start as number, + end as number, + normalizedEncoding); + return this; + } + + if (isArrayBufferView(val)) { + if ((val as ArrayBufferView).byteLength === 0) { + throw new ERR_INVALID_ARG_VALUE('value', 'zero-length'); + } + bufferUtil.fillImpl(this, val as ArrayBufferView, start as number, end as number); + return this; + } + + if (typeof val === "number") { + val = val & 255; + } else if (typeof val === "boolean") { + val = Number(val); + } + val ??= 0; + + Uint8Array.prototype.fill.call(this, val as number, start, end); + + return this; +}; + +function checkBounds( + buf: Buffer, + offset: number, + byteLength2: number) { + validateOffset(offset, "offset", 0, buf.length); + if (buf[offset] === undefined || buf[offset + byteLength2] === undefined) { + boundsError(offset, buf.length - (byteLength2 + 1)); + } +} + +function checkIntBI( + value: bigint|number, + min: bigint|number, + max: bigint|number, + buf: Buffer, + offset: number, + byteLength2: number) { + if (value > max || value < min) { + const n = typeof min === "bigint" ? "n" : ""; + let range; + if (byteLength2 > 3) { + if (min === 0 || min === BigInt(0)) { + range = `>= 0${n} and < 2${n} ** ${(byteLength2 + 1) * 8}${n}`; + } else { + range = `>= -(2${n} ** ${(byteLength2 + 1) * 8 - 1}${n}) and < 2 ** ${ + (byteLength2 + 1) * 8 - 1 + }${n}`; + } + } else { + range = `>= ${min}${n} and <= ${max}${n}`; + } + throw new ERR_OUT_OF_RANGE("value", range, value); + } + checkBounds(buf, offset, byteLength2); +} + +export function isInstance(obj: unknown, type: Function) { + return obj instanceof type || + obj != null && obj.constructor != null && + obj.constructor.name != null && obj.constructor.name === type.name; +} + + +function readUInt48LE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 5]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 6); + } + + return first + + buf[++offset]! * 2 ** 8 + + buf[++offset]! * 2 ** 16 + + buf[++offset]! * 2 ** 24 + + (buf[++offset]! + last * 2 ** 8) * 2 ** 32; +} + +function readUInt40LE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 4]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 5); + } + + return first + + buf[++offset]! * 2 ** 8 + + buf[++offset]! * 2 ** 16 + + buf[++offset]! * 2 ** 24 + + last * 2 ** 32; +} + +function readUInt24LE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 2]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 3); + } + + return first + buf[++offset]! * 2 ** 8 + last * 2 ** 16; +} + +function readUInt48BE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 5]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 6); + } + + return (first * 2 ** 8 + buf[++offset]!) * 2 ** 32 + + buf[++offset]! * 2 ** 24 + + buf[++offset]! * 2 ** 16 + + buf[++offset]! * 2 ** 8 + + last; +} + +function readUInt40BE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 4]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 5); + } + + return first * 2 ** 32 + + buf[++offset]! * 2 ** 24 + + buf[++offset]! * 2 ** 16 + + buf[++offset]! * 2 ** 8 + + last; +} + +function readUInt24BE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 2]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 3); + } + + return first * 2 ** 16 + buf[++offset]! * 2 ** 8 + last; +} + +function readUInt16BE(this: Buffer, offset: number = 0) { + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 1]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 2); + } + + return first * 2 ** 8 + last; +} + +function readUInt32BE(this: Buffer, offset: number = 0) { + validateOffset(offset, "offset", 0, this.length); + const first = this[offset]; + const last = this[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 4); + } + + return first * 2 ** 24 + + this[++offset]! * 2 ** 16 + + this[++offset]! * 2 ** 8 + + last; +} + +function readDoubleBackwards(buffer: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buffer.length); + const first = buffer[offset]; + const last = buffer[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, buffer.length - 8); + } + + uInt8Float64Array[7] = first; + uInt8Float64Array[6] = buffer[++offset]!; + uInt8Float64Array[5] = buffer[++offset]!; + uInt8Float64Array[4] = buffer[++offset]!; + uInt8Float64Array[3] = buffer[++offset]!; + uInt8Float64Array[2] = buffer[++offset]!; + uInt8Float64Array[1] = buffer[++offset]!; + uInt8Float64Array[0] = last; + return float64Array[0]; +} + +function readDoubleForwards(buffer: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buffer.length); + const first = buffer[offset]; + const last = buffer[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, buffer.length - 8); + } + + uInt8Float64Array[0] = first; + uInt8Float64Array[1] = buffer[++offset]!; + uInt8Float64Array[2] = buffer[++offset]!; + uInt8Float64Array[3] = buffer[++offset]!; + uInt8Float64Array[4] = buffer[++offset]!; + uInt8Float64Array[5] = buffer[++offset]!; + uInt8Float64Array[6] = buffer[++offset]!; + uInt8Float64Array[7] = last; + return float64Array[0]; +} + +function writeDoubleForwards(buffer: Buffer|Uint8Array, val: number, offset: number = 0) { + val = +val; + checkBounds(buffer as any, offset, 7); + + float64Array[0] = val; + buffer[offset++] = uInt8Float64Array[0]!; + buffer[offset++] = uInt8Float64Array[1]!; + buffer[offset++] = uInt8Float64Array[2]!; + buffer[offset++] = uInt8Float64Array[3]!; + buffer[offset++] = uInt8Float64Array[4]!; + buffer[offset++] = uInt8Float64Array[5]!; + buffer[offset++] = uInt8Float64Array[6]!; + buffer[offset++] = uInt8Float64Array[7]!; + return offset; +} + +function writeDoubleBackwards(buffer: Buffer|Uint8Array, val: number, offset: number = 0) { + val = +val; + checkBounds(buffer as any, offset, 7); + + float64Array[0] = val; + buffer[offset++] = uInt8Float64Array[7]!; + buffer[offset++] = uInt8Float64Array[6]!; + buffer[offset++] = uInt8Float64Array[5]!; + buffer[offset++] = uInt8Float64Array[4]!; + buffer[offset++] = uInt8Float64Array[3]!; + buffer[offset++] = uInt8Float64Array[2]!; + buffer[offset++] = uInt8Float64Array[1]!; + buffer[offset++] = uInt8Float64Array[0]!; + return offset; +} + +function readFloatBackwards(buffer: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buffer.length); + const first = buffer[offset]; + const last = buffer[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, buffer.length - 4); + } + + uInt8Float32Array[3] = first; + uInt8Float32Array[2] = buffer[++offset]!; + uInt8Float32Array[1] = buffer[++offset]!; + uInt8Float32Array[0] = last; + return float32Array[0]; +} + +function readFloatForwards(buffer: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buffer.length); + const first = buffer[offset]; + const last = buffer[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, buffer.length - 4); + } + + uInt8Float32Array[0] = first; + uInt8Float32Array[1] = buffer[++offset]!; + uInt8Float32Array[2] = buffer[++offset]!; + uInt8Float32Array[3] = last; + return float32Array[0]; +} + +function writeFloatForwards(buffer: Buffer|Uint8Array, val: number, offset: number = 0) { + val = +val; + checkBounds(buffer as any, offset, 3); + + float32Array[0] = val; + buffer[offset++] = uInt8Float32Array[0]!; + buffer[offset++] = uInt8Float32Array[1]!; + buffer[offset++] = uInt8Float32Array[2]!; + buffer[offset++] = uInt8Float32Array[3]!; + return offset; +} + +function writeFloatBackwards(buffer: Buffer|Uint8Array, val: number, offset: number = 0) { + val = +val; + checkBounds(buffer as any, offset, 3); + + float32Array[0] = val; + buffer[offset++] = uInt8Float32Array[3]!; + buffer[offset++] = uInt8Float32Array[2]!; + buffer[offset++] = uInt8Float32Array[1]!; + buffer[offset++] = uInt8Float32Array[0]!; + return offset; +} + +function readInt24LE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 2]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 3); + } + + const val = first + buf[++offset]! * 2 ** 8 + last * 2 ** 16; + return val | (val & 2 ** 23) * 0x1fe; +} + +function readInt40LE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 4]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 5); + } + + return (last | (last & 2 ** 7) * 0x1fffffe) * 2 ** 32 + + first + + buf[++offset]! * 2 ** 8 + + buf[++offset]! * 2 ** 16 + + buf[++offset]! * 2 ** 24; +} + +function readInt48LE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 5]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 6); + } + + const val = buf[offset + 4]! + last * 2 ** 8; + return (val | (val & 2 ** 15) * 0x1fffe) * 2 ** 32 + + first + + buf[++offset]! * 2 ** 8 + + buf[++offset]! * 2 ** 16 + + buf[++offset]! * 2 ** 24; +} + +function readInt24BE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 2]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 3); + } + + const val = first * 2 ** 16 + buf[++offset]! * 2 ** 8 + last; + return val | (val & 2 ** 23) * 0x1fe; +} + +function readInt48BE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 5]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 6); + } + + const val = buf[++offset]! + first * 2 ** 8; + return (val | (val & 2 ** 15) * 0x1fffe) * 2 ** 32 + + buf[++offset]! * 2 ** 24 + + buf[++offset]! * 2 ** 16 + + buf[++offset]! * 2 ** 8 + + last; +} + +function readInt40BE(buf: Buffer|Uint8Array, offset: number = 0) { + validateOffset(offset, "offset", 0, buf.length); + const first = buf[offset]; + const last = buf[offset + 4]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 5); + } + + return (first | (first & 2 ** 7) * 0x1fffffe) * 2 ** 32 + + buf[++offset]! * 2 ** 24 + + buf[++offset]! * 2 ** 16 + + buf[++offset]! * 2 ** 8 + + last; +} + +function boundsError(value: number, length: number, type?: string) : never { + if (Math.floor(value) !== value) { + throw new ERR_OUT_OF_RANGE(type || "offset", "an integer", value); + } + + if (length < 0) { + throw new ERR_BUFFER_OUT_OF_BOUNDS(); + } + + throw new ERR_OUT_OF_RANGE( + type || "offset", + `>= ${type ? 1 : 0} and <= ${length}`, + value, + ); +} + +function validateNumber(value: unknown, name: string) { + if (typeof value !== "number") { + throw new ERR_INVALID_ARG_TYPE(name, "number", value); + } +} + +function checkInt( + value: number|bigint, + min: number|bigint, + max: number|bigint, + buf: Buffer, + offset: number, + byteLength: number) { + if (value > max || value < min) { + const n = typeof min === "bigint" ? "n" : ""; + let range; + if (byteLength > 3) { + if (min === 0 || min === 0n) { + range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}`; + } else { + range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and ` + + `< 2${n} ** ${(byteLength + 1) * 8 - 1}${n}`; + } + } else { + range = `>= ${min}${n} and <= ${max}${n}`; + } + throw new ERR_OUT_OF_RANGE("value", range, value); + } + checkBounds(buf, offset, byteLength); +} + +function toInteger(n: number|undefined, defaultVal: number) { + if (n === undefined) n = 0; + n = +(n as number); + if ( + !Number.isNaN(n) && + n >= Number.MIN_SAFE_INTEGER && + n <= Number.MAX_SAFE_INTEGER + ) { + return ((n % 1) === 0 ? n : Math.floor(n)); + } + return defaultVal; +} + +function writeU_Int8( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + if (value > max || value < min) { + throw new ERR_OUT_OF_RANGE("value", `>= ${min} and <= ${max}`, value); + } + if (buf[offset] === undefined) { + boundsError(offset, buf.length - 1); + } + + buf[offset] = value; + return offset + 1; +} + +function writeU_Int16BE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 1); + + buf[offset++] = value >>> 8; + buf[offset++] = value; + return offset; +} + +function _writeUInt32LE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 3); + + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + return offset; +} + +function writeU_Int16LE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 1); + + buf[offset++] = value; + buf[offset++] = value >>> 8; + return offset; +} + +function _writeUInt32BE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 3); + + buf[offset + 3] = value; + value = value >>> 8; + buf[offset + 2] = value; + value = value >>> 8; + buf[offset + 1] = value; + value = value >>> 8; + buf[offset] = value; + return offset + 4; +} + +function writeU_Int48BE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 5); + + const newVal = Math.floor(value * 2 ** -32); + buf[offset++] = newVal >>> 8; + buf[offset++] = newVal; + buf[offset + 3] = value; + value = value >>> 8; + buf[offset + 2] = value; + value = value >>> 8; + buf[offset + 1] = value; + value = value >>> 8; + buf[offset] = value; + return offset + 4; +} + +function writeU_Int40BE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 4); + + buf[offset++] = Math.floor(value * 2 ** -32); + buf[offset + 3] = value; + value = value >>> 8; + buf[offset + 2] = value; + value = value >>> 8; + buf[offset + 1] = value; + value = value >>> 8; + buf[offset] = value; + return offset + 4; +} + +function writeU_Int32BE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 3); + + buf[offset + 3] = value; + value = value >>> 8; + buf[offset + 2] = value; + value = value >>> 8; + buf[offset + 1] = value; + value = value >>> 8; + buf[offset] = value; + return offset + 4; +} + +function writeU_Int24BE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 2); + + buf[offset + 2] = value; + value = value >>> 8; + buf[offset + 1] = value; + value = value >>> 8; + buf[offset] = value; + return offset + 3; +} + +function validateOffset( + value: number, + name: string, + min: number = 0, + max: number = Number.MAX_SAFE_INTEGER, +) { + if (typeof value !== "number") { + throw new ERR_INVALID_ARG_TYPE(name, "number", value); + } + if (!Number.isInteger(value)) { + throw new ERR_OUT_OF_RANGE(name, "an integer", value); + } + if (value < min || value > max) { + throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } +} + +function writeU_Int48LE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 5); + + const newVal = Math.floor(value * 2 ** -32); + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + buf[offset++] = newVal; + buf[offset++] = newVal >>> 8; + return offset; +} + +function writeU_Int40LE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 4); + + const newVal = value; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + buf[offset++] = Math.floor(newVal * 2 ** -32); + return offset; +} + +function writeU_Int32LE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 3); + + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + return offset; +} + +function writeU_Int24LE( + buf: Buffer, + value: number, + offset: number, + min: number, + max: number) { + value = +value; + validateOffset(offset, "offset", 0, buf.length); + checkInt(value, min, max, buf, offset, 2); + + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + return offset; +} + +export default { + Buffer, + constants, + kMaxLength, + kStringMaxLength, + SlowBuffer, +}; diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_errors.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_errors.ts new file mode 100644 index 000000000..5a6b40f0c --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_errors.ts @@ -0,0 +1,492 @@ +import { inspect } from "./internal_inspect"; + +const classRegExp = /^([A-Z][a-z0-9]*)+$/; + +const kTypes = [ + "string", + "function", + "number", + "object", + "Function", + "Object", + "boolean", + "bigint", + "symbol", +]; + +export class NodeErrorAbstraction extends Error { + code: string; + + constructor(name: string, code: string, message: string) { + super(message); + this.code = code; + this.name = name; + //This number changes depending on the name of this class + //20 characters as of now + (this as any).stack = this.stack && `${name} [${this.code}]${this.stack.slice(20)}`; + } + + override toString() { + return `${this.name} [${this.code}]: ${this.message}`; + } +} + +export class NodeError extends NodeErrorAbstraction { + constructor(code: string, message: string) { + super(Error.prototype.name, code, message); + } +} + +export class NodeRangeError extends NodeErrorAbstraction { + constructor(code: string, message: string) { + super(RangeError.prototype.name, code, message); + Object.setPrototypeOf(this, RangeError.prototype); + this.toString = function () { + return `${this.name} [${this.code}]: ${this.message}`; + }; + } +} + +export class NodeTypeError extends NodeErrorAbstraction implements TypeError { + constructor(code: string, message: string) { + super(TypeError.prototype.name, code, message); + Object.setPrototypeOf(this, TypeError.prototype); + this.toString = function () { + return `${this.name} [${this.code}]: ${this.message}`; + }; + } +} + +function createInvalidArgType( + name: string, + expected: string | string[], +): string { + // https://github.com/nodejs/node/blob/f3eb224/lib/internal/errors.js#L1037-L1087 + expected = Array.isArray(expected) ? expected : [expected]; + let msg = "The "; + if (name.endsWith(" argument")) { + // For cases like 'first argument' + msg += `${name} `; + } else { + const type = name.includes(".") ? "property" : "argument"; + msg += `"${name}" ${type} `; + } + msg += "must be "; + + const types = []; + const instances = []; + const other = []; + for (const value of expected) { + if (kTypes.includes(value)) { + types.push(value.toLocaleLowerCase()); + } else if (classRegExp.test(value)) { + instances.push(value); + } else { + other.push(value); + } + } + + // Special handle `object` in case other instances are allowed to outline + // the differences between each other. + if (instances.length > 0) { + const pos = types.indexOf("object"); + if (pos !== -1) { + types.splice(pos, 1); + instances.push("Object"); + } + } + + if (types.length > 0) { + if (types.length > 2) { + const last = types.pop(); + msg += `one of type ${types.join(", ")}, or ${last}`; + } else if (types.length === 2) { + msg += `one of type ${types[0]} or ${types[1]}`; + } else { + msg += `of type ${types[0]}`; + } + if (instances.length > 0 || other.length > 0) { + msg += " or "; + } + } + + if (instances.length > 0) { + if (instances.length > 2) { + const last = instances.pop(); + msg += `an instance of ${instances.join(", ")}, or ${last}`; + } else { + msg += `an instance of ${instances[0]}`; + if (instances.length === 2) { + msg += ` or ${instances[1]}`; + } + } + if (other.length > 0) { + msg += " or "; + } + } + + if (other.length > 0) { + if (other.length > 2) { + const last = other.pop(); + msg += `one of ${other.join(", ")}, or ${last}`; + } else if (other.length === 2) { + msg += `one of ${other[0]} or ${other[1]}`; + } else { + // @ts-ignore + if (other[0].toLowerCase() !== other[0]) { + msg += "an "; + } + msg += `${other[0]}`; + } + } + + return msg; +} + +function invalidArgTypeHelper(input: any) { + if (input == null) { + return ` Received ${input}`; + } + if (typeof input === "function" && input.name) { + return ` Received function ${input.name}`; + } + if (typeof input === "object") { + if (input.constructor && input.constructor.name) { + return ` Received an instance of ${input.constructor.name}`; + } + return ` Received ${inspect(input, { depth: -1 })}`; + } + let inspected = inspect(input, { colors: false }); + if (inspected.length > 25) { + inspected = `${inspected.slice(0, 25)}...`; + } + return ` Received type ${typeof input} (${inspected})`; +} + +function addNumericalSeparator(val: string) { + let res = ""; + let i = val.length; + const start = val[0] === "-" ? 1 : 0; + for (; i >= start + 4; i -= 3) { + res = `_${val.slice(i - 3, i)}${res}`; + } + return `${val.slice(0, i)}${res}`; +} + +export class ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY extends NodeError { + constructor() { + super("ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY", "Public key is not valid for specified curve"); + } +} + +export class ERR_CRYPTO_HASH_FINALIZED extends NodeError { + constructor() { + super("ERR_CRYPTO_HASH_FINALIZED", "Digest already called"); + } +} + +export class ERR_CRYPTO_HASH_UPDATE_FAILED extends NodeError { + constructor() { + super("ERR_CRYPTO_HASH_UPDATE_FAILED", "Hash update failed"); + } +} + +export class ERR_CRYPTO_INCOMPATIBLE_KEY extends NodeError { + constructor(name: string, msg: string) { + super("ERR_CRYPTO_INCOMPATIBLE_KEY", `Incompatible ${name}: ${msg}`); + } +} + +export class ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE extends NodeError { + constructor(actual: string, expected: string) { + super("ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE", `Invalid key object type ${actual}, expected ${expected}.`); + } +} + +export class ERR_INVALID_ARG_TYPE_RANGE extends NodeRangeError { + constructor(name: string, expected: string | string[], actual: unknown) { + const msg = createInvalidArgType(name, expected); + + super("ERR_INVALID_ARG_TYPE", `${msg}.${invalidArgTypeHelper(actual)}`); + } +} + +export class ERR_INVALID_ARG_TYPE extends NodeTypeError { + constructor(name: string, expected: string | string[], actual: unknown) { + const msg = createInvalidArgType(name, expected); + + super("ERR_INVALID_ARG_TYPE", `${msg}.${invalidArgTypeHelper(actual)}`); + } + + static RangeError = ERR_INVALID_ARG_TYPE_RANGE; +} + +export class ERR_INVALID_ARG_VALUE_RANGE extends NodeRangeError { + constructor(name: string, value: unknown, reason: string = "is invalid") { + const type = name.includes(".") ? "property" : "argument"; + const inspected = inspect(value); + + super( + "ERR_INVALID_ARG_VALUE", + `The ${type} '${name}' ${reason}. Received ${inspected}`, + ); + } +} + +export class ERR_INVALID_ARG_VALUE extends NodeTypeError { + constructor(name: string, value: unknown, reason: string = "is invalid") { + const type = name.includes(".") ? "property" : "argument"; + const inspected = inspect(value); + + super( + "ERR_INVALID_ARG_VALUE", + `The ${type} '${name}' ${reason}. Received ${inspected}`, + ); + } + + static RangeError = ERR_INVALID_ARG_VALUE_RANGE; +} + +export class ERR_OUT_OF_RANGE extends RangeError { + code = "ERR_OUT_OF_RANGE"; + + constructor( + str: string, + range: string, + input: unknown, + replaceDefaultBoolean = false) { + // TODO(later): Implement internal assert? + // assert(range, 'Missing "range" argument'); + let msg = replaceDefaultBoolean + ? str + : `The value of "${str}" is out of range.`; + let received; + if (Number.isInteger(input) && Math.abs(input as number) > 2 ** 32) { + received = addNumericalSeparator(String(input)); + } else if (typeof input === "bigint") { + received = String(input); + if (input > 2n ** 32n || input < -(2n ** 32n)) { + received = addNumericalSeparator(received); + } + received += "n"; + } else { + received = inspect(input); + } + msg += ` It must be ${range}. Received ${received}`; + + super(msg); + + const { name } = this; + // Add the error code to the name to include it in the stack trace. + this.name = `${name} [${this.code}]`; + // Access the stack to generate the error message including the error code from the name. + this.stack; + // Reset the name to the actual name. + this.name = name; + } +} + +export class ERR_UNHANDLED_ERROR extends NodeError { + constructor(x: string) { + super("ERR_UNHANDLED_ERROR", `Unhandled error. (${x})`); + } +} + +export class ERR_INVALID_THIS extends NodeTypeError { + constructor(x: string) { + super("ERR_INVALID_THIS", `Value of "this" must be of type ${x}`); + } +} + +export class ERR_BUFFER_OUT_OF_BOUNDS extends NodeRangeError { + constructor(name?: string) { + super( + "ERR_BUFFER_OUT_OF_BOUNDS", + name + ? `"${name}" is outside of buffer bounds` + : "Attempt to access memory outside buffer bounds", + ); + } +} + +export class ERR_INVALID_BUFFER_SIZE extends NodeRangeError { + constructor(size: number) { + super( + "ERR_INVALID_BUFFER_SIZE", + `Buffer size must be a multiple of ${size}-bits`, + ); + } +} + +export class ERR_UNKNOWN_ENCODING extends NodeTypeError { + constructor(x: string) { + super("ERR_UNKNOWN_ENCODING", `Unknown encoding: ${x}`); + } +} + +export class ERR_STREAM_PREMATURE_CLOSE extends NodeTypeError { + constructor() { + super("ERR_STREAM_PREMATURE_CLOSE", "Premature close"); + } +} + +export class AbortError extends Error { + code: string; + + constructor(message = "The operation was aborted", options?: ErrorOptions) { + if (options !== undefined && typeof options !== "object") { + throw new ERR_INVALID_ARG_TYPE("options", "Object", options); + } + super(message, options); + this.code = "ABORT_ERR"; + this.name = "AbortError"; + } +} + +function determineSpecificType(value: any) { + if (value == null) { + return "" + value; + } + if (typeof value === "function" && value.name) { + return `function ${value.name}`; + } + if (typeof value === "object") { + if (value.constructor?.name) { + return `an instance of ${value.constructor.name}`; + } + return `${inspect(value, { depth: -1 })}`; + } + let inspected = inspect(value, { colors: false }); + if (inspected.length > 28) inspected = `${inspected.slice(0, 25)}...`; + + return `type ${typeof value} (${inspected})`; +} + +export class ERR_AMBIGUOUS_ARGUMENT extends NodeTypeError { + constructor(x: string, y: string) { + super("ERR_AMBIGUOUS_ARGUMENT", `The "${x}" argument is ambiguous. ${y}`); + } +} + +export class ERR_INVALID_RETURN_VALUE extends NodeTypeError { + constructor(input: string, name: string, value: unknown) { + super( + "ERR_INVALID_RETURN_VALUE", + `Expected ${input} to be returned from the "${name}" function but got ${ + determineSpecificType( + value, + ) + }.`, + ); + } +} + +export class ERR_MULTIPLE_CALLBACK extends NodeError { + constructor() { + super("ERR_MULTIPLE_CALLBACK", "Callback called multiple times"); + } +} + +export class ERR_MISSING_ARGS extends NodeTypeError { + constructor(...args: (string | string[])[]) { + let msg = "The "; + + const len = args.length; + + const wrap = (a: unknown) => `"${a}"`; + + args = args.map((a) => + Array.isArray(a) ? a.map(wrap).join(" or ") : wrap(a) + ); + + switch (len) { + case 1: + msg += `${args[0]} argument`; + break; + case 2: + msg += `${args[0]} and ${args[1]} arguments`; + break; + default: + msg += args.slice(0, len - 1).join(", "); + msg += `, and ${args[len - 1]} arguments`; + break; + } + + super("ERR_MISSING_ARGS", `${msg} must be specified`); + } +} + +export class ERR_FALSY_VALUE_REJECTION extends NodeError { + reason: string; + constructor(reason: string) { + super("ERR_FALSY_VALUE_REJECTION", "Promise was rejected with falsy value"); + this.reason = reason; + } +} + +export class ERR_METHOD_NOT_IMPLEMENTED extends NodeError { + constructor(name: string|symbol) { + if (typeof name === 'symbol') { + name = (name as symbol).description!; + } + super("ERR_METHOD_NOT_IMPLEMENTED", `The ${name} method is not implemented`); + } +} + +export class ERR_STREAM_CANNOT_PIPE extends NodeError { + constructor() { + super("ERR_STREAM_CANNOT_PIPE", "Cannot pipe, not readable"); + } +} +export class ERR_STREAM_DESTROYED extends NodeError { + constructor(name: string|symbol) { + if (typeof name === 'symbol') { + name = (name as symbol).description!; + } + super("ERR_STREAM_DESTROYED", `Cannot call ${name} after a stream was destroyed`); + } +} +export class ERR_STREAM_ALREADY_FINISHED extends NodeError { + constructor(name: string|symbol) { + if (typeof name === 'symbol') { + name = (name as symbol).description!; + } + super("ERR_STREAM_ALREADY_FINISHED", `Cannot call ${name} after a stream was finished`); + } +} +export class ERR_STREAM_NULL_VALUES extends NodeTypeError { + constructor() { + super("ERR_STREAM_NULL_VALUES", "May not write null values to stream"); + } +} +export class ERR_STREAM_WRITE_AFTER_END extends NodeError { + constructor() { + super("ERR_STREAM_WRITE_AFTER_END", "write after end"); + } +} + +export class ERR_STREAM_PUSH_AFTER_EOF extends NodeError { + constructor() { + super("ERR_STREAM_PUSH_AFTER_EOF", "stream.push() after EOF"); + } +} + +export class ERR_STREAM_UNSHIFT_AFTER_END_EVENT extends NodeError { + constructor() { + super("ERR_STREAM_UNSHIFT_AFTER_END_EVENT", "stream.unshift() after end event"); + } +} + +export function aggregateTwoErrors(innerError: any, outerError: any) { + if (innerError && outerError && innerError !== outerError) { + if (Array.isArray(outerError.errors)) { + // If `outerError` is already an `AggregateError`. + outerError.errors.push(innerError); + return outerError; + } + const err = new AggregateError([outerError, innerError], outerError.message); + (err as any).code = outerError.code; + return err; + } + return innerError || outerError +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_inspect.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_inspect.ts new file mode 100644 index 000000000..99ac8ecbd --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_inspect.ts @@ -0,0 +1,2454 @@ + +import * as internal from "./util"; + +import { Buffer } from "./internal_buffer"; +import { + isAsyncFunction, + isGeneratorFunction, + isAnyArrayBuffer, + isArrayBuffer, + isArgumentsObject, + isBoxedPrimitive, + isDataView, + isMap, + isMapIterator, + isModuleNamespaceObject, + isNativeError, + isPromise, + isSet, + isSetIterator, + isWeakMap, + isWeakSet, + isRegExp, + isDate, + isTypedArray, + isStringObject, + isNumberObject, + isBooleanObject, + isBigIntObject, +} from "./internal_types"; +// import { ALL_PROPERTIES, ONLY_ENUMERABLE, getOwnNonIndexProperties } from "node-internal:internal_utils"; +import { validateObject, validateString } from "./validators"; + +// // Simplified assertions to avoid `Assertions require every name in the call target to be +// declared with an explicit type` TypeScript error +function assert(value: boolean, message = "Assertion failed"): asserts value { + if (!value) throw new Error(message); +} +assert.fail = function(message = "Assertion failed"): never { + throw new Error(message); +} + +function isError(e: unknown): e is Error { + // An error could be an instance of Error while not being a native error + // or could be from a different realm and not be instance of Error but still + // be a native error. + return isNativeError(e) || e instanceof Error; +} + +const typedArrayPrototype = Object.getPrototypeOf(Uint8Array).prototype; +const typedArrayPrototypeLength: (this: internal.TypedArray) => number = Object.getOwnPropertyDescriptor(typedArrayPrototype, "length")!.get!; +const typedArrayPrototypeToStringTag: (this: internal.TypedArray) => string = Object.getOwnPropertyDescriptor(typedArrayPrototype, Symbol.toStringTag)!.get!; + +const setPrototypeSize: (this: Set) => number = Object.getOwnPropertyDescriptor(Set.prototype, "size")!.get!; +const mapPrototypeSize: (this: Map) => number = Object.getOwnPropertyDescriptor(Map.prototype, "size")!.get!; + +let maxStack_ErrorName: string; +let maxStack_ErrorMessage: string; +function isStackOverflowError(err: Error): boolean { + if (maxStack_ErrorMessage === undefined) { + try { + function overflowStack() { overflowStack(); } + overflowStack(); + } catch (err) { + assert(isError(err)); + maxStack_ErrorMessage = err.message; + maxStack_ErrorName = err.name; + } + } + + return err && err.name === maxStack_ErrorName && + err.message === maxStack_ErrorMessage; +} + +export const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom'); + +const colorRegExp = /\u001b\[\d\d?m/g; + +function removeColors(str: string): string { + return str.replace(colorRegExp, ''); +} + +export interface InspectOptions { + /** + * If `true`, object's non-enumerable symbols and properties are included in the formatted result. + * `WeakMap` and `WeakSet` entries are also included as well as user defined prototype properties (excluding method properties). + * @default false + */ + showHidden?: boolean; + /** + * Specifies the number of times to recurse while formatting object. + * This is useful for inspecting large objects. + * To recurse up to the maximum call stack size pass `Infinity` or `null`. + * @default 2 + */ + depth?: number | null; + /** + * If `true`, the output is styled with ANSI color codes. Colors are customizable. + */ + colors?: boolean; + /** + * If `false`, `[util.inspect.custom](depth, opts, inspect)` functions are not invoked. + * @default true + */ + customInspect?: boolean; + /** + * If `true`, `Proxy` inspection includes the target and handler objects. + * @default false + */ + showProxy?: boolean; + /** + * Specifies the maximum number of `Array`, `TypedArray`, `WeakMap`, and `WeakSet` elements + * to include when formatting. Set to `null` or `Infinity` to show all elements. + * Set to `0` or negative to show no elements. + * @default 100 + */ + maxArrayLength?: number | null; + /** + * Specifies the maximum number of characters to + * include when formatting. Set to `null` or `Infinity` to show all elements. + * Set to `0` or negative to show no characters. + * @default 10000 + */ + maxStringLength?: number | null; + /** + * The length at which input values are split across multiple lines. + * Set to `Infinity` to format the input as a single line + * (in combination with `compact` set to `true` or any number >= `1`). + * @default 80 + */ + breakLength?: number; + /** + * Setting this to `false` causes each object key + * to be displayed on a new line. It will also add new lines to text that is + * longer than `breakLength`. If set to a number, the most `n` inner elements + * are united on a single line as long as all properties fit into + * `breakLength`. Short array elements are also grouped together. Note that no + * text will be reduced below 16 characters, no matter the `breakLength` size. + * For more information, see the example below. + * @default true + */ + compact?: boolean | number; + /** + * If set to `true` or a function, all properties of an object, and `Set` and `Map` + * entries are sorted in the resulting string. + * If set to `true` the default sort is used. + * If set to a function, it is used as a compare function. + */ + sorted?: boolean | ((a: string, b: string) => number); + /** + * If set to `true`, getters are going to be + * inspected as well. If set to `'get'` only getters without setter are going + * to be inspected. If set to `'set'` only getters having a corresponding + * setter are going to be inspected. This might cause side effects depending on + * the getter function. + * @default false + */ + getters?: "get" | "set" | boolean; + /** + * If set to `true`, an underscore is used to separate every three digits in all bigints and numbers. + * @default false + */ + numericSeparator?: boolean; +} +export type Style = + | "special" + | "number" + | "bigint" + | "boolean" + | "undefined" + | "null" + | "string" + | "symbol" + | "date" + | "regexp" + | "module" + | "name"; +export type CustomInspectFunction = (depth: number, options: InspectOptionsStylized) => any; // TODO: , inspect: inspect +export interface InspectOptionsStylized extends InspectOptions { + stylize(text: string, styleType: Style): string; +} + +const builtInObjects = new Set( + Object.getOwnPropertyNames(globalThis).filter( + (e) => /^[A-Z][a-zA-Z0-9]+$/.exec(e) !== null) +); + +// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot +const isUndetectableObject = (v: unknown): boolean => typeof v === 'undefined' && v !== undefined; + +// These options must stay in sync with `getUserOptions`. So if any option will +// be added or removed, `getUserOptions` must also be updated accordingly. +export const inspectDefaultOptions = Object.seal({ + showHidden: false, + depth: 2, + colors: false, + customInspect: true, + showProxy: false, + maxArrayLength: 100, + maxStringLength: 10000, + breakLength: 80, + compact: 3, + sorted: false, + getters: false, + numericSeparator: false, +} as const); + +const kObjectType = 0; +const kArrayType = 1; +const kArrayExtrasType = 2; + +const strEscapeSequencesRegExp = /[\x00-\x1f\x27\x5c\x7f-\x9f]|[\ud800-\udbff](?![\udc00-\udfff])|(?<~]))'; +const ansi = new RegExp(ansiPattern, 'g'); + +interface Context extends Required { + maxArrayLength: number; + maxStringLength: number; + budget: Record; + indentationLvl: number; + seen: unknown[]; + currentDepth: number; + userOptions?: InspectOptions; + circular?: Map; +} + +function getUserOptions(ctx: Context, isCrossContext: boolean): InspectOptionsStylized { + const ret: InspectOptionsStylized = { + stylize: ctx.stylize, + showHidden: ctx.showHidden, + depth: ctx.depth, + colors: ctx.colors, + customInspect: ctx.customInspect, + showProxy: ctx.showProxy, + maxArrayLength: ctx.maxArrayLength, + maxStringLength: ctx.maxStringLength, + breakLength: ctx.breakLength, + compact: ctx.compact, + sorted: ctx.sorted, + getters: ctx.getters, + numericSeparator: ctx.numericSeparator, + ...ctx.userOptions, + }; + + // Typically, the target value will be an instance of `Object`. If that is + // *not* the case, the object may come from another vm.Context, and we want + // to avoid passing it objects from this Context in that case, so we remove + // the prototype from the returned object itself + the `stylize()` function, + // and remove all other non-primitives, including non-primitive user options. + if (isCrossContext) { + Object.setPrototypeOf(ret, null); + for (const key of (Object.keys(ret) as (keyof InspectOptionsStylized)[])) { + if ((typeof ret[key] === 'object' || typeof ret[key] === 'function') && + ret[key] !== null) { + delete ret[key]; + } + } + ret.stylize = Object.setPrototypeOf((value: string, flavour: Style) => { + let stylized; + try { + stylized = `${ctx.stylize(value, flavour)}`; + } catch { + // Continue regardless of error. + } + + if (typeof stylized !== 'string') return value; + // `stylized` is a string as it should be, which is safe to pass along. + return stylized; + }, null); + } + + return ret; +} + +/** + * Echos the value of any input. Tries to print the value out + * in the best way possible given the different types. + * @param {any} value The value to print out. + * @param {object} opts Optional options object that alters the output. + */ +/* Legacy: value, showHidden, depth, colors */ +export function inspect(value: unknown, showHidden?: boolean, depth?: number | null, color?: boolean): string; +export function inspect(value: unknown, opts?: InspectOptions): string; +export function inspect(value: unknown, opts?: Partial | boolean): string { + // Default options + const ctx: Context = { + budget: {}, + indentationLvl: 0, + seen: [], + currentDepth: 0, + stylize: stylizeNoColor, + showHidden: inspectDefaultOptions.showHidden, + depth: inspectDefaultOptions.depth, + colors: inspectDefaultOptions.colors, + customInspect: inspectDefaultOptions.customInspect, + showProxy: inspectDefaultOptions.showProxy, + maxArrayLength: inspectDefaultOptions.maxArrayLength, + maxStringLength: inspectDefaultOptions.maxStringLength, + breakLength: inspectDefaultOptions.breakLength, + compact: inspectDefaultOptions.compact, + sorted: inspectDefaultOptions.sorted, + getters: inspectDefaultOptions.getters, + numericSeparator: inspectDefaultOptions.numericSeparator, + }; + if (arguments.length > 1) { + // Legacy... + if (arguments.length > 2) { + if (arguments[2] !== undefined) { + ctx.depth = arguments[2]; + } + if (arguments.length > 3 && arguments[3] !== undefined) { + ctx.colors = arguments[3]; + } + } + // Set user-specified options + if (typeof opts === 'boolean') { + ctx.showHidden = opts; + } else if (opts) { + const optKeys = Object.keys(opts) as (keyof InspectOptionsStylized)[]; + for (let i = 0; i < optKeys.length; ++i) { + const key = optKeys[i]!; + // TODO(BridgeAR): Find a solution what to do about stylize. Either make + // this function public or add a new API with a similar or better + // functionality. + if ( + Object.prototype.hasOwnProperty.call(inspectDefaultOptions, key) || + key === 'stylize') { + (ctx as Record)[key] = opts[key]; + } else if (ctx.userOptions === undefined) { + // This is required to pass through the actual user input. + ctx.userOptions = opts; + } + } + } + } + if (ctx.colors) ctx.stylize = stylizeWithColor; + if (ctx.maxArrayLength === null) ctx.maxArrayLength = Infinity; + if (ctx.maxStringLength === null) ctx.maxStringLength = Infinity; + return formatValue(ctx, value, 0); +} +inspect.custom = customInspectSymbol; + +Object.defineProperty(inspect, 'defaultOptions', { + get() { + return inspectDefaultOptions; + }, + set(options) { + validateObject(options, 'options'); + return Object.assign(inspectDefaultOptions, options); + }, +}); + +// Set Graphics Rendition https://en.wikipedia.org/wiki/ANSI_escape_code#graphics +// Each color consists of an array with the color code as first entry and the +// reset code as second entry. +const defaultFG = 39; +const defaultBG = 49; +inspect.colors = { + __proto__: null, + reset: [0, 0], + bold: [1, 22], + dim: [2, 22], // Alias: faint + italic: [3, 23], + underline: [4, 24], + blink: [5, 25], + // Swap foreground and background colors + inverse: [7, 27], // Alias: swapcolors, swapColors + hidden: [8, 28], // Alias: conceal + strikethrough: [9, 29], // Alias: strikeThrough, crossedout, crossedOut + doubleunderline: [21, 24], // Alias: doubleUnderline + black: [30, defaultFG], + red: [31, defaultFG], + green: [32, defaultFG], + yellow: [33, defaultFG], + blue: [34, defaultFG], + magenta: [35, defaultFG], + cyan: [36, defaultFG], + white: [37, defaultFG], + bgBlack: [40, defaultBG], + bgRed: [41, defaultBG], + bgGreen: [42, defaultBG], + bgYellow: [43, defaultBG], + bgBlue: [44, defaultBG], + bgMagenta: [45, defaultBG], + bgCyan: [46, defaultBG], + bgWhite: [47, defaultBG], + framed: [51, 54], + overlined: [53, 55], + gray: [90, defaultFG], // Alias: grey, blackBright + redBright: [91, defaultFG], + greenBright: [92, defaultFG], + yellowBright: [93, defaultFG], + blueBright: [94, defaultFG], + magentaBright: [95, defaultFG], + cyanBright: [96, defaultFG], + whiteBright: [97, defaultFG], + bgGray: [100, defaultBG], // Alias: bgGrey, bgBlackBright + bgRedBright: [101, defaultBG], + bgGreenBright: [102, defaultBG], + bgYellowBright: [103, defaultBG], + bgBlueBright: [104, defaultBG], + bgMagentaBright: [105, defaultBG], + bgCyanBright: [106, defaultBG], + bgWhiteBright: [107, defaultBG], +}; + +function defineColorAlias(target: string, alias: string) { + Object.defineProperty(inspect.colors, alias, { + get() { + return this[target]; + }, + set(value) { + this[target] = value; + }, + configurable: true, + enumerable: false, + }); +} + +defineColorAlias('gray', 'grey'); +defineColorAlias('gray', 'blackBright'); +defineColorAlias('bgGray', 'bgGrey'); +defineColorAlias('bgGray', 'bgBlackBright'); +defineColorAlias('dim', 'faint'); +defineColorAlias('strikethrough', 'crossedout'); +defineColorAlias('strikethrough', 'strikeThrough'); +defineColorAlias('strikethrough', 'crossedOut'); +defineColorAlias('hidden', 'conceal'); +defineColorAlias('inverse', 'swapColors'); +defineColorAlias('inverse', 'swapcolors'); +defineColorAlias('doubleunderline', 'doubleUnderline'); + +// TODO(BridgeAR): Add function style support for more complex styles. +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + __proto__: null, + special: 'cyan', + number: 'yellow', + bigint: 'yellow', + boolean: 'yellow', + undefined: 'grey', + null: 'bold', + string: 'green', + symbol: 'green', + date: 'magenta', + name: undefined, + // TODO(BridgeAR): Highlight regular expressions properly. + regexp: 'red', + module: 'underline', +}; + +function addQuotes(str: string, quotes: number): string { + if (quotes === -1) { + return `"${str}"`; + } + if (quotes === -2) { + return `\`${str}\``; + } + return `'${str}'`; +} + +function escapeFn(str: string): string { + const charCode = str.charCodeAt(0); + return meta.length > charCode ? meta[charCode]! : `\\u${charCode.toString(16)}`; +} + +// Escape control characters, single quotes and the backslash. +// This is similar to JSON stringify escaping. +function strEscape(str: string): string { + let escapeTest = strEscapeSequencesRegExp; + let escapeReplace = strEscapeSequencesReplacer; + let singleQuote = 39; + + // Check for double quotes. If not present, do not escape single quotes and + // instead wrap the text in double quotes. If double quotes exist, check for + // backticks. If they do not exist, use those as fallback instead of the + // double quotes. + if (str.includes("'")) { + // This invalidates the charCode and therefore can not be matched for + // anymore. + if (!(str.includes('"'))) { + singleQuote = -1; + } else if (!str.includes('`') && + !str.includes('${')) { + singleQuote = -2; + } + if (singleQuote !== 39) { + escapeTest = strEscapeSequencesRegExpSingle; + escapeReplace = strEscapeSequencesReplacerSingle; + } + } + + // Some magic numbers that worked out fine while benchmarking with v8 6.0 + if (str.length < 5000 && escapeTest.exec(str) === null) + return addQuotes(str, singleQuote); + if (str.length > 100) { + str = str.replace(escapeReplace, escapeFn); + return addQuotes(str, singleQuote); + } + + let result = ''; + let last = 0; + for (let i = 0; i < str.length; i++) { + const point = str.charCodeAt(i); + if (point === singleQuote || + point === 92 || + point < 32 || + (point > 126 && point < 160)) { + if (last === i) { + result += meta[point]; + } else { + result += `${str.slice(last, i)}${meta[point]}`; + } + last = i + 1; + } else if (point >= 0xd800 && point <= 0xdfff) { + if (point <= 0xdbff && i + 1 < str.length) { + const point = str.charCodeAt(i + 1); + if (point >= 0xdc00 && point <= 0xdfff) { + i++; + continue; + } + } + result += `${str.slice(last, i)}\\u${point.toString(16)}`; + last = i + 1; + } + } + + if (last !== str.length) { + result += str.slice(last); + } + return addQuotes(result, singleQuote); +} + +function stylizeWithColor(str: string, styleType: Style): string { + const style = inspect.styles[styleType]; + if (style !== undefined) { + const color = (inspect.colors as unknown as Record)[style]; + if (color !== undefined) + return `\u001b[${color[0]}m${str}\u001b[${color[1]}m`; + } + return str; +} + +function stylizeNoColor(str: string): string { + return str; +} + +// Return a new empty array to push in the results of the default formatter. +function getEmptyFormatArray(): string[] { + return []; +} + +function isInstanceof(object: unknown, proto: Function): boolean { + try { + return object instanceof proto; + } catch { + return false; + } +} + +function getConstructorName(obj: object, ctx: Context, recurseTimes: number, protoProps?: string[]): string | null { + let firstProto: unknown; + const tmp = obj; + while (obj || isUndetectableObject(obj)) { + const descriptor = Object.getOwnPropertyDescriptor(obj, 'constructor'); + if (descriptor !== undefined && + typeof descriptor.value === 'function' && + descriptor.value.name !== '' && + isInstanceof(tmp, descriptor.value)) { + if (protoProps !== undefined && + (firstProto !== obj || + !builtInObjects.has(descriptor.value.name))) { + addPrototypeProperties( + ctx, tmp, firstProto || tmp, recurseTimes, protoProps); + } + return String(descriptor.value.name); + } + + obj = Object.getPrototypeOf(obj); + if (firstProto === undefined) { + firstProto = obj; + } + } + + if (firstProto === null) { + return null; + } + + const res = internal.getConstructorName(tmp); + + if (ctx.depth !== null && recurseTimes > ctx.depth) { + return `${res} `; + } + + const protoConstr = getConstructorName( + firstProto!, ctx, recurseTimes + 1, protoProps); + + if (protoConstr === null) { + return `${res} <${inspect(firstProto, { + ...ctx, + customInspect: false, + depth: -1, + })}>`; + } + + return `${res} <${protoConstr}>`; +} + +// This function has the side effect of adding prototype properties to the +// `output` argument (which is an array). This is intended to highlight user +// defined prototype properties. +function addPrototypeProperties(ctx: Context, main: object, obj: Object, recurseTimes: number, output: string[]): void { + let depth = 0; + let keys: PropertyKey[] | undefined; + let keySet: Set | undefined; + do { + if (depth !== 0 || main === obj) { + obj = Object.getPrototypeOf(obj); + // Stop as soon as a null prototype is encountered. + if (obj === null) { + return; + } + // Stop as soon as a built-in object type is detected. + const descriptor = Object.getOwnPropertyDescriptor(obj, 'constructor'); + if (descriptor !== undefined && + typeof descriptor.value === 'function' && + builtInObjects.has(descriptor.value.name)) { + return; + } + } + + if (depth === 0) { + keySet = new Set(); + } else { + keys!.forEach((key) => keySet!.add(key)); + } + // Get all own property names and symbols. + keys = Reflect.ownKeys(obj); + ctx.seen.push(main); + for (const key of keys) { + // Ignore the `constructor` property and keys that exist on layers above. + if (key === 'constructor' || + Object.prototype.hasOwnProperty.call(main, key) || + (depth !== 0 && keySet!.has(key))) { + continue; + } + const desc = Object.getOwnPropertyDescriptor(obj, key); + if (typeof desc?.value === 'function') { + continue; + } + const value = formatProperty( + ctx, obj, recurseTimes, key, kObjectType, desc, main); + if (ctx.colors) { + // Faint! + output.push(`\u001b[2m${value}\u001b[22m`); + } else { + output.push(value); + } + } + ctx.seen.pop(); + // Limit the inspection to up to three prototype layers. Using `recurseTimes` + // is not a good choice here, because it's as if the properties are declared + // on the current object from the users perspective. + } while (++depth !== 3); +} + +function getPrefix(constructor: string | null, tag: string, fallback: string, size = ''): string { + if (constructor === null) { + if (tag !== '' && fallback !== tag) { + return `[${fallback}${size}: null prototype] [${tag}] `; + } + return `[${fallback}${size}: null prototype] `; + } + + if (tag !== '' && constructor !== tag) { + return `${constructor}${size} [${tag}] `; + } + return `${constructor}${size} `; +} + +// Look up the keys of the object. +function getKeys(value: object, showHidden: boolean): PropertyKey[] { + let keys: PropertyKey[]; + const symbols = Object.getOwnPropertySymbols(value); + if (showHidden) { + keys = Object.getOwnPropertyNames(value); + if (symbols.length !== 0) + keys.push(...symbols); + } else { + // This might throw if `value` is a Module Namespace Object from an + // unevaluated module, but we don't want to perform the actual type + // check because it's expensive. + // TODO(devsnek): track https://github.com/tc39/ecma262/issues/1209 + // and modify this logic as needed. + try { + keys = Object.keys(value); + } catch (err: any) { + assert(isNativeError(err) && err.name === 'ReferenceError' && + isModuleNamespaceObject(value)); + keys = Object.getOwnPropertyNames(value); + } + if (symbols.length !== 0) { + const filter = (key: PropertyKey) => Object.prototype.propertyIsEnumerable.call(value, key); + keys.push(...symbols.filter(filter)); + } + } + return keys; +} + +function getCtxStyle(value: unknown, constructor: string | null, tag: string): string { + let fallback = ''; + if (constructor === null) { + fallback = internal.getConstructorName(value); + if (fallback === tag) { + fallback = 'Object'; + } + } + return getPrefix(constructor, tag, fallback); +} + +function formatProxy(ctx: Context, proxy: internal.ProxyDetails, recurseTimes: number): string { + if (ctx.depth !== null && recurseTimes > ctx.depth) { + return ctx.stylize('Proxy [Array]', 'special'); + } + recurseTimes += 1; + ctx.indentationLvl += 2; + const res: string[] = [ + formatValue(ctx, proxy.target, recurseTimes), + formatValue(ctx, proxy.handler, recurseTimes), + ]; + ctx.indentationLvl -= 2; + return reduceToSingleString( + ctx, res, '', ['Proxy [', ']'], kArrayExtrasType, recurseTimes); +} + +// Note: using `formatValue` directly requires the indentation level to be +// corrected by setting `ctx.indentationLvL += diff` and then to decrease the +// value afterwards again. +function formatValue(ctx: Context, value: unknown, recurseTimes: number, typedArray?: unknown): string { + // Primitive types cannot have properties. + if (typeof value !== 'object' && + typeof value !== 'function' && + !isUndetectableObject(value)) { + return formatPrimitive(ctx.stylize, value as Primitive, ctx); + } + if (value === null) { + return ctx.stylize('null', 'null'); + } + + // Memorize the context for custom inspection on proxies. + const context = value; + // Always check for proxies to prevent side effects and to prevent triggering + // any proxy handlers. + const proxy = internal.getProxyDetails(value); + if (proxy !== undefined) { + if (proxy === null || proxy.target === null) { + return ctx.stylize('', 'special'); + } + if (ctx.showProxy) { + return formatProxy(ctx, proxy, recurseTimes); + } + value = proxy.target; + } + + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it. + if (ctx.customInspect) { + let maybeCustom = (value as Record)[customInspectSymbol]; + + // WORKERD SPECIFIC PATCH: if `value` is a JSG resource type, use a well-known custom inspect + const maybeResourceTypeInspect = (value as Record)[internal.kResourceTypeInspect]; + if (typeof maybeResourceTypeInspect === "object") { + maybeCustom = formatJsgResourceType.bind( + context as Record, + maybeResourceTypeInspect as Record + ); + } + + if (typeof maybeCustom === 'function' && + // Filter out the util module, its inspect function is special. + maybeCustom !== inspect && + // Also filter out any prototype objects using the circular check. + !((value as object).constructor && (value as object).constructor.prototype === value)) { + // This makes sure the recurseTimes are reported as before while using + // a counter internally. + const depth = ctx.depth === null ? null : ctx.depth - recurseTimes; + const isCrossContext = + proxy !== undefined || !(context instanceof Object); + const ret = Function.prototype.call.call( + maybeCustom, + context, + depth, + getUserOptions(ctx, isCrossContext), + inspect, + ); + // If the custom inspection method returned `this`, don't go into + // infinite recursion. + if (ret !== context) { + if (typeof ret !== 'string') { + return formatValue(ctx, ret, recurseTimes); + } + return ret.replaceAll('\n', `\n${' '.repeat(ctx.indentationLvl)}`); + } + } + } + + // Using an array here is actually better for the average case than using + // a Set. `seen` will only check for the depth and will never grow too large. + if (ctx.seen.includes(value)) { + let index: number | undefined = 1; + if (ctx.circular === undefined) { + ctx.circular = new Map(); + ctx.circular.set(value, index); + } else { + index = ctx.circular.get(value); + if (index === undefined) { + index = ctx.circular.size + 1; + ctx.circular.set(value, index); + } + } + return ctx.stylize(`[Circular *${index}]`, 'special'); + } + + return formatRaw(ctx, value, recurseTimes, typedArray); +} + +function formatRaw(ctx: Context, value: unknown, recurseTimes: number, typedArray: unknown): string { + let keys: PropertyKey[] | undefined; + let protoProps: string[] | undefined; + if (ctx.showHidden && (ctx.depth === null || recurseTimes <= ctx.depth)) { + protoProps = []; + } + + const constructor = getConstructorName(value as object, ctx, recurseTimes, protoProps); + // Reset the variable to check for this later on. + if (protoProps !== undefined && protoProps.length === 0) { + protoProps = undefined; + } + + let tag = (value as { [Symbol.toStringTag]?: string })[Symbol.toStringTag]; + // Only list the tag in case it's non-enumerable / not an own property. + // Otherwise we'd print this twice. + if (typeof tag !== 'string' || + (tag !== '' && + (ctx.showHidden ? + Object.prototype.hasOwnProperty : + Object.prototype.propertyIsEnumerable) + .call(value, Symbol.toStringTag))) { + tag = ''; + } + let base = ''; + let formatter: (ctx: Context, value: any, recurseTimes: number) => string[] = getEmptyFormatArray; + let braces: [string, string] | undefined; + let noIterator = true; + let i = 0; + const filter = ctx.showHidden ? internal.ALL_PROPERTIES : internal.ONLY_ENUMERABLE; + + let extrasType = kObjectType; + + // Iterators and the rest are split to reduce checks. + // We have to check all values in case the constructor is set to null. + // Otherwise it would not possible to identify all types properly. + + const isEntriesObject = hasEntries(value); + if (Symbol.iterator in (value as object) || constructor === null || isEntriesObject) { + noIterator = false; + if (isEntriesObject) { + // WORKERD SPECIFIC PATCH: if `value` is an object with entries, format them like a map + const size = value[kEntries].length; + const prefix = getPrefix(constructor, tag, 'Object', `(${size})`); + keys = getKeys(value, ctx.showHidden); + + // Remove `kEntries` and `size` from keys + keys.splice(keys.indexOf(kEntries), 1); + const sizeIndex = keys.indexOf("size"); + if (sizeIndex !== -1) keys.splice(sizeIndex, 1); + + formatter = formatMap.bind(null, value[kEntries][Symbol.iterator]()); + if (size === 0 && keys.length === 0 && protoProps === undefined) + return `${prefix}{}`; + braces = [`${prefix}{`, '}']; + } else if (Array.isArray(value)) { + // Only set the constructor for non ordinary ("Array [...]") arrays. + const prefix = (constructor !== 'Array' || tag !== '') ? + getPrefix(constructor, tag, 'Array', `(${value.length})`) : + ''; + keys = internal.getOwnNonIndexProperties(value, filter); + braces = [`${prefix}[`, ']']; + if (value.length === 0 && keys.length === 0 && protoProps === undefined) + return `${braces[0]}]`; + extrasType = kArrayExtrasType; + formatter = formatArray; + } else if (isSet(value)) { + const size = setPrototypeSize.call(value); + const prefix = getPrefix(constructor, tag, 'Set', `(${size})`); + keys = getKeys(value, ctx.showHidden); + formatter = constructor !== null ? + formatSet.bind(null, value) : + formatSet.bind(null, Set.prototype.values.call(value)); + if (size === 0 && keys.length === 0 && protoProps === undefined) + return `${prefix}{}`; + braces = [`${prefix}{`, '}']; + } else if (isMap(value)) { + const size = mapPrototypeSize.call(value); + const prefix = getPrefix(constructor, tag, 'Map', `(${size})`); + keys = getKeys(value, ctx.showHidden); + formatter = constructor !== null ? + formatMap.bind(null, value) : + formatMap.bind(null, Map.prototype.entries.call(value)); + if (size === 0 && keys.length === 0 && protoProps === undefined) + return `${prefix}{}`; + braces = [`${prefix}{`, '}']; + } else if (isTypedArray(value)) { + keys = internal.getOwnNonIndexProperties(value, filter); + let bound = value; + let fallback = ''; + if (constructor === null) { + fallback = typedArrayPrototypeToStringTag.call(value); + // Reconstruct the array information. + bound = new (globalThis as unknown as Record)[fallback]!(value); + } + const size = typedArrayPrototypeLength.call(value); + const prefix = getPrefix(constructor, tag, fallback, `(${size})`); + braces = [`${prefix}[`, ']']; + if (value.length === 0 && keys.length === 0 && !ctx.showHidden) + return `${braces[0]}]`; + // Special handle the value. The original value is required below. The + // bound function is required to reconstruct missing information. + formatter = formatTypedArray.bind(null, bound, size); + extrasType = kArrayExtrasType; + } else if (isMapIterator(value)) { + keys = getKeys(value, ctx.showHidden); + braces = getIteratorBraces('Map', tag); + // Add braces to the formatter parameters. + formatter = formatIterator.bind(null, braces); + } else if (isSetIterator(value)) { + keys = getKeys(value, ctx.showHidden); + braces = getIteratorBraces('Set', tag); + // Add braces to the formatter parameters. + formatter = formatIterator.bind(null, braces); + } else { + noIterator = true; + } + } + if (noIterator) { + keys = getKeys(value as object, ctx.showHidden); + braces = ['{', '}']; + if (constructor === 'Object') { + if (isArgumentsObject(value)) { + braces[0] = '[Arguments] {'; + } else if (tag !== '') { + braces[0] = `${getPrefix(constructor, tag, 'Object')}{`; + } + if (keys.length === 0 && protoProps === undefined) { + return `${braces[0]}}`; + } + } else if (typeof value === 'function') { + base = getFunctionBase(value, constructor, tag); + if (keys.length === 0 && protoProps === undefined) + return ctx.stylize(base, 'special'); + } else if (isRegExp(value)) { + // Make RegExps say that they are RegExps + base = RegExp.prototype.toString.call( + constructor !== null ? value : new RegExp(value) + ); + const prefix = getPrefix(constructor, tag, 'RegExp'); + if (prefix !== 'RegExp ') + base = `${prefix}${base}`; + if ((keys.length === 0 && protoProps === undefined) || + (ctx.depth !== null && recurseTimes > ctx.depth)) { + return ctx.stylize(base, 'regexp'); + } + } else if (isDate(value)) { + // Make dates with properties first say the date + base = Number.isNaN(Date.prototype.getTime.call(value)) ? + Date.prototype.toString.call(value) : + Date.prototype.toISOString.call(value); + const prefix = getPrefix(constructor, tag, 'Date'); + if (prefix !== 'Date ') + base = `${prefix}${base}`; + if (keys.length === 0 && protoProps === undefined) { + return ctx.stylize(base, 'date'); + } + } else if (isError(value)) { + base = formatError(value, constructor, tag, ctx, keys); + if (keys.length === 0 && protoProps === undefined) + return base; + } else if (isAnyArrayBuffer(value)) { + // Fast path for ArrayBuffer and SharedArrayBuffer. + // Can't do the same for DataView because it has a non-primitive + // .buffer property that we need to recurse for. + const arrayType = isArrayBuffer(value) ? 'ArrayBuffer' : + 'SharedArrayBuffer'; + const prefix = getPrefix(constructor, tag, arrayType); + if (typedArray === undefined) { + formatter = formatArrayBuffer; + } else if (keys.length === 0 && protoProps === undefined) { + return prefix + + `{ byteLength: ${formatNumber(ctx.stylize, value.byteLength, false)} }`; + } + braces[0] = `${prefix}{`; + keys.unshift('byteLength'); + } else if (isDataView(value)) { + braces[0] = `${getPrefix(constructor, tag, 'DataView')}{`; + // .buffer goes last, it's not a primitive like the others. + keys.unshift('byteLength', 'byteOffset', 'buffer'); + } else if (isPromise(value)) { + braces[0] = `${getPrefix(constructor, tag, 'Promise')}{`; + formatter = formatPromise; + } else if (isWeakSet(value)) { + braces[0] = `${getPrefix(constructor, tag, 'WeakSet')}{`; + formatter = ctx.showHidden ? formatWeakSet : formatWeakCollection; + } else if (isWeakMap(value)) { + braces[0] = `${getPrefix(constructor, tag, 'WeakMap')}{`; + formatter = ctx.showHidden ? formatWeakMap : formatWeakCollection; + } else if (isModuleNamespaceObject(value)) { + braces[0] = `${getPrefix(constructor, tag, 'Module')}{`; + // Special handle keys for namespace objects. + formatter = formatNamespaceObject.bind(null, keys); + } else if (isBoxedPrimitive(value)) { + base = getBoxedBase(value, ctx, keys, constructor, tag); + if (keys.length === 0 && protoProps === undefined) { + return base; + } + } else { + if (keys.length === 0 && protoProps === undefined) { + return `${getCtxStyle(value, constructor, tag)}{}`; + } + braces[0] = `${getCtxStyle(value, constructor, tag)}{`; + } + } + + if (ctx.depth !== null && recurseTimes > ctx.depth) { + let constructorName = getCtxStyle(value, constructor, tag).slice(0, -1); + if (constructor !== null) + constructorName = `[${constructorName}]`; + return ctx.stylize(constructorName, 'special'); + } + recurseTimes += 1; + + ctx.seen.push(value); + ctx.currentDepth = recurseTimes; + let output; + const indentationLvl = ctx.indentationLvl; + try { + output = formatter(ctx, value, recurseTimes); + for (i = 0; i < keys!.length; i++) { + output.push( + formatProperty(ctx, value as object, recurseTimes, keys![i]!, extrasType), + ); + } + if (protoProps !== undefined) { + output.push(...protoProps); + } + } catch (err) { + const constructorName = getCtxStyle(value, constructor, tag).slice(0, -1); + return handleMaxCallStackSize(ctx, err as Error, constructorName, indentationLvl); + } + if (ctx.circular !== undefined) { + const index = ctx.circular.get(value); + if (index !== undefined) { + const reference = ctx.stylize(``, 'special'); + // Add reference always to the very beginning of the output. + if (ctx.compact !== true) { + base = base === '' ? reference : `${reference} ${base}`; + } else { + braces![0] = `${reference} ${braces![0]}`; + } + } + } + ctx.seen.pop(); + + if (ctx.sorted) { + const comparator = ctx.sorted === true ? undefined : ctx.sorted; + if (extrasType === kObjectType) { + output.sort(comparator); + } else if (keys!.length > 1) { + const sorted = output.slice(output.length - keys!.length).sort(comparator); + output.splice(output.length - keys!.length, keys!.length, ...sorted); + } + } + + const res = reduceToSingleString( + ctx, output, base, braces!, extrasType, recurseTimes, value); + const budget = ctx.budget[ctx.indentationLvl] || 0; + const newLength = budget + res.length; + ctx.budget[ctx.indentationLvl] = newLength; + // If any indentationLvl exceeds this limit, limit further inspecting to the + // minimum. Otherwise the recursive algorithm might continue inspecting the + // object even though the maximum string size (~2 ** 28 on 32 bit systems and + // ~2 ** 30 on 64 bit systems) exceeded. The actual output is not limited at + // exactly 2 ** 27 but a bit higher. This depends on the object shape. + // This limit also makes sure that huge objects don't block the event loop + // significantly. + if (newLength > 2 ** 27) { + ctx.depth = -1; + } + return res; +} + +function getIteratorBraces(type: string, tag: string): [string, string] { + if (tag !== `${type} Iterator`) { + if (tag !== '') + tag += '] ['; + tag += `${type} Iterator`; + } + return [`[${tag}] {`, '}']; +} + +function getBoxedBase(value: unknown, ctx: Context, keys: PropertyKey[], constructor: string | null, tag: string): string { + let fn: (this: unknown) => Primitive; + let type: Capitalize> | "BigInt"; + if (isNumberObject(value)) { + fn = Number.prototype.valueOf; + type = 'Number'; + } else if (isStringObject(value)) { + fn = String.prototype.valueOf; + type = 'String'; + // For boxed Strings, we have to remove the 0-n indexed entries, + // since they just noisy up the output and are redundant + // Make boxed primitive Strings look like such + keys.splice(0, value.length); + } else if (isBooleanObject(value)) { + fn = Boolean.prototype.valueOf; + type = 'Boolean'; + } else if (isBigIntObject(value)) { + fn = BigInt.prototype.valueOf; + type = 'BigInt'; + } else { + fn = Symbol.prototype.valueOf; + type = 'Symbol'; + } + let base = `[${type}`; + if (type !== constructor) { + if (constructor === null) { + base += ' (null prototype)'; + } else { + base += ` (${constructor})`; + } + } + base += `: ${formatPrimitive(stylizeNoColor, fn.call(value), ctx)}]`; + if (tag !== '' && tag !== constructor) { + base += ` [${tag}]`; + } + if (keys.length !== 0 || ctx.stylize === stylizeNoColor) + return base; + return ctx.stylize(base, type.toLowerCase() as Style); +} + +function getClassBase(value: any, constructor: string | null, tag: string): string { + const hasName = Object.prototype.hasOwnProperty.call(value, 'name'); + const name = (hasName && value.name) || '(anonymous)'; + let base = `class ${name}`; + if (constructor !== 'Function' && constructor !== null) { + base += ` [${constructor}]`; + } + if (tag !== '' && constructor !== tag) { + base += ` [${tag}]`; + } + if (constructor !== null) { + const superName = Object.getPrototypeOf(value).name; + if (superName) { + base += ` extends ${superName}`; + } + } else { + base += ' extends [null prototype]'; + } + return `[${base}]`; +} + +function getFunctionBase(value: Function, constructor: string | null, tag: string): string { + const stringified = Function.prototype.toString.call(value); + if (stringified.startsWith('class') && stringified.endsWith('}')) { + const slice = stringified.slice(5, -1); + const bracketIndex = slice.indexOf('{'); + if (bracketIndex !== -1 && + (!slice.slice(0, bracketIndex).includes('(') || + // Slow path to guarantee that it's indeed a class. + classRegExp.exec(slice.replace(stripCommentsRegExp, "")) !== null) + ) { + return getClassBase(value, constructor, tag); + } + } + let type = 'Function'; + if (isGeneratorFunction(value)) { + type = `Generator${type}`; + } + if (isAsyncFunction(value)) { + type = `Async${type}`; + } + let base = `[${type}`; + if (constructor === null) { + base += ' (null prototype)'; + } + if (value.name === '') { + base += ' (anonymous)'; + } else { + base += `: ${value.name}`; + } + base += ']'; + if (constructor !== type && constructor !== null) { + base += ` ${constructor}`; + } + if (tag !== '' && constructor !== tag) { + base += ` [${tag}]`; + } + return base; +} + +export function identicalSequenceRange(a: unknown[], b: unknown[]): { len: number, offset: number } { + for (let i = 0; i < a.length - 3; i++) { + // Find the first entry of b that matches the current entry of a. + const pos = b.indexOf(a[i]); + if (pos !== -1) { + const rest = b.length - pos; + if (rest > 3) { + let len = 1; + const maxLen = Math.min(a.length - i, rest); + // Count the number of consecutive entries. + while (maxLen > len && a[i + len] === b[pos + len]) { + len++; + } + if (len > 3) { + return { len, offset: i }; + } + } + } + } + + return { len: 0, offset: 0 }; +} + +function getStackString(error: Error): string { + return error.stack ? String(error.stack) : Error.prototype.toString.call(error); +} + +function getStackFrames(ctx: Context, err: Error, stack: string): string[] { + const frames = stack.split('\n'); + + let cause; + try { + ({ cause } = err); + } catch { + // If 'cause' is a getter that throws, ignore it. + } + + // Remove stack frames identical to frames in cause. + if (cause != null && isError(cause)) { + const causeStack = getStackString(cause); + const causeStackStart = causeStack.indexOf('\n at'); + if (causeStackStart !== -1) { + const causeFrames = causeStack.slice(causeStackStart + 1).split('\n'); + const { len, offset } = identicalSequenceRange(frames, causeFrames); + if (len > 0) { + const skipped = len - 2; + const msg = ` ... ${skipped} lines matching cause stack trace ...`; + frames.splice(offset + 1, skipped, ctx.stylize(msg, 'undefined')); + } + } + } + return frames; +} + +function improveStack(stack: string, constructor: string | null, name: string, tag: string): string { + // A stack trace may contain arbitrary data. Only manipulate the output + // for "regular errors" (errors that "look normal") for now. + let len = name.length; + + if (constructor === null || + (name.endsWith('Error') && + stack.startsWith(name) && + (stack.length === len || stack[len] === ':' || stack[len] === '\n'))) { + let fallback = 'Error'; + if (constructor === null) { + const start = /^([A-Z][a-z_ A-Z0-9[\]()-]+)(?::|\n {4}at)/.exec(stack) || + /^([a-z_A-Z0-9-]*Error)$/.exec(stack); + fallback = (start && start[1]) || ''; + len = fallback.length; + fallback = fallback || 'Error'; + } + const prefix = getPrefix(constructor, tag, fallback).slice(0, -1); + if (name !== prefix) { + if (prefix.includes(name)) { + if (len === 0) { + stack = `${prefix}: ${stack}`; + } else { + stack = `${prefix}${stack.slice(len)}`; + } + } else { + stack = `${prefix} [${name}]${stack.slice(len)}`; + } + } + } + return stack; +} + +function removeDuplicateErrorKeys(ctx: Context, keys: PropertyKey[], err: Error, stack: string): void { + if (!ctx.showHidden && keys.length !== 0) { + for (const name of ['name', 'message', 'stack'] as const) { + const index = keys.indexOf(name); + // Only hide the property in case it's part of the original stack + if (index !== -1 && stack.includes(err[name]!)) { + keys.splice(index, 1); + } + } + } +} + +function markNodeModules(ctx: Context, line: string): string { + let tempLine = ''; + let nodeModule; + let pos = 0; + while ((nodeModule = nodeModulesRegExp.exec(line)) !== null) { + // '/node_modules/'.length === 14 + tempLine += line.slice(pos, nodeModule.index + 14); + tempLine += ctx.stylize(nodeModule[1]!, 'module'); + pos = nodeModule.index + nodeModule[0].length; + } + if (pos !== 0) { + line = tempLine + line.slice(pos); + } + return line; +} + +function formatError(err: Error, constructor: string | null, tag: string, ctx: Context, keys: PropertyKey[]): string { + const name = err.name != null ? String(err.name) : 'Error'; + let stack = getStackString(err); + + removeDuplicateErrorKeys(ctx, keys, err, stack); + + if ('cause' in err && + (keys.length === 0 || !keys.includes('cause'))) { + keys.push('cause'); + } + + // Print errors aggregated into AggregateError + if (Array.isArray((err as { errors?: unknown }).errors) && + (keys.length === 0 || !keys.includes('errors'))) { + keys.push('errors'); + } + + stack = improveStack(stack, constructor, name, tag); + + // Ignore the error message if it's contained in the stack. + let pos = (err.message && stack.indexOf(err.message)) || -1; + if (pos !== -1) + pos += err.message.length; + // Wrap the error in brackets in case it has no stack trace. + const stackStart = stack.indexOf('\n at', pos); + if (stackStart === -1) { + stack = `[${stack}]`; + } else { + let newStack = stack.slice(0, stackStart); + const stackFramePart = stack.slice(stackStart + 1); + const lines = getStackFrames(ctx, err, stackFramePart); + if (ctx.colors) { + // Highlight userland code and node modules. + for (let line of lines) { + newStack += '\n'; + + line = markNodeModules(ctx, line); + + newStack += line; + } + } else { + newStack += `\n${lines.join('\n')}`; + } + stack = newStack; + } + // The message and the stack have to be indented as well! + if (ctx.indentationLvl !== 0) { + const indentation = ' '.repeat(ctx.indentationLvl); + stack = stack.replaceAll('\n', `\n${indentation}`); + } + return stack; +} + +function groupArrayElements(ctx: Context, output: string[], value: unknown[] | undefined): string[] { + let totalLength = 0; + let maxLength = 0; + let i = 0; + let outputLength = output.length; + if (ctx.maxArrayLength !== null && ctx.maxArrayLength < output.length) { + // This makes sure the "... n more items" part is not taken into account. + outputLength--; + } + const separatorSpace = 2; // Add 1 for the space and 1 for the separator. + const dataLen = new Array(outputLength); + // Calculate the total length of all output entries and the individual max + // entries length of all output entries. We have to remove colors first, + // otherwise the length would not be calculated properly. + for (; i < outputLength; i++) { + const len = getStringWidth(output[i]!, ctx.colors); + dataLen[i] = len; + totalLength += len + separatorSpace; + if (maxLength < len) + maxLength = len; + } + // Add two to `maxLength` as we add a single whitespace character plus a comma + // in-between two entries. + const actualMax = maxLength + separatorSpace; + // Check if at least three entries fit next to each other and prevent grouping + // of arrays that contains entries of very different length (i.e., if a single + // entry is longer than 1/5 of all other entries combined). Otherwise the + // space in-between small entries would be enormous. + if (actualMax * 3 + ctx.indentationLvl < ctx.breakLength && + (totalLength / actualMax > 5 || maxLength <= 6)) { + + const approxCharHeights = 2.5; + const averageBias = Math.sqrt(actualMax - totalLength / output.length); + const biasedMax = Math.max(actualMax - 3 - averageBias, 1); + // Dynamically check how many columns seem possible. + const columns = Math.min( + // Ideally a square should be drawn. We expect a character to be about 2.5 + // times as high as wide. This is the area formula to calculate a square + // which contains n rectangles of size `actualMax * approxCharHeights`. + // Divide that by `actualMax` to receive the correct number of columns. + // The added bias increases the columns for short entries. + Math.round( + Math.sqrt( + approxCharHeights * biasedMax * outputLength, + ) / biasedMax, + ), + // Do not exceed the breakLength. + Math.floor((ctx.breakLength - ctx.indentationLvl) / actualMax), + // Limit array grouping for small `compact` modes as the user requested + // minimal grouping. + (ctx.compact === false ? 0 : ctx.compact === true ? inspectDefaultOptions.compact : ctx.compact) * 4, + // Limit the columns to a maximum of fifteen. + 15, + ); + // Return with the original output if no grouping should happen. + if (columns <= 1) { + return output; + } + const tmp: string[] = []; + const maxLineLength: number[] = []; + for (let i = 0; i < columns; i++) { + let lineMaxLength = 0; + for (let j = i; j < output.length; j += columns) { + if (dataLen[j] > lineMaxLength) + lineMaxLength = dataLen[j]; + } + lineMaxLength += separatorSpace; + maxLineLength[i] = lineMaxLength; + } + let order = String.prototype.padStart; + if (value !== undefined) { + for (let i = 0; i < output.length; i++) { + if (typeof value[i] !== 'number' && typeof value[i] !== 'bigint') { + order = String.prototype.padEnd; + break; + } + } + } + // Each iteration creates a single line of grouped entries. + for (let i = 0; i < outputLength; i += columns) { + // The last lines may contain less entries than columns. + const max = Math.min(i + columns, outputLength); + let str = ''; + let j = i; + for (; j < max - 1; j++) { + // Calculate extra color padding in case it's active. This has to be + // done line by line as some lines might contain more colors than + // others. + const padding = maxLineLength[j - i]! + output[j]!.length - dataLen[j]; + str += order.call(`${output[j]}, `, padding, ' '); + } + if (order === String.prototype.padStart) { + const padding = maxLineLength[j - i]! + + output[j]!.length - + dataLen[j] - + separatorSpace; + str += output[j]!.padStart(padding, ' '); + } else { + str += output[j]; + } + tmp.push(str); + } + if (ctx.maxArrayLength !== null && ctx.maxArrayLength < output.length) { + tmp.push(output[outputLength]!); + } + output = tmp; + } + return output; +} + +function handleMaxCallStackSize(ctx: Context, err: Error, constructorName: string, indentationLvl: number): string { + if (isStackOverflowError(err)) { + ctx.seen.pop(); + ctx.indentationLvl = indentationLvl; + return ctx.stylize( + `[${constructorName}: Inspection interrupted ` + + 'prematurely. Maximum call stack size exceeded.]', + 'special', + ); + } + /* c8 ignore next */ + assert.fail(err.stack); +} + +function addNumericSeparator(integerString: string): string { + let result = ''; + let i = integerString.length; + const start = integerString.startsWith('-') ? 1 : 0; + for (; i >= start + 4; i -= 3) { + result = `_${integerString.slice(i - 3, i)}${result}`; + } + return i === integerString.length ? + integerString : + `${integerString.slice(0, i)}${result}`; +} + +function addNumericSeparatorEnd(integerString: string): string { + let result = ''; + let i = 0; + for (; i < integerString.length - 3; i += 3) { + result += `${integerString.slice(i, i + 3)}_`; + } + return i === 0 ? + integerString : + `${result}${integerString.slice(i)}`; +} + +const remainingText = (remaining: number) => `... ${remaining} more item${remaining > 1 ? 's' : ''}`; + +function formatNumber(fn: InspectOptionsStylized["stylize"], number: number, numericSeparator?: boolean): string { + if (!numericSeparator) { + // Format -0 as '-0'. Checking `number === -0` won't distinguish 0 from -0. + if (Object.is(number, -0)) { + return fn('-0', 'number'); + } + return fn(`${number}`, 'number'); + } + const integer = Math.trunc(number); + const string = String(integer); + if (integer === number) { + if (!Number.isFinite(number) || string.includes('e')) { + return fn(string, 'number'); + } + return fn(`${addNumericSeparator(string)}`, 'number'); + } + if (Number.isNaN(number)) { + return fn(string, 'number'); + } + return fn(`${ + addNumericSeparator(string) + }.${ + addNumericSeparatorEnd( + String(number).slice(string.length + 1), + ) + }`, 'number'); +} + +function formatBigInt(fn: InspectOptionsStylized["stylize"], bigint: bigint, numericSeparator?: boolean): string { + const string = String(bigint); + if (!numericSeparator) { + return fn(`${string}n`, 'bigint'); + } + return fn(`${addNumericSeparator(string)}n`, 'bigint'); +} + +type Primitive = string | number | bigint | boolean | undefined | symbol; +function formatPrimitive(fn: InspectOptionsStylized["stylize"], value: Primitive, ctx: Context): string { + if (typeof value === 'string') { + let trailer = ''; + if (ctx.maxStringLength !== null && value.length > ctx.maxStringLength) { + const remaining = value.length - ctx.maxStringLength; + value = value.slice(0, ctx.maxStringLength); + trailer = `... ${remaining} more character${remaining > 1 ? 's' : ''}`; + } + if (ctx.compact !== true && + // We do not support handling unicode characters width with + // the readline getStringWidth function as there are + // performance implications. + value.length > kMinLineLength && + value.length > ctx.breakLength - ctx.indentationLvl - 4) { + return value + .split(/(?<=\n)/) + .map((line) => fn(strEscape(line), 'string')) + .join(` +\n${' '.repeat(ctx.indentationLvl + 2)}`) + + trailer; + } + return fn(strEscape(value), 'string') + trailer; + } + if (typeof value === 'number') + return formatNumber(fn, value, ctx.numericSeparator); + if (typeof value === 'bigint') + return formatBigInt(fn, value, ctx.numericSeparator); + if (typeof value === 'boolean') + return fn(`${value}`, 'boolean'); + if (typeof value === 'undefined') + return fn('undefined', 'undefined'); + // es6 symbol primitive + return fn(Symbol.prototype.toString.call(value), 'symbol'); +} + +function formatNamespaceObject(keys: PropertyKey[], ctx: Context, value: object, recurseTimes: number): string[] { + const output = new Array(keys.length); + for (let i = 0; i < keys.length; i++) { + try { + output[i] = formatProperty(ctx, value, recurseTimes, keys[i]!, + kObjectType); + } catch (err) { + assert(isNativeError(err) && err.name === 'ReferenceError'); + // Use the existing functionality. This makes sure the indentation and + // line breaks are always correct. Otherwise it is very difficult to keep + // this aligned, even though this is a hacky way of dealing with this. + const tmp = { [keys[i]!]: '' }; + output[i] = formatProperty(ctx, tmp, recurseTimes, keys[i]!, kObjectType); + const pos = output[i]!.lastIndexOf(' '); + // We have to find the last whitespace and have to replace that value as + // it will be visualized as a regular string. + output[i] = output[i]!.slice(0, pos + 1) + + ctx.stylize('', 'special'); + } + } + // Reset the keys to an empty array. This prevents duplicated inspection. + keys.length = 0; + return output; +} + +// The array is sparse and/or has extra keys +function formatSpecialArray(ctx: Context, value: unknown[], recurseTimes: number, maxLength: number, output: string[], i: number): string[] { + const keys = Object.keys(value); + let index = i; + for (; i < keys.length && output.length < maxLength; i++) { + const key = keys[i]!; + const tmp = +key; + // Arrays can only have up to 2^32 - 1 entries + if (tmp > 2 ** 32 - 2) { + break; + } + if (`${index}` !== key) { + if (numberRegExp.exec(key) === null) { + break; + } + const emptyItems = tmp - index; + const ending = emptyItems > 1 ? 's' : ''; + const message = `<${emptyItems} empty item${ending}>`; + output.push(ctx.stylize(message, 'undefined')); + index = tmp; + if (output.length === maxLength) { + break; + } + } + output.push(formatProperty(ctx, value, recurseTimes, key, kArrayType)); + index++; + } + const remaining = value.length - index; + if (output.length !== maxLength) { + if (remaining > 0) { + const ending = remaining > 1 ? 's' : ''; + const message = `<${remaining} empty item${ending}>`; + output.push(ctx.stylize(message, 'undefined')); + } + } else if (remaining > 0) { + output.push(remainingText(remaining)); + } + return output; +} + +function formatArrayBuffer(ctx: Context, value: ArrayBuffer): string[] { + let buffer; + try { + buffer = new Uint8Array(value); + } catch { + return [ctx.stylize('(detached)', 'special')]; + } + const maxArrayLength = ctx.maxArrayLength; + let str = Buffer.prototype.hexSlice.call(buffer, 0, Math.min(maxArrayLength, buffer.length)) + .replace(/(.{2})/g, '$1 ') + .trim(); + const remaining = buffer.length - maxArrayLength; + if (remaining > 0) + str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`; + return [`${ctx.stylize('[Uint8Contents]', 'special')}: <${str}>`]; +} + +function formatArray(ctx: Context, value: unknown[], recurseTimes: number): string[] { + const valLen = value.length; + const len = Math.min(Math.max(0, ctx.maxArrayLength), valLen); + + const remaining = valLen - len; + const output: string[] = []; + for (let i = 0; i < len; i++) { + // Special handle sparse arrays. + if (!Object.prototype.hasOwnProperty.call(value, i)) { + return formatSpecialArray(ctx, value, recurseTimes, len, output, i); + } + output.push(formatProperty(ctx, value, recurseTimes, i, kArrayType)); + } + if (remaining > 0) { + output.push(remainingText(remaining)); + } + return output; +} + +function formatTypedArray(value: internal.TypedArray, length: number, ctx: Context, _ignored: unknown, recurseTimes: number): string[] { + const maxLength = Math.min(Math.max(0, ctx.maxArrayLength), length); + const remaining = value.length - maxLength; + const output = new Array(maxLength); + const elementFormatter = value.length > 0 && typeof value[0] === 'number' ? + formatNumber : + formatBigInt; + for (let i = 0; i < maxLength; ++i) { + // @ts-expect-error `value[i]` assumed to be of correct numeric type + output[i] = elementFormatter(ctx.stylize, value[i], ctx.numericSeparator); + } + if (remaining > 0) { + output[maxLength] = remainingText(remaining); + } + if (ctx.showHidden) { + // .buffer goes last, it's not a primitive like the others. + // All besides `BYTES_PER_ELEMENT` are actually getters. + ctx.indentationLvl += 2; + for (const key of [ + 'BYTES_PER_ELEMENT', + 'length', + 'byteLength', + 'byteOffset', + 'buffer', + ] as const) { + const str = formatValue(ctx, value[key], recurseTimes, true); + output.push(`[${key}]: ${str}`); + } + ctx.indentationLvl -= 2; + } + return output; +} + +function formatSet(value: Set | IterableIterator, ctx: Context, _ignored: unknown, recurseTimes: number): string[] { + const length = isSet(value) ? value.size : NaN; + const maxLength = Math.min(Math.max(0, ctx.maxArrayLength), length); + const remaining = length - maxLength; + const output: string[] = []; + ctx.indentationLvl += 2; + let i = 0; + for (const v of value) { + if (i >= maxLength) break; + output.push(formatValue(ctx, v, recurseTimes)); + i++; + } + if (remaining > 0) { + output.push(remainingText(remaining)); + } + ctx.indentationLvl -= 2; + return output; +} + +function formatMap(value: Map | IterableIterator<[unknown, unknown]>, ctx: Context, _ignored: unknown, recurseTimes: number): string[] { + const length = isMap(value) ? value.size : NaN; + const maxLength = Math.min(Math.max(0, ctx.maxArrayLength), length); + const remaining = length - maxLength; + const output: string[] = []; + ctx.indentationLvl += 2; + let i = 0; + for (const { 0: k, 1: v } of value) { + if (i >= maxLength) break; + output.push( + `${formatValue(ctx, k, recurseTimes)} => ${formatValue(ctx, v, recurseTimes)}` + ); + i++; + } + if (remaining > 0) { + output.push(remainingText(remaining)); + } + ctx.indentationLvl -= 2; + return output; +} + +function formatSetIterInner(ctx: Context, recurseTimes: number, entries: unknown[], state: number): string[] { + const maxArrayLength = Math.max(ctx.maxArrayLength, 0); + const maxLength = Math.min(maxArrayLength, entries.length); + const output = new Array(maxLength); + ctx.indentationLvl += 2; + for (let i = 0; i < maxLength; i++) { + output[i] = formatValue(ctx, entries[i], recurseTimes); + } + ctx.indentationLvl -= 2; + if (state === kWeak && !ctx.sorted) { + // Sort all entries to have a halfway reliable output (if more entries than + // retrieved ones exist, we can not reliably return the same output) if the + // output is not sorted anyway. + output.sort(); + } + const remaining = entries.length - maxLength; + if (remaining > 0) { + output.push(remainingText(remaining)); + } + return output; +} + +function formatMapIterInner(ctx: Context, recurseTimes: number, entries: unknown[], state: number): string[] { + const maxArrayLength = Math.max(ctx.maxArrayLength, 0); + // Entries exist as [key1, val1, key2, val2, ...] + const len = entries.length / 2; + const remaining = len - maxArrayLength; + const maxLength = Math.min(maxArrayLength, len); + const output = new Array(maxLength); + let i = 0; + ctx.indentationLvl += 2; + if (state === kWeak) { + for (; i < maxLength; i++) { + const pos = i * 2; + output[i] = + `${formatValue(ctx, entries[pos], recurseTimes)} => ${formatValue(ctx, entries[pos + 1], recurseTimes)}`; + } + // Sort all entries to have a halfway reliable output (if more entries than + // retrieved ones exist, we can not reliably return the same output) if the + // output is not sorted anyway. + if (!ctx.sorted) + output.sort(); + } else { + for (; i < maxLength; i++) { + const pos = i * 2; + const res = [ + formatValue(ctx, entries[pos], recurseTimes), + formatValue(ctx, entries[pos + 1], recurseTimes), + ]; + output[i] = reduceToSingleString( + ctx, res, '', ['[', ']'], kArrayExtrasType, recurseTimes); + } + } + ctx.indentationLvl -= 2; + if (remaining > 0) { + output.push(remainingText(remaining)); + } + return output; +} + +function formatWeakCollection(ctx: Context): string[] { + return [ctx.stylize('', 'special')]; +} + +function formatWeakSet(ctx: Context, value: WeakSet, recurseTimes: number): string[] { + const { entries } = internal.previewEntries(value)!; + return formatSetIterInner(ctx, recurseTimes, entries, kWeak); +} + +function formatWeakMap(ctx: Context, value: WeakMap, recurseTimes: number): string[] { + const { entries } = internal.previewEntries(value)!; + return formatMapIterInner(ctx, recurseTimes, entries, kWeak); +} + +function formatIterator(braces: [string, string], ctx: Context, value: Iterator, recurseTimes: number): string[] { + const { entries, isKeyValue } = internal.previewEntries(value)!; + if (isKeyValue) { + // Mark entry iterators as such. + braces[0] = braces[0].replace(/ Iterator] {$/, ' Entries] {'); + return formatMapIterInner(ctx, recurseTimes, entries, kMapEntries); + } + + return formatSetIterInner(ctx, recurseTimes, entries, kIterator); +} + +function formatPromise(ctx: Context, value: Promise, recurseTimes: number): string[] { + let output: string[]; + const { state, result } = internal.getPromiseDetails(value)!; + if (state === internal.kPending) { + output = [ctx.stylize('', 'special')]; + } else { + ctx.indentationLvl += 2; + const str = formatValue(ctx, result, recurseTimes); + ctx.indentationLvl -= 2; + output = [ + state === internal.kRejected ? + `${ctx.stylize('', 'special')} ${str}` : + str, + ]; + } + return output; +} + +function formatProperty(ctx: Context, value: object, recurseTimes: number, key: PropertyKey, type: number, desc?: PropertyDescriptor, + original = value): string { + let name: string, str: string; + let extra = ' '; + desc = desc || Object.getOwnPropertyDescriptor(value, key) || + { value: (value as Record)[key], enumerable: true }; + if (desc.value !== undefined) { + const diff = (ctx.compact !== true || type !== kObjectType) ? 2 : 3; + ctx.indentationLvl += diff; + str = formatValue(ctx, desc.value, recurseTimes); + if (diff === 3 && ctx.breakLength < getStringWidth(str, ctx.colors)) { + extra = `\n${' '.repeat(ctx.indentationLvl)}`; + } + ctx.indentationLvl -= diff; + } else if (desc.get !== undefined) { + const label = desc.set !== undefined ? 'Getter/Setter' : 'Getter'; + const s = ctx.stylize; + const sp = 'special'; + if (ctx.getters && (ctx.getters === true || + (ctx.getters === 'get' && desc.set === undefined) || + (ctx.getters === 'set' && desc.set !== undefined))) { + try { + const tmp = desc.get.call(original); + ctx.indentationLvl += 2; + if (tmp === null) { + str = `${s(`[${label}:`, sp)} ${s('null', 'null')}${s(']', sp)}`; + } else if (typeof tmp === 'object') { + str = `${s(`[${label}]`, sp)} ${formatValue(ctx, tmp, recurseTimes)}`; + } else { + const primitive = formatPrimitive(s, tmp, ctx); + str = `${s(`[${label}:`, sp)} ${primitive}${s(']', sp)}`; + } + ctx.indentationLvl -= 2; + } catch (err) { + const message = ``; + str = `${s(`[${label}:`, sp)} ${message}${s(']', sp)}`; + } + } else { + str = ctx.stylize(`[${label}]`, sp); + } + } else if (desc.set !== undefined) { + str = ctx.stylize('[Setter]', 'special'); + } else { + str = ctx.stylize('undefined', 'undefined'); + } + if (type === kArrayType) { + return str; + } + if (typeof key === 'symbol') { + const tmp = Symbol.prototype.toString.call(key).replace(strEscapeSequencesReplacer, escapeFn); + name = `[${ctx.stylize(tmp, 'symbol')}]`; + } else if (key === '__proto__') { + name = "['__proto__']"; + } else if (desc.enumerable === false) { + const tmp = String(key).replace(strEscapeSequencesReplacer, escapeFn); + name = `[${tmp}]`; + } else if (keyStrRegExp.exec(String(key)) !== null) { + name = ctx.stylize(String(key), 'name'); + } else { + name = ctx.stylize(strEscape(String(key)), 'string'); + } + return `${name}:${extra}${str}`; +} + +function isBelowBreakLength(ctx: Context, output: string[], start: number, base: string): boolean { + // Each entry is separated by at least a comma. Thus, we start with a total + // length of at least `output.length`. In addition, some cases have a + // whitespace in-between each other that is added to the total as well. + // TODO(BridgeAR): Add unicode support. Use the readline getStringWidth + // function. Check the performance overhead and make it an opt-in in case it's + // significant. + let totalLength = output.length + start; + if (totalLength + output.length > ctx.breakLength) + return false; + for (let i = 0; i < output.length; i++) { + if (ctx.colors) { + totalLength += removeColors(output[i]!).length; + } else { + totalLength += output[i]!.length; + } + if (totalLength > ctx.breakLength) { + return false; + } + } + // Do not line up properties on the same line if `base` contains line breaks. + return base === '' || !base.includes('\n'); +} + +function reduceToSingleString( + ctx: Context, output: string[], base: string, braces: [string, string], extrasType: number, recurseTimes: number, value?: unknown): string { + if (ctx.compact !== true) { + if (typeof ctx.compact === 'number' && ctx.compact >= 1) { + // Memorize the original output length. In case the output is grouped, + // prevent lining up the entries on a single line. + const entries = output.length; + // Group array elements together if the array contains at least six + // separate entries. + if (extrasType === kArrayExtrasType && entries > 6) { + output = groupArrayElements(ctx, output, value as unknown[]); + } + // `ctx.currentDepth` is set to the most inner depth of the currently + // inspected object part while `recurseTimes` is the actual current depth + // that is inspected. + // + // Example: + // + // const a = { first: [ 1, 2, 3 ], second: { inner: [ 1, 2, 3 ] } } + // + // The deepest depth of `a` is 2 (a.second.inner) and `a.first` has a max + // depth of 1. + // + // Consolidate all entries of the local most inner depth up to + // `ctx.compact`, as long as the properties are smaller than + // `ctx.breakLength`. + if (ctx.currentDepth - recurseTimes < ctx.compact && + entries === output.length) { + // Line up all entries on a single line in case the entries do not + // exceed `breakLength`. Add 10 as constant to start next to all other + // factors that may reduce `breakLength`. + const start = output.length + ctx.indentationLvl + + braces[0].length + base.length + 10; + if (isBelowBreakLength(ctx, output, start, base)) { + const joinedOutput = output.join(', '); + if (!joinedOutput.includes('\n')) { + return `${base ? `${base} ` : ''}${braces[0]} ${joinedOutput}` + + ` ${braces[1]}`; + } + } + } + } + // Line up each entry on an individual line. + const indentation = `\n${' '.repeat(ctx.indentationLvl)}`; + return `${base ? `${base} ` : ''}${braces[0]}${indentation} ` + + `${output.join(`,${indentation} `)}${indentation}${braces[1]}`; + } + // Line up all entries on a single line in case the entries do not exceed + // `breakLength`. + if (isBelowBreakLength(ctx, output, 0, base)) { + return `${braces[0]}${base ? ` ${base}` : ''} ${output.join(', ')} ` + + braces[1]; + } + const indentation = ' '.repeat(ctx.indentationLvl); + // If the opening "brace" is too large, like in the case of "Set {", + // we need to force the first item to be on the next line or the + // items will not line up correctly. + const ln = base === '' && braces[0].length === 1 ? + ' ' : `${base ? ` ${base}` : ''}\n${indentation} `; + // Line up each entry on an individual line. + return `${braces[0]}${ln}${output.join(`,\n${indentation} `)} ${braces[1]}`; +} + +function hasBuiltInToString(value: object): boolean { + // Prevent triggering proxy traps. + const proxyTarget = internal.getProxyDetails(value); + if (proxyTarget !== undefined) { + if (proxyTarget === null) { + return true; + } + value = proxyTarget.target as object; + } + + // Count objects that have no `toString` function as built-in. + if (typeof value?.toString !== 'function') { + return true; + } + + // The object has a own `toString` property. Thus it's not not a built-in one. + if (Object.prototype.hasOwnProperty.call(value, 'toString')) { + return false; + } + + // Find the object that has the `toString` property as own property in the + // prototype chain. + let pointer = value; + do { + pointer = Object.getPrototypeOf(pointer); + } while (!Object.prototype.hasOwnProperty.call(pointer, 'toString')); + + // Check closer if the object is a built-in. + const descriptor = Object.getOwnPropertyDescriptor(pointer, 'constructor'); + return descriptor !== undefined && + typeof descriptor.value === 'function' && + builtInObjects.has(descriptor.value.name); +} + +const firstErrorLine = (error: unknown) => (isError(error) ? error.message : String(error)).split('\n', 1)[0]; +let CIRCULAR_ERROR_MESSAGE: string | undefined; +function tryStringify(arg: unknown): string { + try { + return JSON.stringify(arg); + } catch (err) { + // Populate the circular error message lazily + if (!CIRCULAR_ERROR_MESSAGE) { + try { + const a: { a?: unknown } = {}; + a.a = a; + JSON.stringify(a); + } catch (circularError) { + CIRCULAR_ERROR_MESSAGE = firstErrorLine(circularError); + } + } + if (typeof err === "object" && err !== null && "name" in err && err.name === 'TypeError' && + firstErrorLine(err) === CIRCULAR_ERROR_MESSAGE) { + return '[Circular]'; + } + throw err; + } +} + +export function format(...args: unknown[]): string { + return formatWithOptionsInternal(undefined, args); +} + +export function formatWithOptions(inspectOptions: InspectOptions, ...args: unknown[]): string { + validateObject(inspectOptions, 'inspectOptions', { allowArray: true }); + return formatWithOptionsInternal(inspectOptions, args); +} + +function formatNumberNoColor(number: number, options?: InspectOptions): string { + return formatNumber( + stylizeNoColor, + number, + options?.numericSeparator ?? inspectDefaultOptions.numericSeparator, + ); +} + +function formatBigIntNoColor(bigint: bigint, options?: InspectOptions): string { + return formatBigInt( + stylizeNoColor, + bigint, + options?.numericSeparator ?? inspectDefaultOptions.numericSeparator, + ); +} + +function formatWithOptionsInternal(inspectOptions: InspectOptions | undefined, args: unknown[]): string { + const first = args[0]; + let a = 0; + let str = ''; + let join = ''; + + if (typeof first === 'string') { + if (args.length === 1) { + return first; + } + let tempStr; + let lastPos = 0; + + for (let i = 0; i < first.length - 1; i++) { + if (first.charCodeAt(i) === 37) { // '%' + const nextChar = first.charCodeAt(++i); + if (a + 1 !== args.length) { + switch (nextChar) { + case 115: { // 's' + const tempArg = args[++a]; + if (typeof tempArg === 'number') { + tempStr = formatNumberNoColor(tempArg, inspectOptions); + } else if (typeof tempArg === 'bigint') { + tempStr = formatBigIntNoColor(tempArg, inspectOptions); + } else if (typeof tempArg !== 'object' || + tempArg === null || + !hasBuiltInToString(tempArg)) { + tempStr = String(tempArg); + } else { + tempStr = inspect(tempArg, { + ...inspectOptions, + compact: 3, + colors: false, + depth: 0, + }); + } + break; + } + case 106: // 'j' + tempStr = tryStringify(args[++a]); + break; + case 100: { // 'd' + const tempNum = args[++a]; + if (typeof tempNum === 'bigint') { + tempStr = formatBigIntNoColor(tempNum, inspectOptions); + } else if (typeof tempNum === 'symbol') { + tempStr = 'NaN'; + } else { + tempStr = formatNumberNoColor(Number(tempNum), inspectOptions); + } + break; + } + case 79: // 'O' + tempStr = inspect(args[++a], inspectOptions); + break; + case 111: // 'o' + tempStr = inspect(args[++a], { + ...inspectOptions, + showHidden: true, + showProxy: true, + depth: 4, + }); + break; + case 105: { // 'i' + const tempInteger = args[++a]; + if (typeof tempInteger === 'bigint') { + tempStr = formatBigIntNoColor(tempInteger, inspectOptions); + } else if (typeof tempInteger === 'symbol') { + tempStr = 'NaN'; + } else { + tempStr = formatNumberNoColor( + Number.parseInt(tempInteger as unknown as string), inspectOptions); + } + break; + } + case 102: { // 'f' + const tempFloat = args[++a]; + if (typeof tempFloat === 'symbol') { + tempStr = 'NaN'; + } else { + tempStr = formatNumberNoColor( + Number.parseFloat(tempFloat as unknown as string), inspectOptions); + } + break; + } + case 99: // 'c' + a += 1; + tempStr = ''; + break; + case 37: // '%' + str += first.slice(lastPos, i); + lastPos = i + 1; + continue; + default: // Any other character is not a correct placeholder + continue; + } + if (lastPos !== i - 1) { + str += first.slice(lastPos, i - 1); + } + str += tempStr; + lastPos = i + 1; + } else if (nextChar === 37) { + str += first.slice(lastPos, i); + lastPos = i + 1; + } + } + } + if (lastPos !== 0) { + a++; + join = ' '; + if (lastPos < first.length) { + str += first.slice(lastPos); + } + } + } + + while (a < args.length) { + const value = args[a]; + str += join; + str += typeof value !== 'string' ? inspect(value, inspectOptions) : value; + join = ' '; + a++; + } + return str; +} + +export function isZeroWidthCodePoint(code: number): boolean { + return code <= 0x1F || // C0 control codes + (code >= 0x7F && code <= 0x9F) || // C1 control codes + (code >= 0x300 && code <= 0x36F) || // Combining Diacritical Marks + (code >= 0x200B && code <= 0x200F) || // Modifying Invisible Characters + // Combining Diacritical Marks for Symbols + (code >= 0x20D0 && code <= 0x20FF) || + (code >= 0xFE00 && code <= 0xFE0F) || // Variation Selectors + (code >= 0xFE20 && code <= 0xFE2F) || // Combining Half Marks + (code >= 0xE0100 && code <= 0xE01EF); // Variation Selectors +} + +/** + * Returns the number of columns required to display the given string. + */ +export function getStringWidth(str: string, removeControlChars = true): number { + let width = 0; + + if (removeControlChars) + str = stripVTControlCharacters(str); + str = str.normalize('NFC'); + for (const char of str) { + const code = char.codePointAt(0)!; + if (isFullWidthCodePoint(code)) { + width += 2; + } else if (!isZeroWidthCodePoint(code)) { + width++; + } + } + + return width; +}; + +/** + * Returns true if the character represented by a given + * Unicode code point is full-width. Otherwise returns false. + */ +const isFullWidthCodePoint = (code: number) => { + // Code points are partially derived from: + // https://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt + return code >= 0x1100 && ( + code <= 0x115f || // Hangul Jamo + code === 0x2329 || // LEFT-POINTING ANGLE BRACKET + code === 0x232a || // RIGHT-POINTING ANGLE BRACKET + // CJK Radicals Supplement .. Enclosed CJK Letters and Months + (code >= 0x2e80 && code <= 0x3247 && code !== 0x303f) || + // Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A + (code >= 0x3250 && code <= 0x4dbf) || + // CJK Unified Ideographs .. Yi Radicals + (code >= 0x4e00 && code <= 0xa4c6) || + // Hangul Jamo Extended-A + (code >= 0xa960 && code <= 0xa97c) || + // Hangul Syllables + (code >= 0xac00 && code <= 0xd7a3) || + // CJK Compatibility Ideographs + (code >= 0xf900 && code <= 0xfaff) || + // Vertical Forms + (code >= 0xfe10 && code <= 0xfe19) || + // CJK Compatibility Forms .. Small Form Variants + (code >= 0xfe30 && code <= 0xfe6b) || + // Halfwidth and Fullwidth Forms + (code >= 0xff01 && code <= 0xff60) || + (code >= 0xffe0 && code <= 0xffe6) || + // Kana Supplement + (code >= 0x1b000 && code <= 0x1b001) || + // Enclosed Ideographic Supplement + (code >= 0x1f200 && code <= 0x1f251) || + // Miscellaneous Symbols and Pictographs 0x1f300 - 0x1f5ff + // Emoticons 0x1f600 - 0x1f64f + (code >= 0x1f300 && code <= 0x1f64f) || + // CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane + (code >= 0x20000 && code <= 0x3fffd) + ); +}; + +/** + * Remove all VT control characters. Use to estimate displayed string width. + */ +export function stripVTControlCharacters(str: string): string { + validateString(str, 'str'); + + return str.replace(ansi, ''); +} + +// ================================================================================================ +// WORKERD SPECIFIC CODE + +// Called from C++ on `console.log()`s to format values +export function formatLog(...args: [...values: unknown[], colors: boolean]): string { + const inspectOptions: InspectOptions = { colors: args.pop() as boolean }; + try { + return formatWithOptions(inspectOptions, ...args); + } catch (err) { + return ``; + } +} + +function isBuiltinPrototype(proto: unknown) { + if (proto === null) return true; + const descriptor = Object.getOwnPropertyDescriptor(proto, 'constructor'); + return ( + descriptor !== undefined && + typeof descriptor.value === "function" && + builtInObjects.has(descriptor.value.name) + ); +} + +function isEntry(value: unknown): value is [unknown, unknown] { + return Array.isArray(value) && value.length === 2; +} +function maybeGetEntries(value: Record): [unknown, unknown][] | undefined { + const entriesFunction = value["entries"]; + if (typeof entriesFunction !== "function") return; + const entriesIterator: unknown = entriesFunction.call(value); + if (typeof entriesIterator !== "object" || entriesIterator === null) return; + if (!(Symbol.iterator in entriesIterator)) return; + const entries = Array.from(entriesIterator as Iterable); + if (!entries.every(isEntry)) return; + return entries; +} + +const kEntries = Symbol("kEntries"); +function hasEntries(value: unknown): value is { [kEntries]: [unknown, unknown][] } { + return typeof value === "object" && value !== null && kEntries in value; +} + +// Default custom inspect implementation for JSG resource types +function formatJsgResourceType( + this: Record, + additionalProperties: Record, + depth: number, + options: InspectOptionsStylized +): unknown { + const name = this.constructor.name; + if (depth < 0) return options.stylize(`[${name}]`, 'special'); + + // Build a plain object for inspection. If this value has an `entries()` function, add those + // entries for map-like `K => V` formatting. Note we can't use a `Map` here as a key may have + // multiple values (e.g. URLSearchParams). + const record: Record = {}; + const maybeEntries = maybeGetEntries(this); + if (maybeEntries !== undefined) record[kEntries] = maybeEntries; + + // Add all instance and prototype non-function-valued properties + let current: object = this; + do { + for (const key of Object.getOwnPropertyNames(current)) { + // `Object.getOwnPropertyDescriptor()` throws `Illegal Invocation` for our prototypes here + const value = this[key]; + // Ignore function-valued and static properties + if (typeof value === "function" || this.constructor.propertyIsEnumerable(key)) continue; + record[key] = value; + } + } while (!isBuiltinPrototype(current = Object.getPrototypeOf(current))); + + // Add additional inspect-only properties as non-enumerable so they appear in square brackets + for (const [key, symbol] of Object.entries(additionalProperties)) { + Object.defineProperty(record, key, { value: this[symbol], enumerable: false }); + } + + // Format the plain object + const inspected = inspect(record, { + ...options, + depth: options.depth == null ? null : depth, + showHidden: true, // Show non-enumerable inspect-only properties + }); + + if (maybeEntries === undefined) { + return `${name} ${inspected}`; + } else { + // Inspecting a entries object gives something like `Object(1) { 'a' => '1' }`, whereas we want + // something like `Headers(1) { 'a' => '1' }`. + return `${name}${inspected.replace("Object", "")}`; + } +} \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_path.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_path.ts new file mode 100644 index 000000000..88659773c --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_path.ts @@ -0,0 +1,616 @@ +import { + CHAR_DOT, + CHAR_FORWARD_SLASH, +} from './constants'; + +import { + validateObject, + validateString, +} from './validators'; + +function isPosixPathSeparator(code : number) { + return code === CHAR_FORWARD_SLASH; +} + +// Resolves . and .. elements in a path with directory names +function normalizeString(path: string, allowAboveRoot: boolean, separator: string, isPathSeparator: (code:number) => boolean) { + let res = ''; + let lastSegmentLength = 0; + let lastSlash = -1; + let dots = 0; + let code = 0; + for (let i = 0; i <= path.length; ++i) { + if (i < path.length) + code = path.charCodeAt(i); + else if (isPathSeparator(code)) + break; + else + code = CHAR_FORWARD_SLASH; + + if (isPathSeparator(code)) { + if (lastSlash === i - 1 || dots === 1) { + // NOOP + } else if (dots === 2) { + if (res.length < 2 || lastSegmentLength !== 2 || + res.charCodeAt(res.length - 1) !== CHAR_DOT || + res.charCodeAt(res.length - 2) !== CHAR_DOT) { + if (res.length > 2) { + const lastSlashIndex = res.lastIndexOf(separator); + if (lastSlashIndex === -1) { + res = ''; + lastSegmentLength = 0; + } else { + res = res.slice(0, lastSlashIndex); + lastSegmentLength = + res.length - 1 - res.lastIndexOf(separator); + } + lastSlash = i; + dots = 0; + continue; + } else if (res.length !== 0) { + res = ''; + lastSegmentLength = 0; + lastSlash = i; + dots = 0; + continue; + } + } + if (allowAboveRoot) { + res += res.length > 0 ? `${separator}..` : '..'; + lastSegmentLength = 2; + } + } else { + if (res.length > 0) + res += `${separator}${path.slice(lastSlash + 1, i)}`; + else + res = path.slice(lastSlash + 1, i); + lastSegmentLength = i - lastSlash - 1; + } + lastSlash = i; + dots = 0; + } else if (code === CHAR_DOT && dots !== -1) { + ++dots; + } else { + dots = -1; + } + } + return res; +} + +function formatExt(ext : string) { + return ext ? `${ext[0] === '.' ? '' : '.'}${ext}` : ''; +} + +/** + * @param {string} sep + * @param {{ + * dir?: string; + * root?: string; + * base?: string; + * name?: string; + * ext?: string; + * }} pathObject + * @returns {string} + */ + +type PathObject = { + dir?: string; + root?: string; + base?: string; + name?: string; + ext?: string; +}; + +function _format(sep : string, pathObject : PathObject) { + validateObject(pathObject, 'pathObject', {}); + const dir = pathObject.dir || pathObject.root; + const base = pathObject.base || + `${pathObject.name || ''}${formatExt(pathObject.ext!)}`; + if (!dir) { + return base; + } + return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`; +} + +// We currently do not implement the path.win32 subset. +const win32 = { + resolve(..._: [string[], string]) { + throw new Error('path.win32.resolve() is not implemented.'); + }, + + normalize(_ : string) { + throw new Error('path.win32.normalize() is not implemented.'); + }, + + isAbsolute(_ : string) { + throw new Error('path.win32.isAbsolute() is not implemented.'); + }, + + join(..._ : string[]) { + throw new Error('path.win32.join() is not implemented.'); + }, + + relative(_0 : string, _1 : string) { + throw new Error('path.win32.relative() is not implemented.'); + }, + + toNamespacedPath(_ : string) { + throw new Error('path.win32.toNamedspacedPath() is not implemented.'); + }, + + dirname(_ : string) { + throw new Error('path.win32.dirname() is not implemented.'); + }, + + basename(_0 : string, _1? : string) { + throw new Error('path.win32.basename() is not implemented.'); +}, + +extname(_ : string) { + throw new Error('path.win32.extname() is not implemented.'); +}, + +format(_ : PathObject) { + throw new Error('path.win32.format() is not implemented.'); +}, + +parse(_: string) { + throw new Error('path.win32.parse() is not implemented.'); +}, + +sep: '\\', + delimiter: ';', + win32: null as Object|null, + posix: null as Object|null, +}; + +const posix = { + /** + * path.resolve([from ...], to) + * @param {...string} args + * @returns {string} + */ + resolve(...args: string[]) { + let resolvedPath = ''; + let resolvedAbsolute = false; + + for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { + const path = i >= 0 ? args[i] : '/'; + + validateString(path, 'path'); + + // Skip empty entries + if (path!.length === 0) { + continue; + } + + resolvedPath = `${path}/${resolvedPath}`; + resolvedAbsolute = path!.charCodeAt(0) === CHAR_FORWARD_SLASH; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', + isPosixPathSeparator); + + if (resolvedAbsolute) { + return `/${resolvedPath}`; + } + return resolvedPath.length > 0 ? resolvedPath : '.'; + }, + + /** + * @param {string} path + * @returns {string} + */ + normalize(path : string) { + validateString(path, 'path'); + + if (path.length === 0) + return '.'; + + const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + const trailingSeparator = path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH; + + // Normalize the path + path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator); + + if (path.length === 0) { + if (isAbsolute) + return '/'; + return trailingSeparator ? './' : '.'; + } + if (trailingSeparator) + path += '/'; + + return isAbsolute ? `/${path}` : path; + }, + + /** + * @param {string} path + * @returns {boolean} + */ + isAbsolute(path : string) { + validateString(path, 'path'); + return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH; + }, + + /** + * @param {...string} args + * @returns {string} + */ + join(...args : string[]) { + if (args.length === 0) + return '.'; + let joined; + for (let i = 0; i < args.length; ++i) { + const arg = args[i]; + validateString(arg, 'path'); + if (arg!.length > 0) { + if (joined === undefined) + joined = arg; + else + joined += `/${arg}`; + } + } + if (joined === undefined) + return '.'; + return posix.normalize(joined); + }, + + /** + * @param {string} from + * @param {string} to + * @returns {string} + */ + relative(from: string, to : string) { + validateString(from, 'from'); + validateString(to, 'to'); + + if (from === to) + return ''; + + // Trim leading forward slashes. + from = posix.resolve(from); + to = posix.resolve(to); + + if (from === to) + return ''; + + const fromStart = 1; + const fromEnd = from.length; + const fromLen = fromEnd - fromStart; + const toStart = 1; + const toLen = to.length - toStart; + + // Compare paths to find the longest common path from root + const length = (fromLen < toLen ? fromLen : toLen); + let lastCommonSep = -1; + let i = 0; + for (; i < length; i++) { + const fromCode = from.charCodeAt(fromStart + i); + if (fromCode !== to.charCodeAt(toStart + i)) + break; + else if (fromCode === CHAR_FORWARD_SLASH) + lastCommonSep = i; + } + if (i === length) { + if (toLen > length) { + if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { + // We get here if `from` is the exact base path for `to`. + // For example: from='/foo/bar'; to='/foo/bar/baz' + return to.slice(toStart + i + 1); + } + if (i === 0) { + // We get here if `from` is the root + // For example: from='/'; to='/foo' + return to.slice(toStart + i); + } + } else if (fromLen > length) { + if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { + // We get here if `to` is the exact base path for `from`. + // For example: from='/foo/bar/baz'; to='/foo/bar' + lastCommonSep = i; + } else if (i === 0) { + // We get here if `to` is the root. + // For example: from='/foo/bar'; to='/' + lastCommonSep = 0; + } + } + } + + let out = ''; + // Generate the relative path based on the path difference between `to` + // and `from`. + for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { + if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { + out += out.length === 0 ? '..' : '/..'; + } + } + + // Lastly, append the rest of the destination (`to`) path that comes after + // the common path parts. + return `${out}${to.slice(toStart + lastCommonSep)}`; + }, + + /** + * @param {string} path + * @returns {string} + */ + toNamespacedPath(path : string) { + // Non-op on posix systems + return path; + }, + + /** + * @param {string} path + * @returns {string} + */ + dirname(path : string) { + validateString(path, 'path'); + if (path.length === 0) + return '.'; + const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + let end = -1; + let matchedSlash = true; + for (let i = path.length - 1; i >= 1; --i) { + if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { + if (!matchedSlash) { + end = i; + break; + } + } else { + // We saw the first non-path separator + matchedSlash = false; + } + } + + if (end === -1) + return hasRoot ? '/' : '.'; + if (hasRoot && end === 1) + return '//'; + return path.slice(0, end); + }, + + /** + * @param {string} path + * @param {string} [suffix] + * @returns {string} + */ + basename(path : string, suffix? : string) { + if (suffix !== undefined) + validateString(suffix, 'ext'); + validateString(path, 'path'); + + let start = 0; + let end = -1; + let matchedSlash = true; + + if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) { + if (suffix === path) + return ''; + let extIdx = suffix.length - 1; + let firstNonSlashEnd = -1; + for (let i = path.length - 1; i >= 0; --i) { + const code = path.charCodeAt(i); + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else { + if (firstNonSlashEnd === -1) { + // We saw the first non-path separator, remember this index in case + // we need it if the extension ends up not matching + matchedSlash = false; + firstNonSlashEnd = i + 1; + } + if (extIdx >= 0) { + // Try to match the explicit extension + if (code === suffix.charCodeAt(extIdx)) { + if (--extIdx === -1) { + // We matched the extension, so mark this as the end of our path + // component + end = i; + } + } else { + // Extension does not match, so our result is the entire path + // component + extIdx = -1; + end = firstNonSlashEnd; + } + } + } + } + + if (start === end) + end = firstNonSlashEnd; + else if (end === -1) + end = path.length; + return path.slice(start, end); + } + for (let i = path.length - 1; i >= 0; --i) { + if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // path component + matchedSlash = false; + end = i + 1; + } + } + + if (end === -1) + return ''; + return path.slice(start, end); +}, + +/** + * @param {string} path + * @returns {string} + */ +extname(path : string) { + validateString(path, 'path'); + let startDot = -1; + let startPart = 0; + let end = -1; + let matchedSlash = true; + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + let preDotState = 0; + for (let i = path.length - 1; i >= 0; --i) { + const code = path.charCodeAt(i); + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) + startDot = i; + else if (preDotState !== 1) + preDotState = 1; + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1; + } + } + + if (startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && + startDot === end - 1 && + startDot === startPart + 1)) { + return ''; + } + return path.slice(startDot, end); +}, + +format: _format.bind(null, '/'), + + /** + * @param {string} path + * @returns {{ + * dir: string; + * root: string; + * base: string; + * name: string; + * ext: string; + * }} + */ + parse(path: string) : PathObject { + validateString(path, 'path'); + + const ret = { root: '', dir: '', base: '', ext: '', name: '' }; + if (path.length === 0) + return ret; + const isAbsolute = + path.charCodeAt(0) === CHAR_FORWARD_SLASH; + let start; + if (isAbsolute) { + ret.root = '/'; + start = 1; + } else { + start = 0; + } + let startDot = -1; + let startPart = 0; + let end = -1; + let matchedSlash = true; + let i = path.length - 1; + + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + let preDotState = 0; + + // Get non-dir info + for (; i >= start; --i) { + const code = path.charCodeAt(i); + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) + startDot = i; + else if (preDotState !== 1) + preDotState = 1; + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1; + } + } + + if (end !== -1) { + const start = startPart === 0 && isAbsolute ? 1 : startPart; + if (startDot === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && + startDot === end - 1 && + startDot === startPart + 1)) { + ret.base = ret.name = path.slice(start, end); + } else { + ret.name = path.slice(start, startDot); + ret.base = path.slice(start, end); + ret.ext = path.slice(startDot, end); + } + } + + if (startPart > 0) + ret.dir = path.slice(0, startPart - 1); + else if (isAbsolute) + ret.dir = '/'; + + return ret; +}, + +sep: '/', + delimiter: ':', + win32: null as Object|null, + posix: null as Object|null, +}; + +posix.win32 = win32.win32 = win32; +posix.posix = win32.posix = posix; + +export default posix; +export { posix, win32 }; \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_stringdecoder.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_stringdecoder.ts new file mode 100644 index 000000000..63474c628 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_stringdecoder.ts @@ -0,0 +1,134 @@ + +import { Buffer, isEncoding } from './internal_buffer'; +import { normalizeEncoding } from './internal_utils'; +import { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_THIS, + ERR_UNKNOWN_ENCODING, +} from './internal_errors'; + +import * as bufferUtil from './buffer'; + +const kIncompleteCharactersStart = 0; +const kIncompleteCharactersEnd = 4; +const kMissingBytes = 4; +const kBufferedBytes = 5; +const kEncoding = 6; +const kSize = 7; + +const encodings : Record = { + ascii: 0, + latin1: 1, + utf8: 2, + utf16le: 3, + base64: 4, + base64url: 5, + hex: 6, +}; + +const kNativeDecoder = Symbol('kNativeDecoder'); + +export interface StringDecoder { + encoding: string; + readonly lastChar: Uint8Array; + readonly lastNeed: number; + readonly lastTotal: number; + new (encoding? : string): StringDecoder; + write(buf: ArrayBufferView|DataView|string): string; + end(buf?: ArrayBufferView|DataView|string): string; + text(buf: ArrayBufferView|DataView|string, offset?: number): string; + new (encoding?: string): StringDecoder; +} + +interface InternalDecoder extends StringDecoder { + [kNativeDecoder]: Buffer; +} + +export function StringDecoder(this: StringDecoder, encoding: string = 'utf8') { + const normalizedEncoding = normalizeEncoding(encoding); + if (!isEncoding(normalizedEncoding)) { + throw new ERR_UNKNOWN_ENCODING(encoding); + } + (this as InternalDecoder)[kNativeDecoder] = Buffer.alloc(kSize); + (this as InternalDecoder)[kNativeDecoder][kEncoding] = encodings[normalizedEncoding!]!; + this.encoding = normalizedEncoding!; +} + +function write(this: StringDecoder, buf: ArrayBufferView|DataView|string): string { + if ((this as InternalDecoder)[kNativeDecoder] === undefined) { + throw new ERR_INVALID_THIS('StringDecoder'); + } + if (typeof buf === 'string') { + return buf; + } + if (!ArrayBuffer.isView(buf)) { + throw new ERR_INVALID_ARG_TYPE('buf', [ + 'Buffer', 'TypedArray', 'DataView', 'string' + ], buf); + } + const buffer = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength); + return bufferUtil.decode(buffer, (this as InternalDecoder)[kNativeDecoder]); +} + +function end(this: StringDecoder, buf?: ArrayBufferView|DataView|string): string { + if ((this as InternalDecoder)[kNativeDecoder] === undefined) { + throw new ERR_INVALID_THIS('StringDecoder'); + } + let ret = ''; + if (buf !== undefined) { + ret = this.write(buf); + } + if ((this as InternalDecoder)[kNativeDecoder][kBufferedBytes]! > 0) { + ret += bufferUtil.flush((this as InternalDecoder)[kNativeDecoder]); + } + return ret; +} + +function text(this: StringDecoder, buf: ArrayBufferView|DataView|string, offset?: number) : string { + if ((this as InternalDecoder)[kNativeDecoder] === undefined) { + throw new ERR_INVALID_THIS('StringDecoder'); + } + (this as InternalDecoder)[kNativeDecoder][kMissingBytes] = 0; + (this as InternalDecoder)[kNativeDecoder][kBufferedBytes] = 0; + return this.write((buf as any).slice(offset)); +} + +StringDecoder.prototype.write = write; +StringDecoder.prototype.end = end; +StringDecoder.prototype.text = text; + +Object.defineProperties(StringDecoder.prototype, { + lastChar: { + enumerable: true, + get(this: StringDecoder) : Buffer { + if ((this as InternalDecoder)[kNativeDecoder] === undefined) { + throw new ERR_INVALID_THIS('StringDecoder'); + } + return (this as InternalDecoder)[kNativeDecoder].subarray( + kIncompleteCharactersStart, kIncompleteCharactersEnd) as Buffer; + }, + }, + lastNeed: { + enumerable: true, + get(this: StringDecoder) : number { + if ((this as InternalDecoder)[kNativeDecoder] === undefined) { + throw new ERR_INVALID_THIS('StringDecoder'); + } + return (this as InternalDecoder)[kNativeDecoder][kMissingBytes]!; + }, + }, + lastTotal: { + enumerable: true, + get(this: StringDecoder) : number { + if ((this as InternalDecoder)[kNativeDecoder] === undefined) { + throw new ERR_INVALID_THIS('StringDecoder'); + } + return (this as InternalDecoder)[kNativeDecoder][kBufferedBytes]! + + (this as InternalDecoder)[kNativeDecoder][kMissingBytes]!; + }, + }, +}); + +export default { + StringDecoder +}; diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_types.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_types.ts new file mode 100644 index 000000000..5fb614dff --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_types.ts @@ -0,0 +1,97 @@ + +import * as internal from "./util"; + +export function isCryptoKey(value: unknown): boolean { + return value instanceof CryptoKey; +} + +export function isKeyObject(_value: unknown): boolean { + // TODO(nodecompat): We currently do not implement KeyObject + return false; +} + +export const isAsyncFunction = internal.isAsyncFunction.bind(internal); +export const isGeneratorFunction = internal.isGeneratorFunction.bind(internal); +export const isGeneratorObject = internal.isGeneratorObject.bind(internal); +export const isAnyArrayBuffer = internal.isAnyArrayBuffer.bind(internal); +export const isArrayBuffer = internal.isArrayBuffer.bind(internal); +export const isArgumentsObject = internal.isArgumentsObject.bind(internal); +export const isBoxedPrimitive = internal.isBoxedPrimitive.bind(internal); +export const isDataView = internal.isDataView.bind(internal); +export const isMap = internal.isMap.bind(internal); +export const isMapIterator = internal.isMapIterator.bind(internal); +export const isModuleNamespaceObject = internal.isModuleNamespaceObject.bind(internal); +export const isNativeError = internal.isNativeError.bind(internal); +export const isPromise = internal.isPromise.bind(internal); +export const isProxy = internal.isProxy.bind(internal); +export const isSet = internal.isSet.bind(internal); +export const isSetIterator = internal.isSetIterator.bind(internal); +export const isSharedArrayBuffer = internal.isSharedArrayBuffer.bind(internal); +export const isWeakMap = internal.isWeakMap.bind(internal); +export const isWeakSet = internal.isWeakSet.bind(internal); +export const isRegExp = internal.isRegExp.bind(internal); +export const isDate = internal.isDate.bind(internal); +export const isStringObject = internal.isStringObject.bind(internal); +export const isSymbolObject = internal.isSymbolObject.bind(internal); +export const isNumberObject = internal.isNumberObject.bind(internal); +export const isBooleanObject = internal.isBooleanObject.bind(internal); +export const isBigIntObject = internal.isBigIntObject.bind(internal); +export const isArrayBufferView = internal.isArrayBufferView.bind(internal); +export const isBigInt64Array = internal.isBigInt64Array.bind(internal); +export const isBigUint64Array = internal.isBigUint64Array.bind(internal); +export const isFloat32Array = internal.isFloat32Array.bind(internal); +export const isFloat64Array = internal.isFloat64Array.bind(internal); +export const isInt8Array = internal.isInt8Array.bind(internal); +export const isInt16Array = internal.isInt16Array.bind(internal); +export const isInt32Array = internal.isInt32Array.bind(internal); +export const isTypedArray = internal.isTypedArray.bind(internal); +export const isUint8Array = internal.isUint8Array.bind(internal); +export const isUint8ClampedArray = internal.isUint8ClampedArray.bind(internal); +export const isUint16Array = internal.isUint16Array.bind(internal); +export const isUint32Array = internal.isUint32Array.bind(internal); + +export default { + isCryptoKey, + isKeyObject, + + isAsyncFunction, + isGeneratorFunction, + isGeneratorObject, + isAnyArrayBuffer, + isArrayBuffer, + isArgumentsObject, + isBoxedPrimitive, + isDataView, + isMap, + isMapIterator, + isModuleNamespaceObject, + isNativeError, + isPromise, + isProxy, + isSet, + isSetIterator, + isSharedArrayBuffer, + isWeakMap, + isWeakSet, + isRegExp, + isDate, + isStringObject, + isSymbolObject, + isNumberObject, + isBooleanObject, + isBigIntObject, + isArrayBufferView, + isBigInt64Array, + isBigUint64Array, + isFloat32Array, + isFloat64Array, + isInt8Array, + isInt16Array, + isInt32Array, + isTypedArray, + isUint8Array, + isUint8ClampedArray, + isUint16Array, + isUint32Array, + // TODO(soon): isExternal +}; \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_utils.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_utils.ts new file mode 100644 index 000000000..a75b67790 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/internal_utils.ts @@ -0,0 +1,182 @@ + +export function normalizeEncoding(enc?: string) : string | undefined { + if (enc == null || + enc === "utf8" || + enc === "utf-8" || + enc === "UTF8" || + enc === "UTF-8") return "utf8"; + return slowCases(enc); +} + +export function slowCases(enc: string) : string | undefined { + switch (enc.length) { + case 4: + if (enc === "UTF8") return "utf8"; + if (enc === "ucs2" || enc === "UCS2") return "utf16le"; + enc = `${enc}`.toLowerCase(); + if (enc === "utf8") return "utf8"; + if (enc === "ucs2") return "utf16le"; + break; + case 3: + if ( + enc === "hex" || enc === "HEX" || + `${enc}`.toLowerCase() === "hex" + ) { + return "hex"; + } + break; + case 5: + if (enc === "ascii") return "ascii"; + if (enc === "ucs-2") return "utf16le"; + if (enc === "UTF-8") return "utf8"; + if (enc === "ASCII") return "ascii"; + if (enc === "UCS-2") return "utf16le"; + enc = `${enc}`.toLowerCase(); + if (enc === "utf-8") return "utf8"; + if (enc === "ascii") return "ascii"; + if (enc === "ucs-2") return "utf16le"; + break; + case 6: + if (enc === "base64") return "base64"; + if (enc === "latin1" || enc === "binary") return "latin1"; + if (enc === "BASE64") return "base64"; + if (enc === "LATIN1" || enc === "BINARY") return "latin1"; + enc = `${enc}`.toLowerCase(); + if (enc === "base64") return "base64"; + if (enc === "latin1" || enc === "binary") return "latin1"; + break; + case 7: + if ( + enc === "utf16le" || enc === "UTF16LE" || + `${enc}`.toLowerCase() === "utf16le" + ) { + return "utf16le"; + } + break; + case 8: + if ( + enc === "utf-16le" || enc === "UTF-16LE" || + `${enc}`.toLowerCase() === "utf-16le" + ) { + return "utf16le"; + } + break; + case 9: + if ( + enc === "base64url" || enc === "BASE64URL" || + `${enc}`.toLowerCase() === "base64url" + ) { + return "base64url"; + } + break; + default: + if (enc === "") return "utf8"; + } + return undefined; +} + +export function spliceOne(list: (string|undefined)[], index: number) { + for (; index + 1 < list.length; index++) list[index] = list[index + 1]; + list.pop(); +} + +export const ALL_PROPERTIES = 0; +export const ONLY_WRITABLE = 1; +export const ONLY_ENUMERABLE = 2; +export const ONLY_CONFIGURABLE = 4; +export const ONLY_ENUM_WRITABLE = 6; +export const SKIP_STRINGS = 8; +export const SKIP_SYMBOLS = 16; + +const isNumericLookup: Record = {}; +export function isArrayIndex(value: unknown): value is number | string { + switch (typeof value) { + case "number": + return value >= 0 && (value | 0) === value; + case "string": { + const result = isNumericLookup[value]; + if (result !== void 0) { + return result; + } + const length = value.length; + if (length === 0) { + return isNumericLookup[value] = false; + } + let ch = 0; + let i = 0; + for (; i < length; ++i) { + ch = value.charCodeAt(i); + if ( + i === 0 && ch === 0x30 && length > 1 /* must not start with 0 */ || + ch < 0x30 /* 0 */ || ch > 0x39 /* 9 */ + ) { + return isNumericLookup[value] = false; + } + } + return isNumericLookup[value] = true; + } + default: + return false; + } +} + +export function getOwnNonIndexProperties( + // deno-lint-ignore ban-types + obj: object, + filter: number, +): (string | symbol)[] { + let allProperties = [ + ...Object.getOwnPropertyNames(obj), + ...Object.getOwnPropertySymbols(obj), + ]; + + if (Array.isArray(obj)) { + allProperties = allProperties.filter((k) => !isArrayIndex(k)); + } + + if (filter === ALL_PROPERTIES) { + return allProperties; + } + + const result: (string | symbol)[] = []; + for (const key of allProperties) { + const desc = Object.getOwnPropertyDescriptor(obj, key); + if (desc === undefined) { + continue; + } + if (filter & ONLY_WRITABLE && !desc.writable) { + continue; + } + if (filter & ONLY_ENUMERABLE && !desc.enumerable) { + continue; + } + if (filter & ONLY_CONFIGURABLE && !desc.configurable) { + continue; + } + if (filter & SKIP_STRINGS && typeof key === "string") { + continue; + } + if (filter & SKIP_SYMBOLS && typeof key === "symbol") { + continue; + } + result.push(key); + } + return result; +} + +export function createDeferredPromise() { + let resolve; + let reject; + + // eslint-disable-next-line promise/param-names + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }) + return { + promise, + resolve, + reject, + }; +} + diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/process.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/process.ts new file mode 100644 index 000000000..d72466f2c --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/process.ts @@ -0,0 +1,65 @@ +import { + validateObject, +} from './validators'; + +import { + ERR_INVALID_ARG_VALUE, +} from './internal_errors' + +export function nextTick(cb: Function, ...args: unknown[]) { + queueMicrotask(() => { cb(...args); }); +}; + +// Note that there is no process-level environment in workers so the process.env +// object is initially empty. This is different from Node.js where process.env +// picks up values from the operating system environment. The configured bindings +// for the worker are accessible from the env argument passed into the fetch +// handler and have no impact here. + +export const env = new Proxy({}, { + // Per Node.js rules. process.env values must be coerced to strings. + // When defined using defineProperty, the property descriptor must be writable, + // configurable, and enumerable using just a falsy check. Getters and setters + // are not permitted. + set(obj: object, prop: PropertyKey, value: any) { + return Reflect.set(obj, prop, `${value}`); + }, + defineProperty(obj: object, prop: PropertyKey, descriptor: PropertyDescriptor) { + validateObject(descriptor, 'descriptor', {}); + if (Reflect.has(descriptor, 'get') || Reflect.has(descriptor, 'set')) { + throw new ERR_INVALID_ARG_VALUE('descriptor', descriptor, + 'process.env value must not have getter/setter'); + } + if (!descriptor.configurable) { + throw new ERR_INVALID_ARG_VALUE('descriptor.configurable', descriptor, + 'process.env value must be configurable') + } + if (!descriptor.enumerable) { + throw new ERR_INVALID_ARG_VALUE('descriptor.enumerable', descriptor, + 'process.env value must be enumerable') + } + if (!descriptor.writable) { + throw new ERR_INVALID_ARG_VALUE('descriptor.writable', descriptor, + 'process.env value must be writable') + } + if (Reflect.has(descriptor, 'value')) { + Reflect.set(descriptor, 'value', `${descriptor.value}`); + } else { + throw new ERR_INVALID_ARG_VALUE('descriptor.value', descriptor, + 'process.env value must be specified explicitly'); + } + return Reflect.defineProperty(obj, prop, descriptor); + } +}); + +export const argv = []; + +export const platform = 'wasm'; + + +export default { + nextTick, + env, + argv, + platform, +}; diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_adapters.js b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_adapters.js new file mode 100644 index 000000000..ad3aadfed --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_adapters.js @@ -0,0 +1,730 @@ +/* eslint-disable */ +import { + Readable, +} from './streams_readable'; + +import { TextEncoder, TextDecoder } from "@sinonjs/text-encoding"; + +import { + Writable, +} from './streams_writable'; + +import { + Duplex, +} from './streams_duplex'; + +import { + destroy, + eos as finished, + isDestroyed, + isReadable, + isWritable, + isWritableEnded, +} from './streams_util'; + +import { + Buffer, +} from './internal_buffer'; + +import { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_STREAM_PREMATURE_CLOSE, + AbortError, +} from './internal_errors'; + +import { + createDeferredPromise, + normalizeEncoding, +} from './internal_utils'; + +import { + validateBoolean, + validateObject, +} from './validators'; + +import * as process from './process'; + +const encoder = new TextEncoder(); + +/** + * @param {Writable} streamWritable + * @returns {WritableStream} + */ +export function newWritableStreamFromStreamWritable(streamWritable) { + // Not using the internal/streams/utils isWritableNodeStream utility + // here because it will return false if streamWritable is a Duplex + // whose writable option is false. For a Duplex that is not writable, + // we want it to pass this check but return a closed WritableStream. + // We check if the given stream is a stream.Writable or http.OutgoingMessage + const checkIfWritableOrOutgoingMessage = + streamWritable && + typeof streamWritable?.write === 'function' && + typeof streamWritable?.on === 'function'; + if (!checkIfWritableOrOutgoingMessage) { + throw new ERR_INVALID_ARG_TYPE( + 'streamWritable', + 'stream.Writable', + streamWritable, + ); + } + + if (isDestroyed(streamWritable) || !isWritable(streamWritable)) { + const writable = new WritableStream(); + writable.close(); + return writable; + } + + const highWaterMark = streamWritable.writableHighWaterMark; + const strategy = + streamWritable.writableObjectMode ? + new CountQueuingStrategy({ highWaterMark }) : + { highWaterMark }; + + let controller; + let backpressurePromise; + let closed; + + function onDrain() { + if (backpressurePromise !== undefined) + backpressurePromise.resolve(); + } + + const cleanup = finished(streamWritable, (error) => { + if (error?.code === 'ERR_STREAM_PREMATURE_CLOSE') { + const err = new AbortError(undefined, { cause: error }); + error = err; + } + + cleanup(); + // This is a protection against non-standard, legacy streams + // that happen to emit an error event again after finished is called. + streamWritable.on('error', () => {}); + if (error != null) { + if (backpressurePromise !== undefined) + backpressurePromise.reject(error); + // If closed is not undefined, the error is happening + // after the WritableStream close has already started. + // We need to reject it here. + if (closed !== undefined) { + closed.reject(error); + closed = undefined; + } + controller.error(error); + controller = undefined; + return; + } + + if (closed !== undefined) { + closed.resolve(); + closed = undefined; + return; + } + controller.error(new AbortError()); + controller = undefined; + }); + + streamWritable.on('drain', onDrain); + + return new WritableStream({ + start(c) { controller = c; }, + + async write(chunk) { + if (streamWritable.writableNeedDrain || !streamWritable.write(chunk)) { + backpressurePromise = createDeferredPromise(); + return backpressurePromise.promise.finally(() => { + backpressurePromise = undefined; + }); + } + }, + + abort(reason) { + destroy(streamWritable, reason); + }, + + close() { + if (closed === undefined && !isWritableEnded(streamWritable)) { + closed = createDeferredPromise(); + streamWritable.end(); + return closed.promise; + } + + controller = undefined; + return Promise.resolve(); + }, + }, strategy); +} + +/** + * @param {WritableStream} writableStream + * @param {{ + * decodeStrings? : boolean, + * highWaterMark? : number, + * objectMode? : boolean, + * signal? : AbortSignal, + * }} [options] + * @returns {Writable} + */ +export function newStreamWritableFromWritableStream(writableStream, options = {}) { + if (!(writableStream instanceof WritableStream)) { + throw new ERR_INVALID_ARG_TYPE( + 'writableStream', + 'WritableStream', + writableStream); + } + + validateObject(options, 'options'); + const { + highWaterMark, + decodeStrings = true, + objectMode = false, + signal, + } = options; + + validateBoolean(objectMode, 'options.objectMode'); + validateBoolean(decodeStrings, 'options.decodeStrings'); + + const writer = writableStream.getWriter(); + let closed = false; + + const writable = new Writable({ + highWaterMark, + objectMode, + decodeStrings, + signal, + + writev(chunks, callback) { + function done(error) { + error = error.filter((e) => e); + try { + callback(error.length === 0 ? undefined : error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => destroy(writable, error)); + } + } + + writer.ready.then(() => { + return Promise.all(chunks.map((data) => writer.write(data))) + .then(done, done); + }, + done); + }, + + write(chunk, encoding, callback) { + if (typeof chunk === 'string' && decodeStrings && !objectMode) { + const enc = normalizeEncoding(encoding); + + if (enc === 'utf8') { + chunk = encoder.encode(chunk); + } else { + chunk = Buffer.from(chunk, encoding); + chunk = new Uint8Array( + chunk.buffer, + chunk.byteOffset, + chunk.byteLength + ); + } + } + + function done(error) { + try { + callback(error); + } catch (error) { + destroy(writable, error); + } + } + + writer.ready.then(() => { + return writer.write(chunk).then(done, done); + }, + done); + }, + + destroy(error, callback) { + function done() { + try { + callback(error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => { throw error; }); + } + } + + if (!closed) { + if (error != null) { + writer.abort(error).then(done, done); + } else { + writer.close().then(done, done); + } + return; + } + + done(); + }, + + final(callback) { + function done(error) { + try { + callback(error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => destroy(writable, error)); + } + } + + if (!closed) { + writer.close().then(done, done); + } + }, + }); + + writer.closed.then(() => { + // If the WritableStream closes before the stream.Writable has been + // ended, we signal an error on the stream.Writable. + closed = true; + if (!isWritableEnded(writable)) + destroy(writable, new ERR_STREAM_PREMATURE_CLOSE()); + }, + (error) => { + // If the WritableStream errors before the stream.Writable has been + // destroyed, signal an error on the stream.Writable. + closed = true; + destroy(writable, error); + }); + + return writable; +} + +/** + * @typedef {import('./queuingstrategies').QueuingStrategy} QueuingStrategy + * @param {Readable} streamReadable + * @param {{ + * strategy : QueuingStrategy + * }} [options] + * @returns {ReadableStream} + */ +export function newReadableStreamFromStreamReadable(streamReadable, options = {}) { + // Not using the internal/streams/utils isReadableNodeStream utility + // here because it will return false if streamReadable is a Duplex + // whose readable option is false. For a Duplex that is not readable, + // we want it to pass this check but return a closed ReadableStream. + if (typeof streamReadable?._readableState !== 'object') { + throw new ERR_INVALID_ARG_TYPE( + 'streamReadable', + 'stream.Readable', + streamReadable); + } + + if (isDestroyed(streamReadable) || !isReadable(streamReadable)) { + const readable = new ReadableStream(); + readable.cancel(); + return readable; + } + + const objectMode = streamReadable.readableObjectMode; + const highWaterMark = streamReadable.readableHighWaterMark; + + const evaluateStrategyOrFallback = (strategy) => { + // If there is a strategy available, use it + if (strategy) + return strategy; + + if (objectMode) { + // When running in objectMode explicitly but no strategy, we just fall + // back to CountQueuingStrategy + return new CountQueuingStrategy({ highWaterMark }); + } + + // When not running in objectMode explicitly, we just fall + // back to a minimal strategy that just specifies the highWaterMark + // and no size algorithm. Using a ByteLengthQueuingStrategy here + // is unnecessary. + return { highWaterMark }; + }; + + const strategy = evaluateStrategyOrFallback(options?.strategy); + + let controller; + + function onData(chunk) { + // Copy the Buffer to detach it from the pool. + if (Buffer.isBuffer(chunk) && !objectMode) + chunk = new Uint8Array(chunk); + controller.enqueue(chunk); + if (controller.desiredSize <= 0) + streamReadable.pause(); + } + + streamReadable.pause(); + + const cleanup = finished(streamReadable, (error) => { + if (error?.code === 'ERR_STREAM_PREMATURE_CLOSE') { + const err = new AbortError(undefined, { cause: error }); + error = err; + } + + cleanup(); + // This is a protection against non-standard, legacy streams + // that happen to emit an error event again after finished is called. + streamReadable.on('error', () => {}); + if (error) + return controller.error(error); + controller.close(); + }); + + streamReadable.on('data', onData); + + return new ReadableStream({ + start(c) { controller = c; }, + + pull() { streamReadable.resume(); }, + + cancel(reason) { + destroy(streamReadable, reason); + }, + }, strategy); +} + +/** + * @param {ReadableStream} readableStream + * @param {{ + * highWaterMark? : number, + * encoding? : string, + * objectMode? : boolean, + * signal? : AbortSignal, + * }} [options] + * @returns {Readable} + */ +export function newStreamReadableFromReadableStream(readableStream, options = {}) { + if (!(readableStream instanceof ReadableStream)) { + throw new ERR_INVALID_ARG_TYPE( + 'readableStream', + 'ReadableStream', + readableStream); + } + + validateObject(options, 'options'); + const { + highWaterMark, + encoding, + objectMode = false, + signal, + } = options; + + if (encoding !== undefined && !Buffer.isEncoding(encoding)) + throw new ERR_INVALID_ARG_VALUE(encoding, 'options.encoding'); + validateBoolean(objectMode, 'options.objectMode'); + + const reader = readableStream.getReader(); + let closed = false; + + const readable = new Readable({ + objectMode, + highWaterMark, + encoding, + signal, + + read() { + reader.read().then((chunk) => { + if (chunk.done) { + // Value should always be undefined here. + readable.push(null); + } else { + readable.push(chunk.value); + } + }, + (error) => destroy(readable, error)); + }, + + destroy(error, callback) { + function done() { + try { + callback(error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => { throw error; }); + } + } + + if (!closed) { + reader.cancel(error).then(done, done); + return; + } + done(); + }, + }); + + reader.closed.then(() => { + closed = true; + }, + (error) => { + closed = true; + destroy(readable, error); + }); + + return readable; +} + +/** + * @typedef {import('./readablestream').ReadableWritablePair + * } ReadableWritablePair + * @typedef {import('../../stream').Duplex} Duplex + */ + +/** + * @param {Duplex} duplex + * @returns {ReadableWritablePair} + */ +export function newReadableWritablePairFromDuplex(duplex) { + // Not using the internal/streams/utils isWritableNodeStream and + // isReadableNodeStream utilities here because they will return false + // if the duplex was created with writable or readable options set to + // false. Instead, we'll check the readable and writable state after + // and return closed WritableStream or closed ReadableStream as + // necessary. + if (typeof duplex?._writableState !== 'object' || + typeof duplex?._readableState !== 'object') { + throw new ERR_INVALID_ARG_TYPE('duplex', 'stream.Duplex', duplex); + } + + if (isDestroyed(duplex)) { + const writable = new WritableStream(); + const readable = new ReadableStream(); + writable.close(); + readable.cancel(); + return { readable, writable }; + } + + const writable = + isWritable(duplex) ? + newWritableStreamFromStreamWritable(duplex) : + new WritableStream(); + + if (!isWritable(duplex)) + writable.close(); + + const readable = + isReadable(duplex) ? + newReadableStreamFromStreamReadable(duplex) : + new ReadableStream(); + + if (!isReadable(duplex)) + readable.cancel(); + + return { writable, readable }; +} + +/** + * @param {ReadableWritablePair} pair + * @param {{ + * allowHalfOpen? : boolean, + * decodeStrings? : boolean, + * encoding? : string, + * highWaterMark? : number, + * objectMode? : boolean, + * signal? : AbortSignal, + * }} [options] + * @returns {Duplex} + */ +export function newStreamDuplexFromReadableWritablePair(pair = {}, options = {}) { + validateObject(pair, 'pair'); + const { + readable: readableStream, + writable: writableStream, + } = pair; + + if (!(readableStream instanceof ReadableStream)) { + throw new ERR_INVALID_ARG_TYPE( + 'pair.readable', + 'ReadableStream', + readableStream); + } + if (!(writableStream instanceof WritableStream)) { + throw new ERR_INVALID_ARG_TYPE( + 'pair.writable', + 'WritableStream', + writableStream); + } + + validateObject(options, 'options'); + const { + allowHalfOpen = false, + objectMode = false, + encoding, + decodeStrings = true, + highWaterMark, + signal, + } = options; + + validateBoolean(objectMode, 'options.objectMode'); + if (encoding !== undefined && !Buffer.isEncoding(encoding)) + throw new ERR_INVALID_ARG_VALUE(encoding, 'options.encoding'); + + const writer = writableStream.getWriter(); + const reader = readableStream.getReader(); + let writableClosed = false; + let readableClosed = false; + + const duplex = new Duplex({ + allowHalfOpen, + highWaterMark, + objectMode, + encoding, + decodeStrings, + signal, + + writev(chunks, callback) { + function done(error) { + error = error.filter((e) => e); + try { + callback(error.length === 0 ? undefined : error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => destroy(duplex, error)); + } + } + + writer.ready.then(() => { + return Promise.all(chunks.map((data) => { + return writer.write(data); + })).then(done, done); + }, + done); + }, + + write(chunk, encoding, callback) { + if (typeof chunk === 'string' && decodeStrings && !objectMode) { + const enc = normalizeEncoding(encoding); + + if (enc === 'utf8') { + chunk = encoder.encode(chunk); + } else { + chunk = Buffer.from(chunk, encoding); + chunk = new Uint8Array( + chunk.buffer, + chunk.byteOffset, + chunk.byteLength + ); + } + } + + function done(error) { + try { + callback(error); + } catch (error) { + destroy(duplex, error); + } + } + + writer.ready.then(() => { + return writer.write(chunk).then(done, done); + }, + done); + }, + + final(callback) { + function done(error) { + try { + callback(error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => destroy(duplex, error)); + } + } + + if (!writableClosed) { + writer.close().then(done, done); + } + }, + + read() { + reader.read().then((chunk) => { + if (chunk.done) { + duplex.push(null); + } else { + duplex.push(chunk.value); + } + }, + (error) => destroy(duplex, error)); + }, + + destroy(error, callback) { + function done() { + try { + callback(error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => { throw error; }); + } + } + + async function closeWriter() { + if (!writableClosed) + await writer.abort(error); + } + + async function closeReader() { + if (!readableClosed) + await reader.cancel(error); + } + + if (!writableClosed || !readableClosed) { + Promise.all([ closeWriter(), closeReader() ]).then(done, done); + return; + } + + done(); + }, + }); + + writer.closed.then(() => { + writableClosed = true; + if (!isWritableEnded(duplex)) + destroy(duplex, new ERR_STREAM_PREMATURE_CLOSE()); + }, + (error) => { + writableClosed = true; + readableClosed = true; + destroy(duplex, error); + }); + + reader.closed.then(() => { + readableClosed = true; + }, + (error) => { + writableClosed = true; + readableClosed = true; + destroy(duplex, error); + }); + + return duplex; +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_duplex.js b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_duplex.js new file mode 100644 index 000000000..7b2b286c7 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_duplex.js @@ -0,0 +1,479 @@ +import { + Readable, + from, +} from './streams_readable'; + +import { + Writable, +} from './streams_writable'; + +import { + newStreamDuplexFromReadableWritablePair, + newReadableWritablePairFromDuplex, +} from './streams_adapters'; + +import { + createDeferredPromise, +} from './internal_utils'; + +import * as process from './process'; + +import { + destroyer, + eos, + isReadable, + isWritable, + isIterable, + isNodeStream, + isReadableNodeStream, + isWritableNodeStream, + isDuplexNodeStream, +} from './streams_util'; + +import { + AbortError, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_RETURN_VALUE, +} from './internal_errors'; + +Object.setPrototypeOf(Duplex.prototype, Readable.prototype); +Object.setPrototypeOf(Duplex, Readable); +{ + const keys = Object.keys(Writable.prototype); + // Allow the keys array to be GC'ed. + for (let i = 0; i < keys.length; i++) { + const method = keys[i]; + if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; + } +} + +export function isDuplexInstance(obj) { + return obj instanceof Duplex; +} + +export function Duplex(options) { + if (!(this instanceof Duplex)) return new Duplex(options); + Readable.call(this, options); + Writable.call(this, options); + if (options) { + this.allowHalfOpen = options.allowHalfOpen !== false; + if (options.readable === false) { + this._readableState.readable = false; + this._readableState.ended = true; + this._readableState.endEmitted = true; + } + if (options.writable === false) { + this._writableState.writable = false; + this._writableState.ending = true; + this._writableState.ended = true; + this._writableState.finished = true; + } + } else { + this.allowHalfOpen = true; + } +} +Object.defineProperties(Duplex.prototype, { + writable: { + ...Object.getOwnPropertyDescriptor(Writable.prototype, 'writable') + }, + writableHighWaterMark: { + ...Object.getOwnPropertyDescriptor(Writable.prototype, 'writableHighWaterMark') + }, + writableObjectMode: { + ...Object.getOwnPropertyDescriptor(Writable.prototype, 'writableObjectMode') + }, + writableBuffer: { + ...Object.getOwnPropertyDescriptor(Writable.prototype, 'writableBuffer') + }, + writableLength: { + ...Object.getOwnPropertyDescriptor(Writable.prototype, 'writableLength') + }, + writableFinished: { + ...Object.getOwnPropertyDescriptor(Writable.prototype, 'writableFinished') + }, + writableCorked: { + ...Object.getOwnPropertyDescriptor(Writable.prototype, 'writableCorked') + }, + writableEnded: { + ...Object.getOwnPropertyDescriptor(Writable.prototype, 'writableEnded') + }, + writableNeedDrain: { + ...Object.getOwnPropertyDescriptor(Writable.prototype, 'writableNeedDrain') + }, + destroyed: { + get() { + if (this._readableState === undefined || this._writableState === undefined) { + return false + } + return this._readableState.destroyed && this._writableState.destroyed + }, + set(value) { + // Backward compatibility, the user is explicitly + // managing destroyed. + if (this._readableState && this._writableState) { + this._readableState.destroyed = value + this._writableState.destroyed = value + } + } + } +}); + +Duplex.fromWeb = function (pair, options) { + return newStreamDuplexFromReadableWritablePair(pair, options) +} +Duplex.toWeb = function (duplex) { + return newReadableWritablePairFromDuplex(duplex) +} + +// ====================================================================================== + +Duplex.from = function (body) { + return duplexify(body, 'body') +} + +function isBlob(b) { + return b instanceof Blob; +} + +// This is needed for pre node 17. +class Duplexify extends (Duplex) { + constructor(options) { + super(options); + // https://github.com/nodejs/node/pull/34385 + + if ((options === null || options === undefined ? undefined : options.readable) === false) { + this['_readableState'].readable = false; + this['_readableState'].ended = true; + this['_readableState'].endEmitted = true; + } + if ((options === null || options === undefined ? undefined : options.writable) === false) { + this['_readableState'].writable = false; + this['_readableState'].ending = true; + this['_readableState'].ended = true; + this['_readableState'].finished = true; + } + } +} + +function duplexify(body, name) { + if (isDuplexNodeStream(body)) { + return body; + } + if (isReadableNodeStream(body)) { + return _duplexify({ + readable: body + }); + } + if (isWritableNodeStream(body)) { + return _duplexify({ + writable: body + }); + } + if (isNodeStream(body)) { + return _duplexify({ + writable: false, + readable: false + }); + } + + // TODO: Webstreams + // if (isReadableStream(body)) { + // return _duplexify({ readable: Readable.fromWeb(body) }); + // } + + // TODO: Webstreams + // if (isWritableStream(body)) { + // return _duplexify({ writable: Writable.fromWeb(body) }); + // } + + if (typeof body === 'function') { + const { value, write, final, destroy } = fromAsyncGen(body) + if (isIterable(value)) { + return from(Duplexify, value, { + // TODO (ronag): highWaterMark? + objectMode: true, + write, + final, + destroy + }); + } + const then = value.then; + if (typeof then === 'function') { + let d; + const promise = Reflect.apply(then, value, [ + (val) => { + if (val != null) { + throw new ERR_INVALID_RETURN_VALUE('nully', 'body', val) + } + }, + (err) => { + destroyer(d, err) + } + ]); + + return (d = new Duplexify({ + // TODO (ronag): highWaterMark? + objectMode: true, + readable: false, + write, + final(cb) { + final(async () => { + try { + await promise + process.nextTick(cb, null); + } catch (err) { + process.nextTick(cb, err); + } + }) + }, + destroy + })); + } + throw new ERR_INVALID_RETURN_VALUE('Iterable, AsyncIterable or AsyncFunction', name, value) + } + if (isBlob(body)) { + return duplexify(body.arrayBuffer(), name); + } + if (isIterable(body)) { + return from(Duplexify, body, { + // TODO (ronag): highWaterMark? + objectMode: true, + writable: false + }) + } + + // TODO: Webstreams. + // if ( + // isReadableStream(body?.readable) && + // isWritableStream(body?.writable) + // ) { + // return Duplexify.fromWeb(body); + // } + + if ( + typeof (body === null || body === undefined ? undefined : body.writable) === 'object' || + typeof (body === null || body === undefined ? undefined : body.readable) === 'object' + ) { + const readable = + body !== null && body !== undefined && body.readable + ? isReadableNodeStream(body === null || body === undefined ? undefined : body.readable) + ? body === null || body === undefined + ? undefined + : body.readable + : duplexify(body.readable, name) + : undefined; + const writable = + body !== null && body !== undefined && body.writable + ? isWritableNodeStream(body === null || body === undefined ? undefined : body.writable) + ? body === null || body === undefined + ? undefined + : body.writable + : duplexify(body.writable, name) + : undefined; + return _duplexify({ + readable, + writable + }); + } + const then = body?.then + if (typeof then === 'function') { + let d; + Reflect.apply(then, body, [ + (val) => { + if (val != null) { + d.push(val); + } + d.push(null); + }, + (err) => { + destroyer(d, err); + } + ]); + + return (d = new Duplexify({ + objectMode: true, + writable: false, + read() {} + })); + } + throw new ERR_INVALID_ARG_TYPE( + name, + [ + 'Blob', + 'ReadableStream', + 'WritableStream', + 'Stream', + 'Iterable', + 'AsyncIterable', + 'Function', + '{ readable, writable } pair', + 'Promise' + ], + body + ) +} + +function fromAsyncGen(fn) { + let { promise, resolve } = createDeferredPromise() + const ac = new AbortController() + const signal = ac.signal + const value = fn( + (async function* () { + while (true) { + const _promise = promise; + promise = null; + const { chunk, done, cb } = await _promise; + process.nextTick(cb); + if (done) return; + if (signal.aborted) + throw new AbortError(undefined, { + cause: signal.reason + }); + ({ promise, resolve } = createDeferredPromise()); + yield chunk; + } + })(), + { + signal + } + ); + return { + value, + write(chunk, _encoding, cb) { + const _resolve = resolve; + resolve = null; + _resolve({ + chunk, + done: false, + cb + }) + }, + final(cb) { + const _resolve = resolve; + resolve = null; + (_resolve)({ + done: true, + cb + }); + }, + destroy(err, cb) { + ac.abort(); + cb(err); + } + } +} + +function _duplexify(pair) { + const r = pair.readable && typeof pair.readable.read !== 'function' ? Readable.wrap(pair.readable) : pair.readable; + const w = pair.writable; + let readable = !!isReadable(r); + let writable = !!isWritable(w); + let ondrain; + let onfinish; + let onreadable; + let onclose; + let d; + function onfinished(err) { + const cb = onclose + onclose = null + if (cb) { + cb(err) + } else if (err) { + d.destroy(err) + } else if (!readable && !writable) { + d.destroy() + } + } + + // TODO(ronag): Avoid double buffering. + // Implement Writable/Readable/Duplex traits. + // See, https://github.com/nodejs/node/pull/33515. + d = new Duplexify({ + // TODO (ronag): highWaterMark? + readableObjectMode: !!(r !== null && r !== undefined && r.readableObjectMode), + writableObjectMode: !!(w !== null && w !== undefined && w.writableObjectMode), + readable, + writable + }); + if (writable) { + eos(w, (err) => { + writable = false; + if (err) { + destroyer(r, err); + } + onfinished(err); + }); + d._write = function (chunk, encoding, callback) { + if (w.write(chunk, encoding)) { + callback(); + } else { + ondrain = callback; + } + }; + d._final = function (callback) { + w.end(); + onfinish = callback; + }; + w.on('drain', function () { + if (ondrain) { + const cb = ondrain; + ondrain = null; + cb(); + } + }); + w.on('finish', function () { + if (onfinish) { + const cb = onfinish; + onfinish = null; + cb(); + } + }); + } + if (readable) { + eos(r, (err) => { + readable = false; + if (err) { + destroyer(r, err); + } + onfinished(err); + }); + r.on('readable', function () { + if (onreadable) { + const cb = onreadable; + onreadable = null; + cb(); + } + }); + r.on('end', function () { + d.push(null); + }); + d._read = function () { + while (true) { + const buf = r.read(); + if (buf === null) { + onreadable = d._read; + return; + } + if (!d.push(buf)) { + return; + } + } + }; + } + d._destroy = function (err, callback) { + if (!err && onclose !== null) { + err = new AbortError(); + } + onreadable = null; + ondrain = null; + onfinish = null; + if (onclose === null) { + callback(err); + } else { + onclose = callback; + destroyer(w, err); + destroyer(r, err); + } + } + return d; +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_legacy.js b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_legacy.js new file mode 100644 index 000000000..f0b4473df --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_legacy.js @@ -0,0 +1,87 @@ + +import + EventEmitter from './events'; + +import { + Buffer, +} from './internal_buffer'; + +export function Stream(opts) { + EventEmitter.call(this, opts||{}); +} + +Object.setPrototypeOf(Stream.prototype, EventEmitter.prototype); +Object.setPrototypeOf(Stream, EventEmitter); + +Stream.prototype.pipe = function (dest, options) { + const source = this; + function ondata(chunk) { + if (dest.writable && dest.write(chunk) === false && source.pause) { + source.pause(); + } + } + source.on('data', ondata); + function ondrain() { + if (source.readable && source.resume) { + source.resume(); + } + } + dest.on('drain', ondrain); + + // If the 'end' option is not supplied, dest.end() will be called when + // source gets the 'end' or 'close' events. Only dest.end() once. + if (!dest._isStdio && (!options || options.end !== false)) { + source.on('end', onend); + source.on('close', onclose); + } + let didOnEnd = false; + function onend() { + if (didOnEnd) return; + didOnEnd = true; + dest.end(); + } + function onclose() { + if (didOnEnd) return; + didOnEnd = true; + if (typeof dest.destroy === 'function') dest.destroy(); + } + + // Don't leave dangling pipes when there are errors. + function onerror(er) { + cleanup(); + if (EventEmitter.listenerCount(this, 'error') === 0) { + this.emit('error', er); + } + } + source.prependListener('error', onerror); + dest.prependListener('error', onerror); + + // Remove all the event listeners that were added. + function cleanup() { + source.removeListener('data', ondata); + dest.removeListener('drain', ondrain); + source.removeListener('end', onend); + source.removeListener('close', onclose); + source.removeListener('error', onerror); + dest.removeListener('error', onerror); + source.removeListener('end', cleanup); + source.removeListener('close', cleanup); + dest.removeListener('close', cleanup); + } + source.on('end', cleanup); + source.on('close', cleanup); + dest.on('close', cleanup); + dest.emit('pipe', source); + + // Allow for unix-like usage: A.pipe(B).pipe(C) + return dest; +} + +// Backwards-compat with node 0.4.x +Stream.Stream = Stream +Stream._isUint8Array = function isUint8Array(value) { + return value instanceof Uint8Array; +} +Stream._uint8ArrayToBuffer = function _uint8ArrayToBuffer(chunk) { + return Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength); +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_readable.js b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_readable.js new file mode 100644 index 000000000..0066ff927 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_readable.js @@ -0,0 +1,1757 @@ +/* eslint-disable */ + +import { + nop, + getHighWaterMark, + getDefaultHighWaterMark, + kPaused, + addAbortSignal, + BufferList, + eos, + construct, + destroy, + destroyer, + undestroy, + errorOrDestroy, + finished, +} from './streams_util'; + +import * as process from './process'; + +import EventEmitter from './events'; + +import { + Stream, +} from './streams_legacy'; + +import { + newStreamReadableFromReadableStream, + newReadableStreamFromStreamReadable, +} from './streams_adapters'; + +import { + Buffer, +} from './internal_buffer'; + +import { + AbortError, + aggregateTwoErrors, + ERR_INVALID_ARG_TYPE, + ERR_METHOD_NOT_IMPLEMENTED, + ERR_MISSING_ARGS, + ERR_OUT_OF_RANGE, + ERR_STREAM_PUSH_AFTER_EOF, + ERR_STREAM_UNSHIFT_AFTER_END_EVENT, + ERR_STREAM_NULL_VALUES, +} from './internal_errors'; + +import { + validateObject, + validateAbortSignal, + validateInteger, +} from './validators'; + +import { StringDecoder } from './internal_stringdecoder'; + +import { isDuplexInstance } from './streams_duplex'; + +// ====================================================================================== +// ReadableState + +function ReadableState(options, stream, isDuplex) { + // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream. + // These options can be provided separately as readableXXX and writableXXX. + if (typeof isDuplex !== 'boolean') isDuplex = isDuplexInstance(stream); + + // Object stream flag. Used to make read(n) ignore n and to + // make all the buffer merging and length checks go away. + this.objectMode = !!(options?.objectMode); + if (isDuplex) this.objectMode = this.objectMode || !!(options?.readableObjectMode); + + // The point at which it stops calling _read() to fill the buffer + // Note: 0 is a valid value, means "don't call _read preemptively ever" + this.highWaterMark = options + ? getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex) + : getDefaultHighWaterMark(false); + + // A linked list is used to store data chunks instead of an array because the + // linked list can remove elements from the beginning faster than + // array.shift(). + this.buffer = new BufferList(); + this.length = 0; + this.pipes = []; + this.flowing = null; + this.ended = false; + this.endEmitted = false; + this.reading = false; + + // Stream is still being constructed and cannot be + // destroyed until construction finished or failed. + // Async construction is opt in, therefore we start as + // constructed. + this.constructed = true; + + // A flag to be able to tell if the event 'readable'/'data' is emitted + // immediately, or on a later tick. We set this to true at first, because + // any actions that shouldn't happen until "later" should generally also + // not happen before the first read call. + this.sync = true; + + // Whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + this.needReadable = false; + this.emittedReadable = false; + this.readableListening = false; + this.resumeScheduled = false; + this[kPaused] = null; + + // True if the error was already emitted and should not be thrown again. + this.errorEmitted = false; + + // Should close be emitted on destroy. Defaults to true. + this.emitClose = !options || options.emitClose !== false; + + // Should .destroy() be called after 'end' (and potentially 'finish'). + this.autoDestroy = !options || options.autoDestroy !== false; + + // Has it been destroyed. + this.destroyed = false; + + // Indicates whether the stream has errored. When true no further + // _read calls, 'data' or 'readable' events should occur. This is needed + // since when autoDestroy is disabled we need a way to tell whether the + // stream has failed. + this.errored = null; + + // Indicates whether the stream has finished destroying. + this.closed = false; + + // True if close has been emitted or would have been emitted + // depending on emitClose. + this.closeEmitted = false; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options?.defaultEncoding || 'utf8'; + + // Ref the piped dest which we need a drain event on it + // type: null | Writable | Set. + this.awaitDrainWriters = null; + this.multiAwaitDrain = false; + + // If true, a maybeReadMore has been scheduled. + this.readingMore = false; + this.dataEmitted = false; + this.decoder = null; + this.encoding = null; + if (options && options.encoding) { + this.decoder = new StringDecoder(options.encoding); + this.encoding = options.encoding; + } +} + +// ====================================================================================== +// Readable + +Readable.ReadableState = ReadableState; + +Object.setPrototypeOf(Readable.prototype, Stream.prototype); +Object.setPrototypeOf(Readable, Stream); + +export function Readable(options) { + if (!(this instanceof Readable)) return new Readable(options); + + // Checking for a Stream.Duplex instance is faster here instead of inside + // the ReadableState constructor, at least with V8 6.5. + const isDuplex = isDuplexInstance(this); + this._readableState = new ReadableState(options, this, isDuplex); + if (options) { + if (typeof options.read === 'function') this._read = options.read; + if (typeof options.destroy === 'function') this._destroy = options.destroy; + if (typeof options.construct === 'function') this._construct = options.construct; + if (options.signal && !isDuplex) addAbortSignal(options.signal, this); + } + Stream.call(this, options); + construct(this, () => { + if (this._readableState.needReadable) { + maybeReadMore(this, this._readableState); + } + }); +} +Readable.prototype.destroy = destroy +Readable.prototype._undestroy = undestroy +Readable.prototype._destroy = function (err, cb) { + if (cb) cb(err); +} + +Readable.prototype[EventEmitter.captureRejectionSymbol] = function (err) { + this.destroy(err); +} + +// Manually shove something into the read() buffer. +// This returns true if the highWaterMark has not been hit yet, +// similar to how Writable.write() returns true if you should +// write() some more. +Readable.prototype.push = function (chunk, encoding) { + return readableAddChunk(this, chunk, encoding, false); +} + +// Unshift should *always* be something directly out of read(). +Readable.prototype.unshift = function (chunk, encoding) { + return readableAddChunk(this, chunk, encoding, true); +} + +function readableAddChunk(stream, chunk, encoding, addToFront) { + const state = stream._readableState; + let err; + if (!state.objectMode) { + if (typeof chunk === 'string') { + encoding ||= state.defaultEncoding; + if (state.encoding !== encoding) { + if (addToFront && state.encoding) { + // When unshifting, if state.encoding is set, we have to save + // the string in the BufferList with the state encoding. + chunk = Buffer.from(chunk, encoding).toString(state.encoding); + } else { + chunk = Buffer.from(chunk, encoding); + encoding = ''; + } + } + } else if (chunk instanceof Buffer) { + encoding = '' + } else if (Stream._isUint8Array(chunk)) { + chunk = Stream._uint8ArrayToBuffer(chunk); + encoding = ''; + } else if (chunk != null) { + err = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk); + } + } + if (err) { + errorOrDestroy(stream, err); + } else if (chunk === null) { + state.reading = false; + onEofChunk(stream, state); + } else if (state.objectMode || (chunk && chunk.length > 0)) { + if (addToFront) { + if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT()); + else if (state.destroyed || state.errored) return false; + else addChunk(stream, state, chunk, true); + } else if (state.ended) { + errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF()); + } else if (state.destroyed || state.errored) { + return false; + } else { + state.reading = false; + if (state.decoder && !encoding) { + chunk = state.decoder.write(chunk); + if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false); + else maybeReadMore(stream, state); + } else { + addChunk(stream, state, chunk, false); + } + } + } else if (!addToFront) { + state.reading = false; + maybeReadMore(stream, state); + } + + // We can push more data if we are below the highWaterMark. + // Also, if we have no data yet, we can stand some more bytes. + // This is to work around cases where hwm=0, such as the repl. + return !state.ended && (state.length < state.highWaterMark || state.length === 0); +} + +function addChunk(stream, state, chunk, addToFront) { + if (state.flowing && state.length === 0 && !state.sync && stream.listenerCount('data') > 0) { + // Use the guard to avoid creating `Set()` repeatedly + // when we have multiple pipes. + if (state.multiAwaitDrain) { + state.awaitDrainWriters.clear(); + } else { + state.awaitDrainWriters = null; + state.multiAwaitDrain = false; + } + state.dataEmitted = true; + stream.emit('data', chunk); + } else { + // Update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) state.buffer.unshift(chunk); + else state.buffer.push(chunk); + if (state.needReadable) emitReadable(stream); + } + maybeReadMore(stream, state); +} + +Readable.prototype.isPaused = function () { + const state = this._readableState; + return state[kPaused] === true || state.flowing === false; +} + +// Backwards compatibility. +Readable.prototype.setEncoding = function (enc) { + const decoder = new StringDecoder(enc); + this._readableState.decoder = decoder; + // If setEncoding(null), decoder.encoding equals utf8. + this._readableState.encoding = decoder.encoding; + const buffer = this._readableState.buffer; + // Iterate over current buffer to convert already stored Buffers: + let content = ''; + for (const data of buffer) { + content += decoder.write(data); + } + buffer.clear(); + if (content !== '') buffer.push(content); + this._readableState.length = content.length; + return this; +} + +// Don't raise the hwm > 1GB. +const MAX_HWM = 0x40000000; +function computeNewHighWaterMark(n) { + if (n > MAX_HWM) { + throw new ERR_OUT_OF_RANGE('size', '<= 1GiB', n); + } else { + // Get the next highest power of 2 to prevent increasing hwm excessively in + // tiny amounts. + n--; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + n++; + } + return n; +} + +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function howMuchToRead(n, state) { + if (n <= 0 || (state.length === 0 && state.ended)) return 0; + if (state.objectMode) return 1; + if (Number.isNaN(n)) { + // Only flow one buffer at a time. + if (state.flowing && state.length) return state.buffer.first().length; + return state.length; + } + if (n <= state.length) return n; + return state.ended ? state.length : 0; +} + +// You can override either this method, or the async _read(n) below. +Readable.prototype.read = function (n) { + // Same as parseInt(undefined, 10), however V8 7.3 performance regressed + // in this scenario, so we are doing it manually. + if (n === undefined) { + n = NaN; + } else if (!Number.isInteger(n)) { + n = Number.parseInt(`${n}`, 10); + } + const state = this._readableState; + const nOrig = n; + + // If we're asking for more than the current hwm, then raise the hwm. + if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n); + if (n !== 0) state.emittedReadable = false; + + // If we're doing read(0) to trigger a readable event, but we + // already have a bunch of data in the buffer, then just trigger + // the 'readable' event and move on. + if ( + n === 0 && + state.needReadable && + ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) { + if (state.length === 0 && state.ended) endReadable(this); + else emitReadable(this); + return null; + } + n = howMuchToRead(n, state); + + // If we've ended, and we're now clear, then finish it up. + if (n === 0 && state.ended) { + if (state.length === 0) endReadable(this); + return null; + } + + // All the actual chunk generation logic needs to be + // *below* the call to _read. The reason is that in certain + // synthetic stream cases, such as passthrough streams, _read + // may be a completely synchronous operation which may change + // the state of the read buffer, providing enough data when + // before there was *not* enough. + // + // So, the steps are: + // 1. Figure out what the state of things will be after we do + // a read from the buffer. + // + // 2. If that resulting state will trigger a _read, then call _read. + // Note that this may be asynchronous, or synchronous. Yes, it is + // deeply ugly to write APIs this way, but that still doesn't mean + // that the Readable class should behave improperly, as streams are + // designed to be sync/async agnostic. + // Take note if the _read call is sync or async (ie, if the read call + // has returned yet), so that we know whether or not it's safe to emit + // 'readable' etc. + // + // 3. Actually pull the requested chunks out of the buffer and return. + + // if we need a readable event, then we need to do some reading. + let doRead = state.needReadable; + + // If we currently have less than the highWaterMark, then also read some. + if (state.length === 0 || state.length - n < state.highWaterMark) { + doRead = true; + } + + // However, if we've ended, then there's no point, if we're already + // reading, then it's unnecessary, if we're constructing we have to wait, + // and if we're destroyed or errored, then it's not allowed, + if (state.ended || state.reading || state.destroyed || state.errored || !state.constructed) { + doRead = false; + } else if (doRead) { + state.reading = true; + state.sync = true; + // If the length is currently zero, then we *need* a readable event. + if (state.length === 0) state.needReadable = true; + + // Call internal read method + try { + this._read(state.highWaterMark); + } catch (err) { + errorOrDestroy(this, err); + } + state.sync = false; + // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + if (!state.reading) n = howMuchToRead(nOrig, state); + } + let ret; + if (n > 0) ret = fromList(n, state); + else ret = null; + if (ret === null) { + state.needReadable = state.length <= state.highWaterMark; + n = 0; + } else { + state.length -= n; + if (state.multiAwaitDrain) { + state.awaitDrainWriters.clear(); + } else { + state.awaitDrainWriters = null; + state.multiAwaitDrain = false; + } + } + if (state.length === 0) { + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (!state.ended) state.needReadable = true + + // If we tried to read() past the EOF, then emit end on the next tick. + if (nOrig !== n && state.ended) endReadable(this) + } + if (ret !== null && !state.errorEmitted && !state.closeEmitted) { + state.dataEmitted = true + this.emit('data', ret) + } + return ret +} + +function onEofChunk(stream, state) { + if (state.ended) return; + if (state.decoder) { + const chunk = state.decoder.end(); + if (chunk && chunk.length) { + state.buffer.push(chunk); + state.length += state.objectMode ? 1 : chunk.length; + } + } + state.ended = true; + if (state.sync) { + // If we are sync, wait until next tick to emit the data. + // Otherwise we risk emitting data in the flow() + // the readable code triggers during a read() call. + emitReadable(stream); + } else { + // Emit 'readable' now to make sure it gets picked up. + state.needReadable = false; + state.emittedReadable = true; + // We have to emit readable now that we are EOF. Modules + // in the ecosystem (e.g. dicer) rely on this event being sync. + emitReadable_(stream); + } +} + +// Don't emit readable right away in sync mode, because this can trigger +// another read() call => stack overflow. This way, it might trigger +// a nextTick recursion warning, but that's not so bad. +function emitReadable(stream) { + const state = stream._readableState; + state.needReadable = false; + if (!state.emittedReadable) { + state.emittedReadable = true; + process.nextTick(emitReadable_, stream); + } +} + +function emitReadable_(stream) { + const state = stream._readableState; + if (!state.destroyed && !state.errored && (state.length || state.ended)) { + stream.emit('readable'); + state.emittedReadable = false; + } + + // The stream needs another readable event if: + // 1. It is not flowing, as the flow mechanism will take + // care of it. + // 2. It is not ended. + // 3. It is below the highWaterMark, so we can schedule + // another readable later. + state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark; + flow(stream); +} + +// At this point, the user has presumably seen the 'readable' event, +// and called read() to consume some data. that may have triggered +// in turn another _read(n) call, in which case reading = true if +// it's in progress. +// However, if we're not ended, or reading, and the length < hwm, +// then go ahead and try to read some more preemptively. +function maybeReadMore(stream, state) { + if (!state.readingMore && state.constructed) { + state.readingMore = true; + process.nextTick(maybeReadMore_, stream, state); + } +} + +function maybeReadMore_(stream, state) { + // Attempt to read more data if we should. + // + // The conditions for reading more data are (one of): + // - Not enough data buffered (state.length < state.highWaterMark). The loop + // is responsible for filling the buffer with enough data if such data + // is available. If highWaterMark is 0 and we are not in the flowing mode + // we should _not_ attempt to buffer any extra data. We'll get more data + // when the stream consumer calls read() instead. + // - No data in the buffer, and the stream is in flowing mode. In this mode + // the loop below is responsible for ensuring read() is called. Failing to + // call read here would abort the flow and there's no other mechanism for + // continuing the flow if the stream consumer has just subscribed to the + // 'data' event. + // + // In addition to the above conditions to keep reading data, the following + // conditions prevent the data from being read: + // - The stream has ended (state.ended). + // - There is already a pending 'read' operation (state.reading). This is a + // case where the stream has called the implementation defined _read() + // method, but they are processing the call asynchronously and have _not_ + // called push() with new data. In this case we skip performing more + // read()s. The execution ends in this method again after the _read() ends + // up calling push() with more data. + while ( + !state.reading && + !state.ended && + (state.length < state.highWaterMark || (state.flowing && state.length === 0)) + ) { + const len = state.length; + stream.read(0); + if (len === state.length) + // Didn't get any data, stop spinning. + break; + } + state.readingMore = false; +} + +// Abstract method. to be overridden in specific implementation classes. +// call cb(er, data) where data is <= n in length. +// for virtual (non-string, non-buffer) streams, "length" is somewhat +// arbitrary, and perhaps not very meaningful. +Readable.prototype._read = function (_size) { + throw new ERR_METHOD_NOT_IMPLEMENTED('_read()'); +} + +Readable.prototype.pipe = function (dest, pipeOpts) { + const src = this; + const state = this._readableState; + if (state.pipes.length === 1) { + if (!state.multiAwaitDrain) { + state.multiAwaitDrain = true; + state.awaitDrainWriters = new Set( + state.awaitDrainWriters ? [state.awaitDrainWriters] : []); + } + } + state.pipes.push(dest); + const doEnd = (!pipeOpts || pipeOpts.end !== false); + const endFn = doEnd ? onend : unpipe; + if (state.endEmitted) process.nextTick(endFn); + else src.once('end', endFn); + dest.on('unpipe', onunpipe); + function onunpipe(readable, unpipeInfo) { + if (readable === src) { + if (unpipeInfo && unpipeInfo.hasUnpiped === false) { + unpipeInfo.hasUnpiped = true; + cleanup(); + } + } + } + function onend() { + dest.end(); + } + let ondrain; + let cleanedUp = false; + function cleanup() { + // Cleanup event handlers once the pipe is broken. + dest.removeListener('close', onclose); + dest.removeListener('finish', onfinish); + if (ondrain) { + dest.removeListener('drain', ondrain); + } + dest.removeListener('error', onerror); + dest.removeListener('unpipe', onunpipe); + src.removeListener('end', onend); + src.removeListener('end', unpipe); + src.removeListener('data', ondata); + cleanedUp = true; + + // If the reader is waiting for a drain event from this + // specific writer, then it would cause it to never start + // flowing again. + // So, if this is awaiting a drain, then we just call it now. + // If we don't know, then assume that we are waiting for one. + if (ondrain && state.awaitDrainWriters && (!dest._writableState || dest._writableState.needDrain)) ondrain(); + } + function pause() { + // If the user unpiped during `dest.write()`, it is possible + // to get stuck in a permanently paused state if that write + // also returned false. + // => Check whether `dest` is still a piping destination. + if (!cleanedUp) { + if (state.pipes.length === 1 && state.pipes[0] === dest) { + state.awaitDrainWriters = dest; + state.multiAwaitDrain = false; + } else if (state.pipes.length > 1 && state.pipes.includes(dest)) { + state.awaitDrainWriters.add(dest); + } + src.pause(); + } + if (!ondrain) { + // When the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + ondrain = pipeOnDrain(src, dest); + dest.on('drain', ondrain); + } + } + src.on('data', ondata); + function ondata(chunk) { + const ret = dest.write(chunk); + if (ret === false) { + pause(); + } + } + + // If the dest has an error, then stop piping into it. + // However, don't suppress the throwing behavior for this. + function onerror(er) { + unpipe(); + dest.removeListener('error', onerror); + if (dest.listenerCount('error') === 0) { + const s = dest._writableState || dest._readableState; + if (s && !s.errorEmitted) { + // User incorrectly emitted 'error' directly on the stream. + errorOrDestroy(dest, er); + } else { + dest.emit('error', er); + } + } + } + + // Make sure our error handler is attached before userland ones. + dest.prependListener('error', onerror); + + // Both close and finish should trigger unpipe, but only once. + function onclose() { + dest.removeListener('finish', onfinish); + unpipe(); + } + dest.once('close', onclose); + function onfinish() { + dest.removeListener('close', onclose); + unpipe(); + } + dest.once('finish', onfinish); + function unpipe() { + src.unpipe(dest); + } + + // Tell the dest that it's being piped to. + dest.emit('pipe', src); + + // Start the flow if it hasn't been started already. + + if (dest.writableNeedDrain === true) { + if (state.flowing) { + pause(); + } + } else if (!state.flowing) { + src.resume(); + } + return dest; +} + +function pipeOnDrain(src, dest) { + return function pipeOnDrainFunctionResult() { + const state = src._readableState; + + // `ondrain` will call directly, + // `this` maybe not a reference to dest, + // so we use the real dest here. + if (state.awaitDrainWriters === dest) { + state.awaitDrainWriters = null; + } else if (state.multiAwaitDrain) { + state.awaitDrainWriters.delete(dest); + } + if ((!state.awaitDrainWriters || state.awaitDrainWriters.size === 0) && + src.listenerCount('data')) { + src.resume(); + } + } +} + +Readable.prototype.unpipe = function (dest) { + const state = this._readableState; + const unpipeInfo = { + hasUnpiped: false + }; + + // If we're not piping anywhere, then do nothing. + if (state.pipes.length === 0) return this; + if (!dest) { + // remove all. + const dests = state.pipes; + state.pipes = []; + this.pause(); + for (let i = 0; i < dests.length; i++) + dests[i].emit('unpipe', this, { + hasUnpiped: false + }) + return this; + } + + // Try to find the right one. + const index = state.pipes.indexOf(dest); + if (index === -1) return this; + state.pipes.splice(index, 1); + if (state.pipes.length === 0) this.pause(); + dest.emit('unpipe', this, unpipeInfo); + return this; +} + +// Set up data events if they are asked for +// Ensure readable listeners eventually get something. +Readable.prototype.on = function (ev, fn) { + const res = Stream.prototype.on.call(this, ev, fn); + const state = this._readableState; + if (ev === 'data') { + // Update readableListening so that resume() may be a no-op + // a few lines down. This is needed to support once('readable'). + state.readableListening = this.listenerCount('readable') > 0; + + // Try start flowing on next tick if stream isn't explicitly paused. + if (state.flowing !== false) this.resume(); + } else if (ev === 'readable') { + if (!state.endEmitted && !state.readableListening) { + state.readableListening = state.needReadable = true; + state.flowing = false; + state.emittedReadable = false; + if (state.length) { + emitReadable(this); + } else if (!state.reading) { + process.nextTick(nReadingNextTick, this); + } + } + } + return res; +} +Readable.prototype.addListener = Readable.prototype.on +Readable.prototype.removeListener = function (ev, fn) { + const res = Stream.prototype.removeListener.call(this, ev, fn); + if (ev === 'readable') { + // We need to check if there is someone still listening to + // readable and reset the state. However this needs to happen + // after readable has been emitted but before I/O (nextTick) to + // support once('readable', fn) cycles. This means that calling + // resume within the same tick will have no + // effect. + process.nextTick(updateReadableListening, this); + } + return res; +} +Readable.prototype.off = Readable.prototype.removeListener +Readable.prototype.removeAllListeners = function (ev) { + const res = Stream.prototype.removeAllListeners.apply(this, arguments); + if (ev === 'readable' || ev === undefined) { + // We need to check if there is someone still listening to + // readable and reset the state. However this needs to happen + // after readable has been emitted but before I/O (nextTick) to + // support once('readable', fn) cycles. This means that calling + // resume within the same tick will have no + // effect. + process.nextTick(updateReadableListening, this); + } + return res; +} + +function updateReadableListening(self) { + const state = self._readableState; + state.readableListening = self.listenerCount('readable') > 0; + if (state.resumeScheduled && state[kPaused] === false) { + // Flowing needs to be set to true now, otherwise + // the upcoming resume will not flow. + state.flowing = true; + + // Crude way to check if we should resume. + } else if (self.listenerCount('data') > 0) { + self.resume(); + } else if (!state.readableListening) { + state.flowing = null; + } +} + +function nReadingNextTick(self) { + self.read(0); +} + +// pause() and resume() are remnants of the legacy readable stream API +// If the user uses them, then switch into old mode. +Readable.prototype.resume = function () { + const state = this._readableState; + if (!state.flowing) { + // We flow only if there is no one listening + // for readable, but we still have to call + // resume(). + state.flowing = !state.readableListening; + resume(this, state); + } + state[kPaused] = false; + return this; +} + +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + process.nextTick(resume_, stream, state); + } +} + +function resume_(stream, state) { + if (!state.reading) { + stream.read(0); + } + state.resumeScheduled = false; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) stream.read(0); +} + +Readable.prototype.pause = function () { + if (this._readableState.flowing !== false) { + this._readableState.flowing = false; + this.emit('pause'); + } + this._readableState[kPaused] = true; + return this; +} + +function flow(stream) { + const state = stream._readableState; + while (state.flowing && stream.read() !== null); +} + +// Wrap an old-style stream as the async data source. +// This is *not* part of the readable stream interface. +// It is an ugly unfortunate mess of history. +Readable.prototype.wrap = function (stream) { + let paused = false; + + // TODO (ronag): Should this.destroy(err) emit + // 'error' on the wrapped stream? Would require + // a static factory method, e.g. Readable.wrap(stream). + stream.on('data', (chunk) => { + if (!this.push(chunk) && stream.pause) { + paused = true; + stream.pause(); + } + }); + + stream.on('end', () => { + this.push(null); + }); + stream.on('error', (err) => { + errorOrDestroy(this, err); + }); + stream.on('close', () => { + this.destroy(); + }); + stream.on('destroy', () => { + this.destroy(); + }); + this._read = () => { + if (paused && stream.resume) { + paused = false; + stream.resume(); + } + }; + + // Proxy all the other methods. Important when wrapping filters and duplexes. + const streamKeys = Object.keys(stream); + for (let j = 1; j < streamKeys.length; j++) { + const i = streamKeys[j]; + if (this[i] === undefined && typeof stream[i] === 'function') { + this[i] = stream[i].bind(stream); + } + } + return this; +} + +Readable.prototype[Symbol.asyncIterator] = function () { + return streamToAsyncIterator(this); +} + +Readable.prototype.iterator = function (options) { + if (options !== undefined) { + validateObject(options, 'options', options); + } + return streamToAsyncIterator(this, options); +} + +function streamToAsyncIterator(stream, options) { + if (typeof stream.read !== 'function') { + stream = Readable.wrap(stream, { + objectMode: true + }); + } + const iter = createAsyncIterator(stream, options); + iter.stream = stream; + return iter; +} + +async function* createAsyncIterator(stream, options) { + let callback = nop; + function next(resolve) { + if (this === stream) { + callback(); + callback = nop; + } else { + callback = resolve; + } + } + stream.on('readable', next); + let error; + const cleanup = eos( + stream, + { + writable: false + }, + (err) => { + error = err ? aggregateTwoErrors(error, err) : null + callback() + callback = nop + } + ); + try { + while (true) { + const chunk = stream.destroyed ? null : stream.read(); + if (chunk !== null) { + yield chunk; + } else if (error) { + throw error; + } else if (error === null) { + return; + } else { + await new Promise(next); + } + } + } catch (err) { + error = aggregateTwoErrors(error, err); + throw error; + } finally { + if ( + (error || (options === null || options === undefined ? undefined : options.destroyOnReturn) !== false) && + (error === undefined || stream._readableState.autoDestroy) + ) { + destroyer(stream, null); + } else { + stream.off('readable', next); + cleanup(); + } + } +} + +// Making it explicit these properties are not enumerable +// because otherwise some prototype manipulation in +// userland will fail. +Object.defineProperties(Readable.prototype, { + readable: { + get() { + const r = this._readableState; + // r.readable === false means that this is part of a Duplex stream + // where the readable side was disabled upon construction. + // Compat. The user might manually disable readable side through + // deprecated setter. + return !!r && r.readable !== false && !r.destroyed && !r.errorEmitted && !r.endEmitted; + }, + set(val) { + // Backwards compat. + if (this._readableState) { + this._readableState.readable = !!val; + } + } + }, + readableDidRead: { + enumerable: false, + get: function () { + return !!(this._readableState?.dataEmitted); + } + }, + readableAborted: { + enumerable: false, + get: function () { + return !!( + this._readableState?.readable !== false && + (this._readableState?.destroyed || this._readableState?.errored) && + !this._readableState?.endEmitted + ); + } + }, + readableHighWaterMark: { + enumerable: false, + get: function () { + return this._readableState?.highWaterMark; + } + }, + readableBuffer: { + enumerable: false, + get: function () { + return this._readableState?.buffer; + } + }, + readableFlowing: { + enumerable: false, + get: function () { + return !!(this._readableState?.flowing); + }, + set: function (state) { + if (this._readableState) { + this._readableState.flowing = state + } + } + }, + readableLength: { + enumerable: false, + get() { + return this._readableState?.length | 0; + } + }, + readableObjectMode: { + enumerable: false, + get() { + return this._readableState ? this._readableState.objectMode : false; + } + }, + readableEncoding: { + enumerable: false, + get() { + return this._readableState?.encoding || null; + } + }, + errored: { + enumerable: false, + get() { + return this._readableState?.errored || null; + } + }, + closed: { + get() { + return !!(this._readableState?.closed); + } + }, + destroyed: { + enumerable: false, + get() { + return !!(this._readableState?.destroyed); + }, + set(value) { + // We ignore the value if the stream + // has not been initialized yet. + if (!this._readableState) { + return + } + + // Backward compatibility, the user is explicitly + // managing destroyed. + this._readableState.destroyed = value + } + }, + readableEnded: { + enumerable: false, + get() { + return !!(this._readableState?.endEmitted); + } + } +}); + +Object.defineProperties(ReadableState.prototype, { + // Legacy getter for `pipesCount`. + pipesCount: { + get() { + return this.pipes.length + } + }, + // Legacy property for `paused`. + paused: { + get() { + return this[kPaused] !== false + }, + set(value) { + this[kPaused] = !!value + } + } +}); + +// Exposed for testing purposes only. +Readable._fromList = fromList + +// Pluck off n bytes from an array of buffers. +// Length is the combined lengths of all the buffers in the list. +// This function is designed to be inlinable, so please take care when making +// changes to the function body. +function fromList(n, state) { + // nothing buffered. + if (state.length === 0) return null + let ret + if (state.objectMode) ret = state.buffer.shift() + else if (!n || n >= state.length) { + // Read it all, truncate the list. + if (state.decoder) ret = state.buffer.join('') + else if (state.buffer.length === 1) ret = state.buffer.first(); + else ret = state.buffer.concat(state.length); + state.buffer.clear() + } else { + // read part of list. + ret = state.buffer.consume(n, !!state.decoder); + } + return ret +} + +function endReadable(stream) { + const state = stream._readableState + if (!state.endEmitted) { + state.ended = true; + process.nextTick(endReadableNT, state, stream); + } +} + +function endReadableNT(state, stream) { + // Check that we didn't get one last unshift. + if (!state.errored && !state.closeEmitted && !state.endEmitted && state.length === 0) { + state.endEmitted = true + stream.emit('end') + if (stream.writable && stream.allowHalfOpen === false) { + process.nextTick(endWritableNT, stream); + } else if (state.autoDestroy) { + // In case of duplex streams we need a way to detect + // if the writable side is ready for autoDestroy as well. + const wState = stream._writableState + const autoDestroy = + !wState || + (wState.autoDestroy && + // We don't expect the writable to ever 'finish' + // if writable is explicitly set to false. + (wState.finished || wState.writable === false)) + if (autoDestroy) { + stream.destroy() + } + } + } +} + +function endWritableNT(stream) { + const writable = stream.writable && !stream.writableEnded && !stream.destroyed + if (writable) { + stream.end() + } +} + +Readable.fromWeb = function (readableStream, options) { + return newStreamReadableFromReadableStream(readableStream, options) +} +Readable.toWeb = function (streamReadable, options) { + return newReadableStreamFromStreamReadable(streamReadable, options) +} + +Readable.wrap = function (src, options) { + let _ref, _src$readableObjectMo; + return new Readable({ + objectMode: + (_ref = + (_src$readableObjectMo = src.readableObjectMode) !== null && _src$readableObjectMo !== undefined + ? _src$readableObjectMo + : src.objectMode) !== null && _ref !== undefined + ? _ref + : true, + ...options, + destroy(err, callback) { + destroyer(src, err) + callback(err) + } + }).wrap(src); +}; + +// ====================================================================================== +// + +Readable.from = function (iterable, opts) { + return from(Readable, iterable, opts) +}; + +export function from(Readable, iterable, opts) { + let iterator; + if (typeof iterable === 'string' || iterable instanceof Buffer) { + return new Readable({ + objectMode: true, + ...opts, + read() { + this.push(iterable) + this.push(null) + } + }) + } + let isAsync; + if (iterable && iterable[Symbol.asyncIterator]) { + isAsync = true + iterator = iterable[Symbol.asyncIterator]() + } else if (iterable && iterable[Symbol.iterator]) { + isAsync = false + iterator = iterable[Symbol.iterator]() + } else { + throw new ERR_INVALID_ARG_TYPE('iterable', ['Iterable'], iterable) + } + const readable = new Readable({ + objectMode: true, + highWaterMark: 1, + // TODO(ronag): What options should be allowed? + ...opts + }) + + // Flag to protect against _read + // being called before last iteration completion. + let reading = false + readable._read = function () { + if (!reading) { + reading = true + next() + } + } + readable._destroy = function (error, cb) { + close(error).then( + () => process.nextTick(cb, error), + (err) => process.nextTick(cb, err || error)); + } + async function close(error) { + const hadError = error !== undefined && error !== null + const hasThrow = typeof iterator.throw === 'function' + if (hadError && hasThrow) { + const { value, done } = await iterator.throw(error) + await value + if (done) { + return + } + } + if (typeof iterator.return === 'function') { + const { value } = await iterator.return() + await value + } + } + async function next() { + for (;;) { + try { + const { value, done } = isAsync ? await iterator.next() : iterator.next() + if (done) { + readable.push(null) + } else { + const res = value && typeof value.then === 'function' ? await value : value + if (res === null) { + reading = false + throw new ERR_STREAM_NULL_VALUES() + } else if (readable.push(res)) { + continue + } else { + reading = false + } + } + } catch (err) { + readable.destroy(err) + } + break + } + } + return readable +} + +// ====================================================================================== +// Operators + +const kWeakHandler = Symbol('kWeak') +const kEmpty = Symbol('kEmpty') +const kEof = Symbol('kEof') + +function map(fn, options) { + if (typeof fn !== 'function') { + throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'AsyncFunction'], fn) + } + if (options != null) { + validateObject(options, 'options', options); + } + if (options?.signal != null) { + validateAbortSignal(options.signal, 'options.signal'); + } + let concurrency = 1; + if (options?.concurrency != null) { + concurrency = Math.floor(options.concurrency); + } + validateInteger(concurrency, 'concurrency', 1); + return async function* map() { + let _options$signal, _options$signal2; + const ac = new AbortController(); + const stream = this; + const queue = []; + const signal = ac.signal; + const signalOpt = { + signal + }; + const abort = () => ac.abort(); + if ( + options !== null && + options !== undefined && + (_options$signal = options.signal) !== null && + _options$signal !== undefined && + _options$signal.aborted + ) { + abort(); + } + options === null || options === undefined + ? undefined + : (_options$signal2 = options.signal) === null || _options$signal2 === undefined + ? undefined + : _options$signal2.addEventListener('abort', abort); + let next; + let resume; + let done = false; + function onDone() { + done = true; + } + async function pump() { + try { + for await (let val of stream) { + let _val; + if (done) { + return; + } + if (signal.aborted) { + throw new AbortError(); + } + try { + val = fn(val, signalOpt); + } catch (err) { + val = Promise.reject(err); + } + if (val === kEmpty) { + continue; + } + if (typeof ((_val = val) === null || _val === undefined ? undefined : _val.catch) === 'function') { + val.catch(onDone); + } + queue.push(val); + if (next) { + next(); + next = null; + } + if (!done && queue.length && queue.length >= concurrency) { + await new Promise((resolve) => { + resume = resolve; + }); + } + } + queue.push(kEof); + } catch (err) { + const val = Promise.reject(err); + val.then(undefined, onDone);; + queue.push(val); + } finally { + let _options$signal3; + done = true; + if (next) { + next(); + next = null; + } + options === null || options === undefined + ? undefined + : (_options$signal3 = options.signal) === null || _options$signal3 === undefined + ? undefined + : _options$signal3.removeEventListener('abort', abort); + } + } + pump(); + try { + while (true) { + while (queue.length > 0) { + const val = await queue[0]; + if (val === kEof) { + return; + } + if (signal.aborted) { + throw new AbortError(); + } + if (val !== kEmpty) { + yield val; + } + queue.shift() + if (resume) { + resume(); + resume = null; + } + } + await new Promise((resolve) => { + next = resolve + }); + } + } finally { + ac.abort(); + done = true; + if (resume) { + resume(); + resume = null; + } + } + }.call(this); +} + +function asIndexedPairs(options) { + if (options != null) { + validateObject(options, 'options', options); + } + if ((options === null || options === undefined ? undefined : options.signal) != null) { + validateAbortSignal(options.signal, 'options.signal'); + } + return async function* asIndexedPairs() { + let index = 0; + for await (const val of this) { + let _options$signal4; + if ( + options !== null && + options !== undefined && + (_options$signal4 = options.signal) !== null && + _options$signal4 !== undefined && + _options$signal4.aborted + ) { + throw new AbortError('Aborted', { + cause: options.signal?.reason + }); + } + yield [index++, val]; + } + }.call(this); +} + +async function some(fn, options) { + if (typeof fn !== 'function') { + throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'AsyncFunction'], fn); + } + for await (const _ of filter.call(this, fn, options)) { + return true; + } + return false; +} + +async function every(fn, options) { + if (typeof fn !== 'function') { + throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'AsyncFunction'], fn); + } + // https://en.wikipedia.org/wiki/De_Morgan%27s_laws + return !(await some.call( + this, + async (...args) => { + return !(await fn(...args)) + }, + options + )); +} + +async function find(fn, options) { + for await (const result of filter.call(this, fn, options)) { + return result; + } + return undefined; +} + +async function forEach(fn, options) { + if (typeof fn !== 'function') { + throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'AsyncFunction'], fn) + } + async function forEachFn(value, options) { + await fn(value, options); + return kEmpty + } + // eslint-disable-next-line no-unused-vars + for await (const _ of map.call(this, forEachFn, options)); +} + +function filter(fn, options) { + if (typeof fn !== 'function') { + throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'AsyncFunction'], fn) + } + async function filterFn(value, options) { + if (await fn(value, options)) { + return value + } + return kEmpty + } + return map.call(this, filterFn, options) +} + +// Specific to provide better error to reduce since the argument is only +// missing if the stream has no items in it - but the code is still appropriate +class ReduceAwareErrMissingArgs extends ERR_MISSING_ARGS { + constructor() { + super('reduce') + this.message = 'Reduce of an empty stream requires an initial value' + } +} + +async function reduce(reducer, initialValue, options) { + let _options$signal5; + if (typeof reducer !== 'function') { + throw new ERR_INVALID_ARG_TYPE('reducer', ['Function', 'AsyncFunction'], reducer); + } + if (options != null) { + validateObject(options, 'options', options); + } + if (options?.signal != null) { + validateAbortSignal(options?.signal, 'options.signal'); + } + let hasInitialValue = arguments.length > 1; + if ( + options !== null && + options !== undefined && + (_options$signal5 = options.signal) !== null && + _options$signal5 !== undefined && + _options$signal5.aborted + ) { + const err = new AbortError(undefined, { + cause: options.signal?.reason + }) + this.once('error', () => {}); // The error is already propagated + await finished(this.destroy(err)); + throw err; + } + const ac = new AbortController(); + const signal = ac.signal; + if (options?.signal) { + const opts = { + once: true, + [kWeakHandler]: this + }; + options.signal.addEventListener('abort', () => ac.abort(), opts); + } + let gotAnyItemFromStream = false; + try { + for await (const value of this) { + let _options$signal6; + gotAnyItemFromStream = true; + if ( + options !== null && + options !== undefined && + (_options$signal6 = options.signal) !== null && + _options$signal6 !== undefined && + _options$signal6.aborted + ) { + throw new AbortError(); + } + if (!hasInitialValue) { + initialValue = value; + hasInitialValue = true; + } else { + initialValue = await reducer(initialValue, value, { + signal + }); + } + } + if (!gotAnyItemFromStream && !hasInitialValue) { + throw new ReduceAwareErrMissingArgs(); + } + } finally { + ac.abort(); + } + return initialValue; +} + +async function toArray(options) { + if (options != null) { + validateObject(options, 'options', options); + } + if (options?.signal != null) { + validateAbortSignal(options?.signal, 'options.signal') + } + const result = []; + for await (const val of this) { + let _options$signal7; + if ( + options !== null && + options !== undefined && + (_options$signal7 = options.signal) !== null && + _options$signal7 !== undefined && + _options$signal7.aborted + ) { + throw new AbortError(undefined, { + cause: options.signal?.reason + }); + } + result.push(val); + } + return result; +} + +function flatMap(fn, options) { + const values = map.call(this, fn, options) + return async function* flatMap() { + for await (const val of values) { + yield* val + } + }.call(this) +} + +function toIntegerOrInfinity(number) { + // We coerce here to align with the spec + // https://github.com/tc39/proposal-iterator-helpers/issues/169 + number = Number(number) + if (Number.isNaN(number)) { + return 0 + } + if (number < 0) { + throw new ERR_OUT_OF_RANGE('number', '>= 0', number) + } + return number +} + +function drop(number, options) { + if (options != null) { + validateObject(options, 'options', options); + } + if (options?.signal != null) { + validateAbortSignal(options?.signal, 'options.signal'); + } + number = toIntegerOrInfinity(number); + return async function* drop() { + let _options$signal8; + if ( + options !== null && + options !== undefined && + (_options$signal8 = options.signal) !== null && + _options$signal8 !== undefined && + _options$signal8.aborted + ) { + throw new AbortError(); + } + for await (const val of this) { + let _options$signal9; + if ( + options !== null && + options !== undefined && + (_options$signal9 = options.signal) !== null && + _options$signal9 !== undefined && + _options$signal9.aborted + ) { + throw new AbortError(); + } + if (number-- <= 0) { + yield val; + } + } + }.call(this); +} + +function take(number, options) { + if (options != null) { + validateObject(options, 'options', options); + } + if (options?.signal != null) { + validateAbortSignal(options?.signal, 'options.signal'); + } + number = toIntegerOrInfinity(number); + return async function* take() { + let _options$signal10; + if ( + options !== null && + options !== undefined && + (_options$signal10 = options.signal) !== null && + _options$signal10 !== undefined && + _options$signal10.aborted + ) { + throw new AbortError(); + } + for await (const val of this) { + let _options$signal11; + if ( + options !== null && + options !== undefined && + (_options$signal11 = options.signal) !== null && + _options$signal11 !== undefined && + _options$signal11.aborted + ) { + throw new AbortError(); + } + if (number-- > 0) { + yield val; + } else { + return; + } + } + }.call(this); +} + +Readable.prototype.map = function(fn, options) { + return from(Readable, map.call(this, fn, options)); +} + +Readable.prototype.asIndexedPairs = function(options) { + return from(Readable, asIndexedPairs.call(this, options)); +} + +Readable.prototype.drop = function(number, options) { + return from(Readable, drop.call(this, number, options)); +} + +Readable.prototype.filter = function(fn, options) { + return from(Readable, filter.call(this, fn, options)); +} + +Readable.prototype.flatMap = function(fn, options) { + return from(Readable, flatMap.call(this, fn, options)); +}; + +Readable.prototype.take = function(number, options) { + return from(Readable, take.call(this, number, options)); +} + +Readable.prototype.every = every; +Readable.prototype.forEach = forEach; +Readable.prototype.reduce = reduce; +Readable.prototype.toArray = toArray; +Readable.prototype.some = some; +Readable.prototype.find = find; + diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_transform.d.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_transform.d.ts new file mode 100644 index 000000000..127ce7887 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_transform.d.ts @@ -0,0 +1,344 @@ +import EventEmitter from './events'; + +interface WritableOptions { + highWaterMark?: number | undefined; + decodeStrings?: boolean | undefined; + defaultEncoding?: BufferEncoding | undefined; + objectMode?: boolean | undefined; + emitClose?: boolean | undefined; + write?(this: Writable, chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void; + writev?(this: Writable, chunks: Array<{ chunk: any, encoding: BufferEncoding }>, callback: (error?: Error | null) => void): void; + destroy?(this: Writable, error: Error | null, callback: (error: Error | null) => void): void; + final?(this: Writable, callback: (error?: Error | null) => void): void; + autoDestroy?: boolean | undefined; +} + +export class internal extends EventEmitter { + pipe(destination: T, options?: { end?: boolean | undefined; }): T; +} + +export class Stream extends internal { + constructor(opts?: ReadableOptions); +} + +interface ReadableOptions { + highWaterMark?: number | undefined; + encoding?: BufferEncoding | undefined; + objectMode?: boolean | undefined; + read?(this: Readable, size: number): void; + destroy?(this: Readable, error: Error | null, callback: (error: Error | null) => void): void; + autoDestroy?: boolean | undefined; +} + +export class Readable extends Stream implements NodeJS.ReadableStream { + static from(iterable: Iterable | AsyncIterable, options?: ReadableOptions): Readable; + + readable: boolean; + readonly readableEncoding: BufferEncoding | null; + readonly readableEnded: boolean; + readonly readableFlowing: boolean | null; + readonly readableHighWaterMark: number; + readonly readableLength: number; + readonly readableObjectMode: boolean; + destroyed: boolean; + constructor(opts?: ReadableOptions); + _read(size: number): void; + read(size?: number): any; + setEncoding(encoding: BufferEncoding): this; + pause(): this; + resume(): this; + isPaused(): boolean; + unpipe(destination?: NodeJS.WritableStream): this; + unshift(chunk: any, encoding?: BufferEncoding): void; + wrap(oldStream: NodeJS.ReadableStream): this; + push(chunk: any, encoding?: BufferEncoding): boolean; + _destroy(error: Error | null, callback: (error?: Error | null) => void): void; + destroy(error?: Error): this; + + addListener(event: "close", listener: () => void): this; + addListener(event: "data", listener: (chunk: any) => void): this; + addListener(event: "end", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "pause", listener: () => void): this; + addListener(event: "readable", listener: () => void): this; + addListener(event: "resume", listener: () => void): this; + addListener(event: string | symbol, listener: (...args: any[]) => void): this; + + emit(event: "close"): boolean; + emit(event: "data", chunk: any): boolean; + emit(event: "end"): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "pause"): boolean; + emit(event: "readable"): boolean; + emit(event: "resume"): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + + on(event: "close", listener: () => void): this; + on(event: "data", listener: (chunk: any) => void): this; + on(event: "end", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "pause", listener: () => void): this; + on(event: "readable", listener: () => void): this; + on(event: "resume", listener: () => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + + once(event: "close", listener: () => void): this; + once(event: "data", listener: (chunk: any) => void): this; + once(event: "end", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "pause", listener: () => void): this; + once(event: "readable", listener: () => void): this; + once(event: "resume", listener: () => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + + prependListener(event: "close", listener: () => void): this; + prependListener(event: "data", listener: (chunk: any) => void): this; + prependListener(event: "end", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "pause", listener: () => void): this; + prependListener(event: "readable", listener: () => void): this; + prependListener(event: "resume", listener: () => void): this; + prependListener(event: string | symbol, listener: (...args: any[]) => void): this; + + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "data", listener: (chunk: any) => void): this; + prependOnceListener(event: "end", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "pause", listener: () => void): this; + prependOnceListener(event: "readable", listener: () => void): this; + prependOnceListener(event: "resume", listener: () => void): this; + prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; + + removeListener(event: "close", listener: () => void): this; + removeListener(event: "data", listener: (chunk: any) => void): this; + removeListener(event: "end", listener: () => void): this; + removeListener(event: "error", listener: (err: Error) => void): this; + removeListener(event: "pause", listener: () => void): this; + removeListener(event: "readable", listener: () => void): this; + removeListener(event: "resume", listener: () => void): this; + removeListener(event: string | symbol, listener: (...args: any[]) => void): this; + + [Symbol.asyncIterator](): AsyncIterableIterator; +} + +export class Writable extends Stream implements NodeJS.WritableStream { + readonly writable: boolean; + readonly writableEnded: boolean; + readonly writableFinished: boolean; + readonly writableHighWaterMark: number; + readonly writableLength: number; + readonly writableObjectMode: boolean; + readonly writableCorked: number; + destroyed: boolean; + constructor(opts?: WritableOptions); + _write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void; + _writev?(chunks: Array<{ chunk: any, encoding: BufferEncoding }>, callback: (error?: Error | null) => void): void; + _destroy(error: Error | null, callback: (error?: Error | null) => void): void; + _final(callback: (error?: Error | null) => void): void; + write(chunk: any, cb?: (error: Error | null | undefined) => void): boolean; + write(chunk: any, encoding: BufferEncoding, cb?: (error: Error | null | undefined) => void): boolean; + setDefaultEncoding(encoding: BufferEncoding): this; + end(cb?: () => void): this; + end(chunk: any, cb?: () => void): this; + end(chunk: any, encoding: BufferEncoding, cb?: () => void): this; + cork(): void; + uncork(): void; + destroy(error?: Error): this; + + addListener(event: "close", listener: () => void): this; + addListener(event: "drain", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "finish", listener: () => void): this; + addListener(event: "pipe", listener: (src: Readable) => void): this; + addListener(event: "unpipe", listener: (src: Readable) => void): this; + addListener(event: string | symbol, listener: (...args: any[]) => void): this; + + emit(event: "close"): boolean; + emit(event: "drain"): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "finish"): boolean; + emit(event: "pipe", src: Readable): boolean; + emit(event: "unpipe", src: Readable): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + + on(event: "close", listener: () => void): this; + on(event: "drain", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "finish", listener: () => void): this; + on(event: "pipe", listener: (src: Readable) => void): this; + on(event: "unpipe", listener: (src: Readable) => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + + once(event: "close", listener: () => void): this; + once(event: "drain", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "finish", listener: () => void): this; + once(event: "pipe", listener: (src: Readable) => void): this; + once(event: "unpipe", listener: (src: Readable) => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + + prependListener(event: "close", listener: () => void): this; + prependListener(event: "drain", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "finish", listener: () => void): this; + prependListener(event: "pipe", listener: (src: Readable) => void): this; + prependListener(event: "unpipe", listener: (src: Readable) => void): this; + prependListener(event: string | symbol, listener: (...args: any[]) => void): this; + + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "drain", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "finish", listener: () => void): this; + prependOnceListener(event: "pipe", listener: (src: Readable) => void): this; + prependOnceListener(event: "unpipe", listener: (src: Readable) => void): this; + prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; + + removeListener(event: "close", listener: () => void): this; + removeListener(event: "drain", listener: () => void): this; + removeListener(event: "error", listener: (err: Error) => void): this; + removeListener(event: "finish", listener: () => void): this; + removeListener(event: "pipe", listener: (src: Readable) => void): this; + removeListener(event: "unpipe", listener: (src: Readable) => void): this; + removeListener(event: string | symbol, listener: (...args: any[]) => void): this; +} + + +export class Duplex extends Readable implements Writable { + readonly writable: boolean; + readonly writableEnded: boolean; + readonly writableFinished: boolean; + readonly writableHighWaterMark: number; + readonly writableLength: number; + readonly writableObjectMode: boolean; + readonly writableCorked: number; + allowHalfOpen: boolean; + constructor(opts?: DuplexOptions); + _write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void; + _writev?(chunks: Array<{ chunk: any, encoding: BufferEncoding }>, callback: (error?: Error | null) => void): void; + _destroy(error: Error | null, callback: (error: Error | null) => void): void; + _final(callback: (error?: Error | null) => void): void; + write(chunk: any, encoding?: BufferEncoding, cb?: (error: Error | null | undefined) => void): boolean; + write(chunk: any, cb?: (error: Error | null | undefined) => void): boolean; + setDefaultEncoding(encoding: BufferEncoding): this; + end(cb?: () => void): this; + end(chunk: any, cb?: () => void): this; + end(chunk: any, encoding?: BufferEncoding, cb?: () => void): this; + cork(): void; + uncork(): void; + addListener(event: 'close', listener: () => void): this; + addListener(event: 'data', listener: (chunk: any) => void): this; + addListener(event: 'drain', listener: () => void): this; + addListener(event: 'end', listener: () => void): this; + addListener(event: 'error', listener: (err: Error) => void): this; + addListener(event: 'finish', listener: () => void): this; + addListener(event: 'pause', listener: () => void): this; + addListener(event: 'pipe', listener: (src: Readable) => void): this; + addListener(event: 'readable', listener: () => void): this; + addListener(event: 'resume', listener: () => void): this; + addListener(event: 'unpipe', listener: (src: Readable) => void): this; + addListener(event: string | symbol, listener: (...args: any[]) => void): this; + emit(event: 'close'): boolean; + emit(event: 'data', chunk: any): boolean; + emit(event: 'drain'): boolean; + emit(event: 'end'): boolean; + emit(event: 'error', err: Error): boolean; + emit(event: 'finish'): boolean; + emit(event: 'pause'): boolean; + emit(event: 'pipe', src: Readable): boolean; + emit(event: 'readable'): boolean; + emit(event: 'resume'): boolean; + emit(event: 'unpipe', src: Readable): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + on(event: 'close', listener: () => void): this; + on(event: 'data', listener: (chunk: any) => void): this; + on(event: 'drain', listener: () => void): this; + on(event: 'end', listener: () => void): this; + on(event: 'error', listener: (err: Error) => void): this; + on(event: 'finish', listener: () => void): this; + on(event: 'pause', listener: () => void): this; + on(event: 'pipe', listener: (src: Readable) => void): this; + on(event: 'readable', listener: () => void): this; + on(event: 'resume', listener: () => void): this; + on(event: 'unpipe', listener: (src: Readable) => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + once(event: 'close', listener: () => void): this; + once(event: 'data', listener: (chunk: any) => void): this; + once(event: 'drain', listener: () => void): this; + once(event: 'end', listener: () => void): this; + once(event: 'error', listener: (err: Error) => void): this; + once(event: 'finish', listener: () => void): this; + once(event: 'pause', listener: () => void): this; + once(event: 'pipe', listener: (src: Readable) => void): this; + once(event: 'readable', listener: () => void): this; + once(event: 'resume', listener: () => void): this; + once(event: 'unpipe', listener: (src: Readable) => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + prependListener(event: 'close', listener: () => void): this; + prependListener(event: 'data', listener: (chunk: any) => void): this; + prependListener(event: 'drain', listener: () => void): this; + prependListener(event: 'end', listener: () => void): this; + prependListener(event: 'error', listener: (err: Error) => void): this; + prependListener(event: 'finish', listener: () => void): this; + prependListener(event: 'pause', listener: () => void): this; + prependListener(event: 'pipe', listener: (src: Readable) => void): this; + prependListener(event: 'readable', listener: () => void): this; + prependListener(event: 'resume', listener: () => void): this; + prependListener(event: 'unpipe', listener: (src: Readable) => void): this; + prependListener(event: string | symbol, listener: (...args: any[]) => void): this; + prependOnceListener(event: 'close', listener: () => void): this; + prependOnceListener(event: 'data', listener: (chunk: any) => void): this; + prependOnceListener(event: 'drain', listener: () => void): this; + prependOnceListener(event: 'end', listener: () => void): this; + prependOnceListener(event: 'error', listener: (err: Error) => void): this; + prependOnceListener(event: 'finish', listener: () => void): this; + prependOnceListener(event: 'pause', listener: () => void): this; + prependOnceListener(event: 'pipe', listener: (src: Readable) => void): this; + prependOnceListener(event: 'readable', listener: () => void): this; + prependOnceListener(event: 'resume', listener: () => void): this; + prependOnceListener(event: 'unpipe', listener: (src: Readable) => void): this; + prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; + removeListener(event: 'close', listener: () => void): this; + removeListener(event: 'data', listener: (chunk: any) => void): this; + removeListener(event: 'drain', listener: () => void): this; + removeListener(event: 'end', listener: () => void): this; + removeListener(event: 'error', listener: (err: Error) => void): this; + removeListener(event: 'finish', listener: () => void): this; + removeListener(event: 'pause', listener: () => void): this; + removeListener(event: 'pipe', listener: (src: Readable) => void): this; + removeListener(event: 'readable', listener: () => void): this; + removeListener(event: 'resume', listener: () => void): this; + removeListener(event: 'unpipe', listener: (src: Readable) => void): this; + removeListener(event: string | symbol, listener: (...args: any[]) => void): this; +} + +interface DuplexOptions extends ReadableOptions, WritableOptions { + allowHalfOpen?: boolean | undefined; + readableObjectMode?: boolean | undefined; + writableObjectMode?: boolean | undefined; + readableHighWaterMark?: number | undefined; + writableHighWaterMark?: number | undefined; + writableCorked?: number | undefined; + read?(this: Duplex, size: number): void; + write?(this: Duplex, chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void; + writev?(this: Duplex, chunks: Array<{ chunk: any, encoding: BufferEncoding }>, callback: (error?: Error | null) => void): void; + final?(this: Duplex, callback: (error?: Error | null) => void): void; + destroy?(this: Duplex, error: Error | null, callback: (error: Error | null) => void): void; +} + +interface TransformOptions extends DuplexOptions { + read?(this: Transform, size: number): void; + write?(this: Transform, chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void; + writev?(this: Transform, chunks: Array<{ chunk: any, encoding: BufferEncoding }>, callback: (error?: Error | null) => void): void; + final?(this: Transform, callback: (error?: Error | null) => void): void; + destroy?(this: Transform, error: Error | null, callback: (error: Error | null) => void): void; + transform?(this: Transform, chunk: any, encoding: BufferEncoding, callback: TransformCallback): void; + flush?(this: Transform, callback: TransformCallback): void; +} + +type TransformCallback = (error?: Error | null, data?: any) => void; + +export class Transform extends Duplex { + constructor(opts?: TransformOptions); + _transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback): void; + _flush(callback: TransformCallback): void; +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_transform.js b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_transform.js new file mode 100644 index 000000000..3abb6ddca --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_transform.js @@ -0,0 +1,147 @@ +'use strict' + +import { + ERR_METHOD_NOT_IMPLEMENTED +} from './internal_errors'; + +import { + Duplex, +} from './streams_duplex'; + +import { + getHighWaterMark, +} from './streams_util'; + +Object.setPrototypeOf(Transform.prototype, Duplex.prototype); +Object.setPrototypeOf(Transform, Duplex); + +const kCallback = Symbol('kCallback'); + +export function Transform(options) { + if (!(this instanceof Transform)) return new Transform(options); + + // TODO (ronag): This should preferably always be + // applied but would be semver-major. Or even better; + // make Transform a Readable with the Writable interface. + const readableHighWaterMark = options ? getHighWaterMark(options, 'readableHighWaterMark', true) : null; + if (readableHighWaterMark === 0) { + // A Duplex will buffer both on the writable and readable side while + // a Transform just wants to buffer hwm number of elements. To avoid + // buffering twice we disable buffering on the writable side. + options = { + ...options, + highWaterMark: null, + readableHighWaterMark, + // TODO (ronag): 0 is not optimal since we have + // a "bug" where we check needDrain before calling _write and not after. + // Refs: https://github.com/nodejs/node/pull/32887 + // Refs: https://github.com/nodejs/node/pull/35941 + writableHighWaterMark: options?.writableHighWaterMark || 0 + }; + } + Duplex.call(this, options); + + // We have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + this._readableState.sync = false; + this[kCallback] = null; + if (options) { + if (typeof options.transform === 'function') this._transform = options.transform; + if (typeof options.flush === 'function') this._flush = options.flush; + } + + // When the writable side finishes, then flush out anything remaining. + // Backwards compat. Some Transform streams incorrectly implement _final + // instead of or in addition to _flush. By using 'prefinish' instead of + // implementing _final we continue supporting this unfortunate use case. + this.on('prefinish', prefinish); +} + +function final(cb) { + if (typeof this._flush === 'function' && !this.destroyed) { + this._flush((er, data) => { + if (er) { + if (cb) { + cb(er); + } else { + this.destroy(er); + } + return; + } + if (data != null) { + this.push(data); + } + this.push(null); + if (cb) { + cb(); + } + }) + } else { + this.push(null); + if (cb) { + cb(); + } + } +} + +function prefinish() { + if (this._final !== final) { + final.call(this); + } +} +Transform.prototype._final = final; + +Transform.prototype._transform = function () { + throw new ERR_METHOD_NOT_IMPLEMENTED('_transform()') +} + +Transform.prototype._write = function (chunk, encoding, callback) { + const rState = this._readableState; + const wState = this._writableState; + const length = rState.length; + this._transform(chunk, encoding, (err, val) => { + if (err) { + callback(err); + return; + } + if (val != null) { + this.push(val); + } + if ( + wState.ended || + // Backwards compat. + length === rState.length || + // Backwards compat. + rState.length < rState.highWaterMark + ) { + callback(); + } else { + this[kCallback] = callback; + } + }) +} + +Transform.prototype._read = function (_size) { + if (this[kCallback]) { + const callback = this[kCallback]; + this[kCallback] = null; + callback(); + } +} + +Object.setPrototypeOf(PassThrough.prototype, Transform.prototype); +Object.setPrototypeOf(PassThrough, Transform); + +export function PassThrough(options) { + if (!(this instanceof PassThrough)) return new PassThrough(options); + Transform.call(this, { + ...options, + transform: undefined, + flush: undefined, + }); +} + +PassThrough.prototype._transform = function (chunk, _, cb) { + cb(null, chunk); +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_util.js b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_util.js new file mode 100644 index 000000000..e9c50973b --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_util.js @@ -0,0 +1,997 @@ +export const kDestroyed = Symbol('kDestroyed'); +export const kIsErrored = Symbol('kIsErrored'); +export const kIsReadable = Symbol('kIsReadable'); +export const kIsDisturbed = Symbol('kIsDisturbed'); +export const kPaused = Symbol('kPaused'); +export const kOnFinished = Symbol('kOnFinished'); +export const kDestroy = Symbol('kDestroy'); +export const kConstruct = Symbol('kConstruct'); + +import { + AbortError, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_STREAM_PREMATURE_CLOSE, + ERR_MULTIPLE_CALLBACK, + aggregateTwoErrors, +} from './internal_errors'; + +import * as process from './process'; + +import { Buffer } from './internal_buffer'; + +import { + validateAbortSignal, + validateFunction, + validateObject, +} from './validators'; + +export function isReadableNodeStream(obj, strict = false) { + let _obj$_readableState; + return !!( + ( + obj && + typeof obj.pipe === 'function' && + typeof obj.on === 'function' && + (!strict || (typeof obj.pause === 'function' && + typeof obj.resume === 'function')) && + (!obj._writableState || + ((_obj$_readableState = obj._readableState) === null || + _obj$_readableState === undefined ? undefined + : _obj$_readableState.readable) !== false) && + // Duplex + (!obj._writableState || obj._readableState) + ) // Writable has .pipe. + ); +} + +export function isWritableNodeStream(obj) { + let _obj$_writableState; + return !!( + ( + obj && + typeof obj.write === 'function' && + typeof obj.on === 'function' && + (!obj._readableState || + ((_obj$_writableState = obj._writableState) === null || + _obj$_writableState === undefined ? undefined + : _obj$_writableState.writable) !== false) + ) // Duplex + ); +} + +export function isDuplexNodeStream(obj) { + return !!( + obj && + typeof obj.pipe === 'function' && + obj._readableState && + typeof obj.on === 'function' && + typeof obj.write === 'function' + ); +} + +export function isNodeStream(obj) { + return ( + obj && + (obj._readableState || + obj._writableState || + (typeof obj.write === 'function' && + typeof obj.on === 'function') || + (typeof obj.pipe === 'function' && + typeof obj.on === 'function')) + ); +} + +export function isIterable(obj, isAsync = false) { + if (obj == null) return false; + if (isAsync === true) return typeof obj[Symbol.asyncIterator] === 'function'; + if (isAsync === false) return typeof obj[Symbol.iterator] === 'function'; + return typeof obj[Symbol.asyncIterator] === 'function' || + typeof obj[Symbol.iterator] === 'function'; +} + +export function isDestroyed(stream) { + if (!isNodeStream(stream)) return null; + const wState = stream._writableState; + const rState = stream._readableState; + const state = wState || rState; + return !!(stream.destroyed || stream[kDestroyed] || (state !== null && state !== undefined && state.destroyed)); +} + +export function isWritableEnded(stream) { + if (!isWritableNodeStream(stream)) return null; + if (stream.writableEnded === true) return true; + const wState = stream._writableState; + if (wState !== null && wState !== undefined && wState.errored) return false; + if (typeof (wState === null || wState === undefined ? undefined : wState.ended) !== 'boolean') return null; + return wState.ended; +} + +export function isWritableFinished(stream, strict = false) { + if (!isWritableNodeStream(stream)) return null; + if (stream.writableFinished === true) return true; + const wState = stream._writableState; + if (wState !== null && wState !== undefined && wState.errored) return false; + if (typeof (wState === null || wState === undefined ? undefined : wState.finished) !== 'boolean') return null; + return !!(wState.finished || (strict === false && wState.ended === true && wState.length === 0)); +} + +export function isReadableEnded(stream) { + if (!isReadableNodeStream(stream)) return null; + if (stream.readableEnded === true) return true; + const rState = stream._readableState; + if (!rState || rState.errored) return false; + if (typeof (rState === null || rState === undefined ? undefined : rState.ended) !== 'boolean') return null; + return rState.ended; +} + +export function isReadableFinished(stream, strict = false) { + if (!isReadableNodeStream(stream)) return null; + const rState = stream._readableState; + if (rState !== null && rState !== undefined && rState.errored) return false; + if (typeof (rState === null || rState === undefined ? undefined : rState.endEmitted) !== 'boolean') return null; + return !!(rState.endEmitted || (strict === false && rState.ended === true && rState.length === 0)); +} + +export function isReadable(stream) { + if (stream && stream[kIsReadable] != null) return stream[kIsReadable]; + if (typeof (stream === null || stream === undefined ? undefined : stream.readable) !== 'boolean') return null; + if (isDestroyed(stream)) return false; + return isReadableNodeStream(stream) && stream.readable && !isReadableFinished(stream); +} + +export function isWritable(stream) { + if (typeof (stream === null || stream === undefined ? undefined : stream.writable) !== 'boolean') return null; + if (isDestroyed(stream)) return false; + return isWritableNodeStream(stream) && stream.writable && !isWritableEnded(stream); +} + +export function isFinished(stream, opts = {}) { + if (!isNodeStream(stream)) { + return null; + } + if (isDestroyed(stream)) { + return true; + } + if ((opts === null || opts === undefined ? undefined : opts.readable) !== false && + isReadable(stream)) { + return false; + } + if ((opts === null || opts === undefined ? undefined : opts.writable) !== false && + isWritable(stream)) { + return false; + } + return true; +} + +export function isWritableErrored(stream) { + let _stream$_writableStat, _stream$_writableStat2; + if (!isNodeStream(stream)) { + return null; + } + if (stream.writableErrored) { + return stream.writableErrored; + } + return (_stream$_writableStat = + (_stream$_writableStat2 = stream._writableState) === null || _stream$_writableStat2 === undefined + ? undefined + : _stream$_writableStat2.errored) !== null && _stream$_writableStat !== undefined + ? _stream$_writableStat + : null; +} + +export function isReadableErrored(stream) { + let _stream$_readableStat, _stream$_readableStat2; + if (!isNodeStream(stream)) { + return null; + } + if (stream.readableErrored) { + return stream.readableErrored; + } + return (_stream$_readableStat = + (_stream$_readableStat2 = stream._readableState) === null || _stream$_readableStat2 === undefined + ? undefined + : _stream$_readableStat2.errored) !== null && _stream$_readableStat !== undefined + ? _stream$_readableStat + : null; +} + +export function isClosed(stream) { + if (!isNodeStream(stream)) { + return null + } + if (typeof stream.closed === 'boolean') { + return stream.closed + } + const wState = stream._writableState + const rState = stream._readableState + if ( + typeof (wState === null || wState === undefined ? undefined : wState.closed) === 'boolean' || + typeof (rState === null || rState === undefined ? undefined : rState.closed) === 'boolean' + ) { + return ( + (wState === null || wState === undefined ? undefined : wState.closed) || + (rState === null || rState === undefined ? undefined : rState.closed) + ) + } + if (typeof stream._closed === 'boolean' && isOutgoingMessage(stream)) { + return stream._closed + } + return null +} + +// TODO(later): We do not actually support OutgoingMessage yet. Might not ever? +// Keeping this here tho just to keep things simple. +export function isOutgoingMessage(stream) { + return ( + typeof stream._closed === 'boolean' && + typeof stream._defaultKeepAlive === 'boolean' && + typeof stream._removedConnection === 'boolean' && + typeof stream._removedContLen === 'boolean' + ); +} + +// TODO(later): We do not actually support Server Response yet. Might not ever? +// Keeping this here tho just to keep things simple. +export function isServerResponse(stream) { + return typeof stream._sent100 === 'boolean' && isOutgoingMessage(stream); +} + +// TODO(later): We do not actually support Server Request yet. Might not ever? +// Keeping this here tho just to keep things simple. +export function isServerRequest(stream) { + let _stream$req; + return ( + typeof stream._consuming === 'boolean' && + typeof stream._dumped === 'boolean' && + ((_stream$req = stream.req) === null || _stream$req === undefined ? undefined : _stream$req.upgradeOrConnect) === + undefined + ); +} + +export function willEmitClose(stream) { + if (!isNodeStream(stream)) return null; + const wState = stream._writableState; + const rState = stream._readableState; + const state = wState || rState; + return ( + (!state && isServerResponse(stream)) || !!(state && state.autoDestroy && state.emitClose && state.closed === false) + ); +} + +export function isDisturbed(stream) { + let _stream$kIsDisturbed; + return !!( + stream && + ((_stream$kIsDisturbed = stream[kIsDisturbed]) !== null && _stream$kIsDisturbed !== undefined + ? _stream$kIsDisturbed + : stream.readableDidRead || stream.readableAborted) + ); +} + +export function isErrored(stream) { + var _ref, + _ref2, + _ref3, + _ref4, + _ref5, + _stream$kIsErrored, + _stream$_readableStat3, + _stream$_writableStat3, + _stream$_readableStat4, + _stream$_writableStat4 + return !!( + stream && + ((_ref = + (_ref2 = + (_ref3 = + (_ref4 = + (_ref5 = + (_stream$kIsErrored = stream[kIsErrored]) !== null && _stream$kIsErrored !== undefined + ? _stream$kIsErrored + : stream.readableErrored) !== null && _ref5 !== undefined + ? _ref5 + : stream.writableErrored) !== null && _ref4 !== undefined + ? _ref4 + : (_stream$_readableStat3 = stream._readableState) === null || _stream$_readableStat3 === undefined + ? undefined + : _stream$_readableStat3.errorEmitted) !== null && _ref3 !== undefined + ? _ref3 + : (_stream$_writableStat3 = stream._writableState) === null || _stream$_writableStat3 === undefined + ? undefined + : _stream$_writableStat3.errorEmitted) !== null && _ref2 !== undefined + ? _ref2 + : (_stream$_readableStat4 = stream._readableState) === null || _stream$_readableStat4 === undefined + ? undefined + : _stream$_readableStat4.errored) !== null && _ref !== undefined + ? _ref + : (_stream$_writableStat4 = stream._writableState) === null || _stream$_writableStat4 === undefined + ? undefined + : _stream$_writableStat4.errored) + ) +} + +export const nop = () => {}; + +export function once(callback) { + let called = false + return function (...args) { + if (called) { + return + } + called = true + callback.apply(this, args) + } +} + +// ====================================================================================== +// highWaterMark handling + +export function highWaterMarkFrom(options, isDuplex, duplexKey) { + return options.highWaterMark != null ? options.highWaterMark : + isDuplex ? options[duplexKey] : null +} + +export function getDefaultHighWaterMark(objectMode = false) { + return objectMode ? 16 : 16 * 1024 +} + +export function getHighWaterMark(state, options, duplexKey, isDuplex) { + const hwm = highWaterMarkFrom(options, isDuplex, duplexKey) + if (hwm != null) { + if (!Number.isInteger(hwm) || hwm < 0) { + const name = isDuplex ? `options.${duplexKey}` : 'options.highWaterMark'; + throw new ERR_INVALID_ARG_VALUE(name, hwm, name); + } + return Math.floor(hwm); + } + + // Default value + return getDefaultHighWaterMark(state.objectMode) +} + +// ====================================================================================== +// addAbortSignal + +export function addAbortSignal(signal, stream) { + validateAbortSignal(signal, 'signal'); + if (!isNodeStream(stream)) { + throw new ERR_INVALID_ARG_TYPE('stream', 'stream.Stream', stream); + } + const onAbort = () => { + stream.destroy( + new AbortError(undefined, { + cause: signal.reason + }) + ); + } + if (signal.aborted) { + onAbort(); + } else { + signal.addEventListener('abort', onAbort); + eos(stream, () => signal.removeEventListener('abort', onAbort)); + } + return stream; +} + +// ====================================================================================== +// BufferList + +export class BufferList { + head = null; + tail = null; + length = 0; + + push(v) { + const entry = { + data: v, + next: null, + } + if (this.length > 0) this.tail.next = entry; + else this.head = entry; + this.tail = entry; + ++this.length; + } + unshift(v) { + const entry = { + data: v, + next: this.head, + } + if (this.length === 0) this.tail = entry; + this.head = entry; + ++this.length; + } + shift() { + if (this.length === 0) return; + const ret = this.head.data; + if (this.length === 1) this.head = this.tail = null; + else this.head = this.head.next; + --this.length; + return ret; + } + + clear() { + this.head = this.tail = null; + this.length = 0; + } + + join(s) { + if (this.length === 0) return ''; + let p = this.head; + let ret = '' + p.data; + while ((p = p.next) !== null) ret += s + p.data; + return ret; + } + + concat(n) { + if (this.length === 0) return Buffer.alloc(0); + const ret = Buffer.allocUnsafe(n >>> 0); + let p = this.head; + let i = 0; + while (p) { + ret.set(p.data, i); + i += p.data.length; + p = p.next; + } + return ret; + } + + consume(n, hasStrings = false) { + const data = this.head.data; + if (n < data.length) { + // `slice` is the same for buffers and strings. + const slice = data.slice(0, n); + this.head.data = data.slice(n); + return slice; + } + if (n === data.length) { + // First chunk is a perfect match. + return this.shift(); + } + // Result spans more than one buffer. + return hasStrings ? this._getString(n) : this._getBuffer(n); + } + + first() { + return this.head.data; + } + + *[Symbol.iterator]() { + for (let p = this.head; p; p = p.next) { + yield p.data; + } + } + + _getString(n) { + let ret = ''; + let p = this.head; + let c = 0; + do { + const str = p.data; + if (n > str.length) { + ret += str; + n -= str.length; + } else { + if (n === str.length) { + ret += str + ++c + if (p.next) this.head = p.next; + else this.head = this.tail = null; + } else { + ret += str.slice(0, n); + this.head = p; + p.data = str.slice(n); + } + break; + } + ++c; + } while ((p = p.next) !== null); + this.length -= c; + return ret; + } + + _getBuffer(n) { + const ret = Buffer.allocUnsafe(n); + const retLen = n; + let p = this.head; + let c = 0; + do { + const buf = p.data; + if (n > buf.length) { + ret.set(buf, retLen - n); + n -= buf.length; + } else { + if (n === buf.length) { + ret.set(buf, retLen - n); + ++c; + if (p.next) this.head = p.next; + else this.head = this.tail = null; + } else { + ret.set(new Uint8Array(buf.buffer, buf.byteOffset, n), retLen - n); + this.head = p; + p.data = buf.slice(n); + } + break; + } + ++c + } while ((p = p.next) !== null); + this.length -= c; + return ret; + } +} + +// ====================================================================================== + +// TODO(later): We do not current implement Node.js' Request object. Might never? +function isRequest(stream) { + return stream && stream.setHeader && typeof stream.abort === 'function' +} + +export function eos(stream, options, callback) { + let _options$readable, _options$writable; + if (arguments.length === 2) { + callback = options; + options = {}; + } else if (options == null) { + options = {}; + } else { + validateObject(options, 'options', options); + } + validateFunction(callback, 'callback'); + validateAbortSignal(options.signal, 'options.signal'); + callback = once(callback); + const readable = + (_options$readable = options.readable) !== null && _options$readable !== undefined + ? _options$readable + : isReadableNodeStream(stream); + const writable = + (_options$writable = options.writable) !== null && _options$writable !== undefined + ? _options$writable + : isWritableNodeStream(stream); + if (!isNodeStream(stream)) { + // TODO: Webstreams. + throw new ERR_INVALID_ARG_TYPE('stream', 'Stream', stream); + } + const wState = stream._writableState; + const rState = stream._readableState; + const onlegacyfinish = () => { + if (!stream.writable) { + onfinish(); + } + }; + + // TODO (ronag): Improve soft detection to include core modules and + // common ecosystem modules that do properly emit 'close' but fail + // this generic check. + let _willEmitClose = + willEmitClose(stream) && isReadableNodeStream(stream) === readable && isWritableNodeStream(stream) === writable; + let writableFinished = isWritableFinished((stream), false); + const onfinish = () => { + writableFinished = true; + // Stream should not be destroyed here. If it is that + // means that user space is doing something differently and + // we cannot trust willEmitClose. + if (stream.destroyed) { + _willEmitClose = false; + } + if (_willEmitClose && (!stream.readable || readable)) { + return; + } + if (!readable || readableFinished) { + callback.call(stream); + } + }; + let readableFinished = isReadableFinished(stream, false); + const onend = () => { + readableFinished = true; + // Stream should not be destroyed here. If it is that + // means that user space is doing something differently and + // we cannot trust willEmitClose. + if (stream.destroyed) { + _willEmitClose = false; + } + if (_willEmitClose && (!stream.writable || writable)) { + return; + } + if (!writable || writableFinished) { + callback.call(stream); + } + }; + const onerror = (err) => { + callback.call(stream, err); + }; + let closed = isClosed(stream); + const onclose = () => { + closed = true; + const errored = isWritableErrored(stream) || isReadableErrored(stream) + if (errored && typeof errored !== 'boolean') { + return callback.call(stream, errored); + } + if (readable && !readableFinished && isReadableNodeStream(stream, true)) { + if (!isReadableFinished(stream, false)) return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE()); + } + if (writable && !writableFinished) { + if (!isWritableFinished(stream, false)) return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE()); + } + callback.call(stream); + }; + const onrequest = () => { + stream.req.on('finish', onfinish); + }; + if (isRequest(stream)) { + stream.on('complete', onfinish); + if (!_willEmitClose) { + stream.on('abort', onclose); + } + if (stream.req) { + onrequest(); + } else { + stream.on('request', onrequest); + } + } else if (writable && !wState) { + // legacy streams + stream.on('end', onlegacyfinish); + stream.on('close', onlegacyfinish); + } + + // Not all streams will emit 'close' after 'aborted'. + if (!_willEmitClose && typeof stream.aborted === 'boolean') { + stream.on('aborted', onclose); + } + stream.on('end', onend); + stream.on('finish', onfinish); + if (options.error !== false) { + stream.on('error', onerror); + } + stream.on('close', onclose); + if (closed) { + process.nextTick(onclose); + } else if ( + (wState !== null && wState !== undefined && wState.errorEmitted) || + (rState !== null && rState !== undefined && rState.errorEmitted) + ) { + if (!_willEmitClose) { + process.nextTick(onclose); + } + } else if ( + !readable && + (!_willEmitClose || isReadable(stream)) && + (writableFinished || isWritable(stream) === false) + ) { + process.nextTick(onclose); + } else if ( + !writable && + (!_willEmitClose || isWritable(stream)) && + (readableFinished || isReadable(stream) === false) + ) { + process.nextTick(onclose); + } else if (rState && stream.req && stream.aborted) { + process.nextTick(onclose); + } + const cleanup = () => { + callback = nop; + stream.removeListener('aborted', onclose); + stream.removeListener('complete', onfinish); + stream.removeListener('abort', onclose); + stream.removeListener('request', onrequest); + if (stream.req) stream .req.removeListener('finish', onfinish); + stream.removeListener('end', onlegacyfinish); + stream.removeListener('close', onlegacyfinish); + stream.removeListener('finish', onfinish); + stream.removeListener('end', onend); + stream.removeListener('error', onerror); + stream.removeListener('close', onclose); + } + if (options.signal && !closed) { + const abort = () => { + // Keep it because cleanup removes it. + const endCallback = callback + cleanup() + endCallback.call( + stream, + new AbortError(undefined, { + cause: options.signal?.reason + }) + ) + } + if (options.signal.aborted) { + process.nextTick(abort); + } else { + const originalCallback = callback; + callback = once((...args) => { + options.signal.removeEventListener('abort', abort) + originalCallback.apply(stream, args); + }); + options.signal.addEventListener('abort', abort); + } + } + return cleanup; +} + +export function finished(stream, opts) { + return new Promise((resolve, reject) => { + eos(stream, opts, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }) + }); +} + +eos.finished = finished; + +// ====================================================================================== +// Destroy + +function checkError(err, w, r) { + if (err) { + // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 + err.stack; // eslint-disable-line no-unused-expressions + + if (w && !w.errored) { + w.errored = err; + } + if (r && !r.errored) { + r.errored = err; + } + } +} + +export function destroy(err, cb) { + const r = this._readableState; + const w = this._writableState; + // With duplex streams we use the writable side for state. + const s = w || r; + if ((w && w.destroyed) || (r && r.destroyed)) { + if (typeof cb === 'function') { + cb(); + } + return this; + } + + // We set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks + checkError(err, w, r); + if (w) { + w.destroyed = true; + } + if (r) { + r.destroyed = true; + } + + // If still constructing then defer calling _destroy. + if (!s.constructed) { + this.once(kDestroy, function (er) { + _destroy(this, aggregateTwoErrors(er, err), cb); + }); + } else { + _destroy(this, err, cb); + } + return this; +} + +function _destroy(self, err, cb) { + let called = false; + function onDestroy(err) { + if (called) { + return; + } + called = true; + const r = self._readableState; + const w = self._writableState; + checkError(err, w, r); + if (w) { + w.closed = true; + } + if (r) { + r.closed = true; + } + if (typeof cb === 'function') { + cb(err); + } + if (err) { + process.nextTick(emitErrorCloseNT, self, err); + } else { + process.nextTick(emitCloseNT, self); + } + } + try { + self._destroy(err || null, onDestroy); + } catch (err) { + onDestroy(err); + } +} + +function emitErrorCloseNT(self, err) { + emitErrorNT(self, err); + emitCloseNT(self); +} + +function emitCloseNT(self) { + const r = self._readableState; + const w = self._writableState; + if (w) { + w.closeEmitted = true; + } + if (r) { + r.closeEmitted = true; + } + if ((w && w.emitClose) || (r && r.emitClose)) { + self.emit('close'); + } +} + +function emitErrorNT(self, err) { + const r = self._readableState; + const w = self._writableState; + if ((w && w.errorEmitted) || (r && r.errorEmitted)) { + return; + } + if (w) { + w.errorEmitted = true; + } + if (r) { + r.errorEmitted = true; + } + self.emit('error', err); +} + +export function undestroy() { + const r = this._readableState; + const w = this._writableState; + if (r) { + r.constructed = true; + r.closed = false; + r.closeEmitted = false; + r.destroyed = false; + r.errored = null; + r.errorEmitted = false; + r.reading = false; + r.ended = r.readable === false; + r.endEmitted = r.readable === false; + } + if (w) { + w.constructed = true; + w.destroyed = false; + w.closed = false; + w.closeEmitted = false; + w.errored = null; + w.errorEmitted = false; + w.finalCalled = false; + w.prefinished = false; + w.ended = w.writable === false; + w.ending = w.writable === false; + w.finished = w.writable === false; + } +} + +export function errorOrDestroy(stream, err, sync = false) { + // We have tests that rely on errors being emitted + // in the same tick, so changing this is semver major. + // For now when you opt-in to autoDestroy we allow + // the error to be emitted nextTick. In a future + // semver major update we should change the default to this. + + const r = stream._readableState; + const w = stream._writableState; + if ((w && w.destroyed) || (r && r.destroyed)) { + return; + } + if ((r && r.autoDestroy) || (w && w.autoDestroy)) stream.destroy(err); + else if (err) { + // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 + err.stack; // eslint-disable-line no-unused-expressions + + if (w && !w.errored) { + w.errored = err; + } + if (r && !r.errored) { + r.errored = err; + } + if (sync) { + process.nextTick(emitErrorNT, stream, err); + } else { + emitErrorNT(stream, err) + } + } +} + +export function construct(stream, cb) { + if (typeof stream._construct !== 'function') { + return; + } + const r = stream._readableState; + const w = stream._writableState; + if (r) { + r.constructed = false; + } + if (w) { + w.constructed = false; + } + stream.once(kConstruct, cb) + if (stream.listenerCount(kConstruct) > 1) { + // Duplex + return; + } + process.nextTick(constructNT, stream); +} + +function constructNT(stream) { + let called = false; + function onConstruct(err) { + if (called) { + errorOrDestroy(stream, err !== null && err !== undefined ? err : new ERR_MULTIPLE_CALLBACK()); + return; + } + called = true; + const r = stream._readableState; + const w = stream._writableState; + const s = w || r; + if (r) { + r.constructed = true; + } + if (w) { + w.constructed = true; + } + if (s.destroyed) { + stream.emit(kDestroy, err); + } else if (err) { + errorOrDestroy(stream, err, true); + } else { + process.nextTick(emitConstructNT, stream); + } + } + try { + stream._construct(onConstruct); + } catch (err) { + onConstruct(err); + } +} + +function emitConstructNT(stream) { + stream.emit(kConstruct); +} + +function emitCloseLegacy(stream) { + stream.emit('close'); +} + +function emitErrorCloseLegacy(stream, err) { + stream.emit('error', err); + process.nextTick(emitCloseLegacy, stream); +} + +// Normalize destroy for legacy. +export function destroyer(stream, err) { + if (!stream || isDestroyed(stream)) { + return; + } + if (!err && !isFinished(stream)) { + err = new AbortError(); + } + + // TODO: Remove isRequest branches. + if (isServerRequest(stream)) { + stream.socket = null; + stream.destroy(err); + } else if (isRequest(stream)) { + stream.abort(); + } else if (isRequest(stream.req)) { + stream.req.abort(); + } else if (typeof stream.destroy === 'function') { + stream.destroy(err); + } else if (typeof stream.close === 'function') { + // TODO: Don't lose err? + stream.close(); + } else if (err) { + process.nextTick(emitErrorCloseLegacy, stream, err); + } else { + process.nextTick(emitCloseLegacy, stream); + } + if (!stream.destroyed) { + stream[kDestroyed] = true; + } +} + diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_writable.js b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_writable.js new file mode 100644 index 000000000..f356c6794 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/streams_writable.js @@ -0,0 +1,815 @@ +import + EventEmitter + from './events'; + +import { + Stream +} from './streams_legacy'; + +import { + Buffer, +} from './internal_buffer'; + +import * as process from './process'; + +import { + nop, + kOnFinished, + getHighWaterMark, + getDefaultHighWaterMark, + addAbortSignal, + construct, + destroy, + undestroy, + errorOrDestroy, +} from './streams_util'; + +import { + newStreamWritableFromWritableStream, + newWritableStreamFromStreamWritable, +} from './streams_adapters'; + +import { + ERR_INVALID_ARG_TYPE, + ERR_METHOD_NOT_IMPLEMENTED, + ERR_MULTIPLE_CALLBACK, + ERR_STREAM_CANNOT_PIPE, + ERR_STREAM_DESTROYED, + ERR_STREAM_ALREADY_FINISHED, + ERR_STREAM_NULL_VALUES, + ERR_STREAM_WRITE_AFTER_END, + ERR_UNKNOWN_ENCODING +} from './internal_errors'; + +import { isDuplexInstance } from './streams_duplex'; + +// ====================================================================================== +// WritableState + +function WritableState(options, stream, isDuplex) { + // Duplex streams are both readable and writable, but share + // the same options object. + // However, some cases require setting options to different + // values for the readable and the writable sides of the duplex stream, + // e.g. options.readableObjectMode vs. options.writableObjectMode, etc. + if (typeof isDuplex !== 'boolean') isDuplex = isDuplexInstance(stream); + + // Object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!(options?.objectMode); + if (isDuplex) this.objectMode = this.objectMode || !!(options?.writableObjectMode); + + // The point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write(). + this.highWaterMark = options + ? getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex) + : getDefaultHighWaterMark(false); + + // if _final has been called. + this.finalCalled = false; + + // drain event flag. + this.needDrain = false; + // At the start of calling end() + this.ending = false; + // When end() has been called, and returned. + this.ended = false; + // When 'finish' is emitted. + this.finished = false; + + // Has it been destroyed + this.destroyed = false; + + // Should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + const noDecode = !!(options?.decodeStrings === false); + this.decodeStrings = !noDecode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = (options?.defaultEncoding) || 'utf8'; + + // Not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + this.length = 0; + + // A flag to see when we're in the middle of a write. + this.writing = false; + + // When true all writes will be buffered until .uncork() call. + this.corked = 0; + + // A flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // A flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + this.bufferProcessing = false; + + // The callback that's passed to _write(chunk, cb). + this.onwrite = (err) => onwrite.call(undefined, stream, err); + + // The callback that the user supplies to write(chunk, encoding, cb). + this.writecb = null; + + // The amount that is being written when _write is called. + this.writelen = 0; + + // Storage for data passed to the afterWrite() callback in case of + // synchronous _write() completion. + this.afterWriteTickInfo = null; + resetBuffer(this); + + // Number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted. + this.pendingcb = 0; + + // Stream is still being constructed and cannot be + // destroyed until construction finished or failed. + // Async construction is opt in, therefore we start as + // constructed. + this.constructed = true; + + // Emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams. + this.prefinished = false; + + // True if the error was already emitted and should not be thrown again. + this.errorEmitted = false; + + // Should close be emitted on destroy. Defaults to true. + this.emitClose = !options || options.emitClose !== false; + + // Should .destroy() be called after 'finish' (and potentially 'end'). + this.autoDestroy = !options || options.autoDestroy !== false; + + // Indicates whether the stream has errored. When true all write() calls + // should return false. This is needed since when autoDestroy + // is disabled we need a way to tell whether the stream has failed. + this.errored = null; + + // Indicates whether the stream has finished destroying. + this.closed = false; + + // True if close has been emitted or would have been emitted + // depending on emitClose. + this.closeEmitted = false; + this[kOnFinished] = []; +} + +function resetBuffer(state) { + state.buffered = []; + state.bufferedIndex = 0; + state.allBuffers = true; + state.allNoop = true; +} + +WritableState.prototype.getBuffer = function getBuffer() { + return this.buffered.slice(this.bufferedIndex); +} + +Object.defineProperty(WritableState.prototype, 'bufferedRequestCount', { + get() { + return this.buffered.length - this.bufferedIndex; + } +}); + +// ====================================================================================== +// Writable + +Writable.WritableState = WritableState; + +Object.setPrototypeOf(Writable.prototype, Stream.prototype); +Object.setPrototypeOf(Writable, Stream); + +export function Writable(options) { + // Writable ctor is applied to Duplexes, too. + // `realHasInstance` is necessary because using plain `instanceof` + // would return false, as no `_writableState` property is attached. + + // Trying to use the custom `instanceof` for Writable here will also break the + // Node.js LazyTransform implementation, which has a non-trivial getter for + // `_writableState` that would lead to infinite recursion. + + // Checking for a Stream.Duplex instance is faster here instead of inside + // the WritableState constructor, at least with V8 6.5. + const isDuplex = isDuplexInstance(this); + if (!isDuplex && !Writable[Symbol.hasInstance](this)) return new Writable(options); + this._writableState = new WritableState(options, this, isDuplex) + if (options) { + if (typeof options.write === 'function') this._write = options.write; + if (typeof options.writev === 'function') this._writev = options.writev; + if (typeof options.destroy === 'function') this._destroy = options.destroy; + if (typeof options.final === 'function') this._final = options.final; + if (typeof options.construct === 'function') this._construct = options.construct; + if (options.signal) addAbortSignal(options.signal, this); + } + Stream.call(this, options); + construct(this, () => { + const state = this._writableState; + if (!state.writing) { + clearBuffer(this, state); + } + finishMaybe(this, state); + }) +} + +Object.defineProperty(Writable, Symbol.hasInstance, { + value: function (object) { + if (Function.prototype[Symbol.hasInstance].call(this, object)) return true; + if (this !== Writable) return false; + return object?._writableState instanceof WritableState; + } +}) + +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function (_1, _2) { + errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE()); +} + +function _write(stream, chunk, encoding, cb) { + const state = stream._writableState; + if (typeof encoding === 'function') { + cb = encoding; + encoding = state.defaultEncoding; + } else { + if (!encoding) encoding = state.defaultEncoding; + else if (encoding !== 'buffer' && !Buffer.isEncoding(encoding)) { + throw new ERR_UNKNOWN_ENCODING(encoding); + } + if (typeof cb !== 'function') cb = nop; + } + if (chunk === null) { + throw new ERR_STREAM_NULL_VALUES(); + } else if (!state.objectMode) { + if (typeof chunk === 'string') { + if (state.decodeStrings !== false) { + chunk = Buffer.from(chunk, encoding); + encoding = 'buffer'; + } + } else if (chunk instanceof Buffer) { + encoding = 'buffer'; + } else if (Stream._isUint8Array(chunk)) { + chunk = Stream._uint8ArrayToBuffer(chunk); + encoding = 'buffer'; + } else { + throw new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk); + } + } + let err; + if (state.ending) { + err = new ERR_STREAM_WRITE_AFTER_END(); + } else if (state.destroyed) { + err = new ERR_STREAM_DESTROYED('write'); + } + if (err) { + process.nextTick(cb, err); + errorOrDestroy(stream, err, true); + return err; + } + state.pendingcb++; + return writeOrBuffer(stream, state, chunk, encoding, cb); +} + +function write(chunk, encoding, cb) { + return _write(this, chunk, encoding, cb) === true; +} + +Writable.prototype.write = write; + +Writable.prototype.cork = function () { + this._writableState.corked++; +} + +Writable.prototype.uncork = function () { + const state = this._writableState; + if (state.corked) { + state.corked--; + if (!state.writing) clearBuffer(this, state); + } +} + +function setDefaultEncoding(encoding) { + if (typeof encoding === 'string') encoding = encoding.toLowerCase(); + if (!Buffer.isEncoding(encoding)) throw new ERR_UNKNOWN_ENCODING(encoding); + this._writableState.defaultEncoding = encoding; + return this; +} + +Writable.prototype.setDefaultEncoding = setDefaultEncoding; + +// If we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. +function writeOrBuffer(stream, state, chunk, encoding, callback) { + const len = state.objectMode ? 1 : chunk.length; + state.length += len; + + // stream._write resets state.length + const ret = state.length < state.highWaterMark; + // We must ensure that previous needDrain will not be reset to false. + if (!ret) state.needDrain = true; + if (state.writing || state.corked || state.errored || !state.constructed) { + state.buffered.push({ + chunk, + encoding, + callback, + }); + if (state.allBuffers && encoding !== 'buffer') { + state.allBuffers = false; + } + if (state.allNoop && callback !== nop) { + state.allNoop = false; + } + } else { + state.writelen = len; + state.writecb = callback; + state.writing = true; + state.sync = true; + stream._write(chunk, encoding, state.onwrite); + state.sync = false; + } + + // Return false if errored or destroyed in order to break + // any synchronous while(stream.write(data)) loops. + return ret && !state.errored && !state.destroyed; +} + +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write')); + else if (writev) stream._writev(chunk, state.onwrite); + else stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} + +function onwriteError(stream, state, er, cb) { + --state.pendingcb; + cb(er); + // Ensure callbacks are invoked even when autoDestroy is + // not enabled. Passing `er` here doesn't make sense since + // it's related to one specific write, not to the buffered + // writes. + errorBuffer(state); + // This can emit error, but error must always follow cb. + errorOrDestroy(stream, er); +} + +function onwrite(stream, er) { + const state = stream._writableState; + const sync = state.sync; + const cb = state.writecb; + if (typeof cb !== 'function') { + errorOrDestroy(stream, new ERR_MULTIPLE_CALLBACK()); + return; + } + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; + if (er) { + // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 + er.stack; // eslint-disable-line no-unused-expressions + + if (!state.errored) { + state.errored = er; + } + + // In case of duplex streams we need to notify the readable side of the + // error. + if (stream._readableState && !stream._readableState.errored) { + stream._readableState.errored = er; + } + if (sync) { + process.nextTick(onwriteError, stream, state, er, cb); + } else { + onwriteError(stream, state, er, cb); + } + } else { + if (state.buffered.length > state.bufferedIndex) { + clearBuffer(stream, state); + } + if (sync) { + // It is a common case that the callback passed to .write() is always + // the same. In that case, we do not schedule a new nextTick(), but + // rather just increase a counter, to improve performance and avoid + // memory allocations. + if (state.afterWriteTickInfo !== null && state.afterWriteTickInfo.cb === cb) { + state.afterWriteTickInfo.count++; + } else { + state.afterWriteTickInfo = { + count: 1, + cb, + stream, + state + }; + process.nextTick(afterWriteTick, state.afterWriteTickInfo); + } + } else { + afterWrite(stream, state, 1, cb); + } + } +} + +function afterWriteTick({ stream, state, count, cb }) { + state.afterWriteTickInfo = null; + return afterWrite(stream, state, count, cb); +} + +function afterWrite(stream, state, count, cb) { + const needDrain = !state.ending && !stream.destroyed && state.length === 0 && state.needDrain; + if (needDrain) { + state.needDrain = false; + stream.emit('drain'); + } + while (count-- > 0) { + state.pendingcb--; + cb(); + } + if (state.destroyed) { + errorBuffer(state); + } + finishMaybe(stream, state); +} + +// If there's something in the buffer waiting, then invoke callbacks. +function errorBuffer(state) { + if (state.writing) { + return; + } + for (let n = state.bufferedIndex; n < state.buffered.length; ++n) { + let _state$errored; + const { chunk, callback } = state.buffered[n]; + const len = state.objectMode ? 1 : chunk.length; + state.length -= len; + callback( + (_state$errored = state.errored) !== null && _state$errored !== undefined + ? _state$errored + : new ERR_STREAM_DESTROYED('write')); + } + const onfinishCallbacks = state[kOnFinished].splice(0); + for (let i = 0; i < onfinishCallbacks.length; i++) { + let _state$errored2; + onfinishCallbacks[i]( + (_state$errored2 = state.errored) !== null && _state$errored2 !== undefined + ? _state$errored2 + : new ERR_STREAM_DESTROYED('end')); + } + resetBuffer(state); +} + +// If there's something in the buffer waiting, then process it. +function clearBuffer(stream, state) { + if (state.corked || state.bufferProcessing || state.destroyed || !state.constructed) { + return; + } + const { buffered, bufferedIndex, objectMode } = state; + const bufferedLength = buffered.length - bufferedIndex; + if (!bufferedLength) { + return; + } + let i = bufferedIndex; + state.bufferProcessing = true; + if (bufferedLength > 1 && stream._writev) { + state.pendingcb -= bufferedLength - 1; + const callback = state.allNoop + ? nop + : (err) => { + for (let n = i; n < buffered.length; ++n) { + buffered[n].callback(err) + } + }; + // Make a copy of `buffered` if it's going to be used by `callback` above, + // since `doWrite` will mutate the array. + const chunks = state.allNoop && i === 0 ? buffered : buffered.slice(i); + chunks.allBuffers = state.allBuffers; + doWrite(stream, state, true, state.length, chunks, '', callback); + resetBuffer(state); + } else { + do { + const { chunk, encoding, callback } = buffered[i]; + buffered[i++] = null; + const len = objectMode ? 1 : chunk.length; + doWrite(stream, state, false, len, chunk, encoding, callback); + } while (i < buffered.length && !state.writing) + if (i === buffered.length) { + resetBuffer(state); + } else if (i > 256) { + buffered.splice(0, i); + state.bufferedIndex = 0; + } else { + state.bufferedIndex = i; + } + } + state.bufferProcessing = false; +} + +Writable.prototype._write = function (chunk, encoding, cb) { + if (this._writev) { + this._writev( + [ + { + chunk, + encoding + } + ], + cb, + ); + } else { + throw new ERR_METHOD_NOT_IMPLEMENTED('_write()'); + } +} + +Writable.prototype._writev = null; + +function end(chunk, encoding, cb) { + const state = this._writableState; + if (typeof chunk === 'function') { + cb = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === 'function') { + cb = encoding; + encoding = null; + } + let err; + if (chunk !== null && chunk !== undefined) { + const ret = _write(this, chunk, encoding); + if (ret instanceof Error) { + err = ret; + } + } + + // .end() fully uncorks. + if (state.corked) { + state.corked = 1; + this.uncork(); + } + if (err) { + // Do nothing... + } else if (!state.errored && !state.ending) { + // This is forgiving in terms of unnecessary calls to end() and can hide + // logic errors. However, usually such errors are harmless and causing a + // hard error can be disproportionately destructive. It is not always + // trivial for the user to determine whether end() needs to be called + // or not. + + state.ending = true; + finishMaybe(this, state, true); + state.ended = true; + } else if (state.finished) { + err = new ERR_STREAM_ALREADY_FINISHED('end'); + } else if (state.destroyed) { + err = new ERR_STREAM_DESTROYED('end'); + } + if (typeof cb === 'function') { + if (err || state.finished) { + process.nextTick(cb, err); + } else { + state[kOnFinished].push(cb); + } + } + return this; +} + +Writable.prototype.end = end; + +function needFinish(state) { + return ( + state.ending && + !state.destroyed && + state.constructed && + state.length === 0 && + !state.errored && + state.buffered.length === 0 && + !state.finished && + !state.writing && + !state.errorEmitted && + !state.closeEmitted + ); +} + +function callFinal(stream, state) { + let called = false; + function onFinish(err) { + if (called) { + errorOrDestroy(stream, err || new ERR_MULTIPLE_CALLBACK()); + return; + } + called = true; + state.pendingcb--; + if (err) { + const onfinishCallbacks = state[kOnFinished].splice(0); + for (let i = 0; i < onfinishCallbacks.length; i++) { + onfinishCallbacks[i](err); + } + errorOrDestroy(stream, err, state.sync); + } else if (needFinish(state)) { + state.prefinished = true; + stream.emit('prefinish'); + // Backwards compat. Don't check state.sync here. + // Some streams assume 'finish' will be emitted + // asynchronously relative to _final callback. + state.pendingcb++; + process.nextTick(finish, stream, state); + } + } + state.sync = true; + state.pendingcb++; + try { + stream._final(onFinish); + } catch (err) { + onFinish(err); + } + state.sync = false; +} + +function prefinish(stream, state) { + if (!state.prefinished && !state.finalCalled) { + if (typeof stream._final === 'function' && !state.destroyed) { + state.finalCalled = true; + callFinal(stream, state); + } else { + state.prefinished = true; + stream.emit('prefinish'); + } + } +} + +function finishMaybe(stream, state, sync = false) { + if (needFinish(state)) { + prefinish(stream, state); + if (state.pendingcb === 0) { + if (sync) { + state.pendingcb++; + process.nextTick(() => { + ((stream, state) => { + if (needFinish(state)) { + finish(stream, state); + } else { + state.pendingcb--; + } + })(stream, state); + }); + } else if (needFinish(state)) { + state.pendingcb++; + finish(stream, state); + } + } + } +} + +function finish(stream, state) { + state.pendingcb--; + state.finished = true; + const onfinishCallbacks = state[kOnFinished].splice(0); + for (let i = 0; i < onfinishCallbacks.length; i++) { + onfinishCallbacks[i](); + } + stream.emit('finish'); + if (state.autoDestroy) { + // In case of duplex streams we need a way to detect + // if the readable side is ready for autoDestroy as well. + const rState = stream._readableState; + const autoDestroy = + !rState || + (rState.autoDestroy && + // We don't expect the readable to ever 'end' + // if readable is explicitly set to false. + (rState.endEmitted || rState.readable === false)); + if (autoDestroy) { + stream.destroy(); + } + } +} + +Object.defineProperties(Writable.prototype, { + closed: { + get() { + return !!(this._writableState?.closed); + } + }, + destroyed: { + get() { + return !!(this._writableState?.destroyed); + }, + set(value) { + // Backward compatibility, the user is explicitly managing destroyed. + if (this._writableState) { + this._writableState.destroyed = value + } + } + }, + errored: { + enumerable: false, + get() { + return this._writableState?.errored || null; + } + }, + writable: { + get() { + const w = this._writableState + // w.writable === false means that this is part of a Duplex stream + // where the writable side was disabled upon construction. + // Compat. The user might manually disable writable side through + // deprecated setter. + return !!w && w.writable !== false && !w.destroyed && !w.errored && !w.ending && !w.ended; + }, + set(val) { + // Backwards compatible. + if (this._writableState) { + this._writableState.writable = !!val; + } + } + }, + writableFinished: { + get() { + return !!(this._writableState?.finished); + } + }, + writableObjectMode: { + get() { + return !!(this._writableState?.objectMode); + } + }, + writableBuffer: { + get() { + return this._writableState?.getBuffer(); + } + }, + writableEnded: { + get() { + return !!(this._writableState?.ending); + } + }, + writableNeedDrain: { + get() { + const wState = this._writableState; + if (!wState) return false; + return !wState.destroyed && !wState.ending && wState.needDrain; + } + }, + writableHighWaterMark: { + get() { + return this._writableState?.highWaterMark; + } + }, + writableCorked: { + get() { + return this._writableState?.corked | 0; + } + }, + writableLength: { + get() { + return this._writableState?.length; + } + }, + writableAborted: { + enumerable: false, + get() { + return !!( + this._writableState.writable !== false && + (this._writableState.destroyed || this._writableState.errored) && + !this._writableState.finished + ) + } + } +}); + +Writable.prototype.destroy = function (err, cb) { + const state = this._writableState; + + // Invoke pending callbacks. + if (!state.destroyed && (state.bufferedIndex < state.buffered.length || state[kOnFinished].length)) { + process.nextTick(errorBuffer, state); + } + destroy.call(this, err, cb); + return this; +} + +Writable.prototype._undestroy = undestroy + +Writable.prototype._destroy = function (err, cb) { + if (cb) cb(err); +} +Writable.prototype[EventEmitter.captureRejectionSymbol] = function (err) { + this.destroy(err); +} + +Writable.fromWeb = function (writableStream, options) { + return newStreamWritableFromWritableStream(writableStream, options) +} +Writable.toWeb = function (streamWritable) { + return newWritableStreamFromStreamWritable(streamWritable) +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/util.d.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/util.d.ts new file mode 100644 index 000000000..400179abc --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/util.d.ts @@ -0,0 +1,102 @@ +export abstract class MIMEType { + public constructor(input: string); + public type: string; + public subtype: string; + public readonly essence: string; + public readonly params: MIMEParams; + public toString(): string; + public toJSON(): string; +} + +export abstract class MIMEParams { + public constructor(); + public delete(name: string): void; + public get(name: string): string|undefined; + public has(name: string): boolean; + public set(name: string, value: string): void; + public entries(): Iterable; + public keys(): Iterable; + public values(): Iterable; +} + +export const kResourceTypeInspect: unique symbol; + +export const ALL_PROPERTIES: 0; +export const ONLY_ENUMERABLE: 1; +export function getOwnNonIndexProperties(value: unknown, filter: typeof ALL_PROPERTIES | typeof ONLY_ENUMERABLE): PropertyKey[]; + +export const kPending: 0; +export const kFulfilled: 1; +export const kRejected: 2; +export interface PromiseDetails { + state: typeof kPending | typeof kFulfilled | typeof kRejected; + result: unknown; +} +export function getPromiseDetails(value: unknown): PromiseDetails | undefined; + +export interface ProxyDetails { + target: unknown; + handler: unknown; +} +export function getProxyDetails(value: unknown): ProxyDetails | undefined; + +export interface PreviewedEntries { + entries: unknown[]; + isKeyValue: boolean; +} +export function previewEntries(value: unknown): PreviewedEntries | undefined; + +export function getConstructorName(value: unknown): string; + +export type TypedArray = + | Uint8Array + | Uint8ClampedArray + | Uint16Array + | Uint32Array + | Int8Array + | Int16Array + | Int32Array + | BigUint64Array + | BigInt64Array + | Float32Array + | Float64Array; + +export function isArrayBufferView(value: unknown): value is ArrayBufferView; +export function isArgumentsObject(value: unknown): value is IArguments; +export function isArrayBuffer(value: unknown): value is ArrayBuffer; +export function isAsyncFunction(value: unknown): value is Function; +export function isBigInt64Array(value: unknown): value is BigInt64Array; +export function isBigIntObject(value: unknown): value is BigInt; +export function isBigUint64Array(value: unknown): value is BigUint64Array; +export function isBooleanObject(value: unknown): value is Boolean; +export function isDataView(value: unknown): value is DataView; +export function isDate(value: unknown): value is Date; +export function isFloat32Array(value: unknown): value is Float32Array; +export function isFloat64Array(value: unknown): value is Float64Array; +export function isGeneratorFunction(value: unknown): value is GeneratorFunction; +export function isGeneratorObject(value: unknown): value is Generator; +export function isInt8Array(value: unknown): value is Int8Array; +export function isInt16Array(value: unknown): value is Int16Array; +export function isInt32Array(value: unknown): value is Int32Array; +export function isMap(value: unknown): value is Map; +export function isMapIterator(value: unknown): value is IterableIterator; +export function isModuleNamespaceObject(value: unknown): boolean; +export function isNativeError(value: unknown): value is Error; +export function isNumberObject(value: unknown): value is Number; +export function isPromise(value: unknown): value is Promise; +export function isProxy(value: unknown): boolean; +export function isRegExp(value: unknown): value is RegExp; +export function isSet(value: unknown): value is Set; +export function isSetIterator(value: unknown): value is IterableIterator; +export function isSharedArrayBuffer(value: unknown): value is SharedArrayBuffer; +export function isStringObject(value: unknown): value is String; +export function isSymbolObject(value: unknown): value is Symbol; +export function isTypedArray(value: unknown): value is TypedArray; +export function isUint8Array(value: unknown): value is Uint8Array; +export function isUint8ClampedArray(value: unknown): value is Uint8ClampedArray; +export function isUint16Array(value: unknown): value is Uint16Array; +export function isUint32Array(value: unknown): value is Uint32Array; +export function isWeakMap(value: unknown): value is WeakMap; +export function isWeakSet(value: unknown): value is WeakSet; +export function isAnyArrayBuffer(value: unknown): value is ArrayBuffer | SharedArrayBuffer; +export function isBoxedPrimitive(value: unknown): value is Number | String | Boolean | BigInt | Symbol; diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/util.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/util.ts new file mode 100644 index 000000000..2821fd065 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/util.ts @@ -0,0 +1,256 @@ +import { ALL_PROPERTIES as A_PROPERTIES, ONLY_CONFIGURABLE, ONLY_ENUMERABLE as O_ENUMERABLE, ONLY_WRITABLE, SKIP_STRINGS, SKIP_SYMBOLS, isArrayIndex } from "./internal_utils"; + +export const kPending = 0; +export const kFulfilled= 1; +export const kRejected = 2; + +export const kResourceTypeInspect: unique symbol = Symbol.for("nodejs.util.inspect.custom"); + +export interface PromiseDetails { + state: typeof kPending | typeof kFulfilled | typeof kRejected; + result: unknown; +} + +export type TypedArray = + | Uint8Array + | Uint8ClampedArray + | Uint16Array + | Uint32Array + | Int8Array + | Int16Array + | Int32Array + | BigUint64Array + | BigInt64Array + | Float32Array + | Float64Array; + +export interface PreviewedEntries { + entries: unknown[]; + isKeyValue: boolean; +} +export interface ProxyDetails { + target: unknown; + handler: unknown; +} + +export function getOwnNonIndexProperties( + // deno-lint-ignore ban-types + obj: object, + filter: number, +): (string | symbol)[] { + let allProperties = [ + ...Object.getOwnPropertyNames(obj), + ...Object.getOwnPropertySymbols(obj), + ]; + + if (Array.isArray(obj)) { + allProperties = allProperties.filter((k) => !isArrayIndex(k)); + } + + if (filter === A_PROPERTIES) { + return allProperties; + } + + const result: (string | symbol)[] = []; + for (const key of allProperties) { + const desc = Object.getOwnPropertyDescriptor(obj, key); + if (desc === undefined) { + continue; + } + if (filter & ONLY_WRITABLE && !desc.writable) { + continue; + } + if (filter & O_ENUMERABLE && !desc.enumerable) { + continue; + } + if (filter & ONLY_CONFIGURABLE && !desc.configurable) { + continue; + } + if (filter & SKIP_STRINGS && typeof key === "string") { + continue; + } + if (filter & SKIP_SYMBOLS && typeof key === "symbol") { + continue; + } + result.push(key); + } + return result; +} + +export const ALL_PROPERTIES = A_PROPERTIES; +export const ONLY_ENUMERABLE = O_ENUMERABLE; + +// TODO: implement this properly +export function isArrayBufferView(value: unknown): value is ArrayBufferView { + return false; +} + +export function isArgumentsObject(value: unknown): value is IArguments { + return false; +} + +export function isArrayBuffer(value: unknown): value is ArrayBuffer { + return false; +} + +export function isAsyncFunction(value: unknown): value is Function { + return false; +} + +export function isBigInt64Array(value: unknown): value is BigInt64Array { + return false; +} + +export function isBigIntObject(value: unknown): value is BigInt { + return false; +} + +export function isBigUint64Array(value: unknown): value is BigUint64Array { + return false; +} + +export function isBooleanObject(value: unknown): value is Boolean { + return false; +} + +export function isDataView(value: unknown): value is DataView { + return false; +} + +export function isDate(value: unknown): value is Date { + return false; +} + +export function isFloat32Array(value: unknown): value is Float32Array { + return false; +} + +export function isFloat64Array(value: unknown): value is Float64Array { + return false; +} + +export function isGeneratorFunction(value: unknown): value is GeneratorFunction { + return false; +} + +export function isGeneratorObject(value: unknown): value is Generator { + return false; +} + +export function isInt8Array(value: unknown): value is Int8Array { + return false; +} + +export function isInt16Array(value: unknown): value is Int16Array { + return false; +} + +export function isInt32Array(value: unknown): value is Int32Array { + return false; +} + +export function isMap(value: unknown): value is Map { + return false; +} + +export function isMapIterator(value: unknown): value is IterableIterator { + return false; +} + +export function isModuleNamespaceObject(value: unknown): boolean { + return false; +} + +export function isNativeError(value: unknown): value is Error { + return false; +} + +export function isNumberObject(value: unknown): value is Number { + return false; +} + +export function isPromise(value: unknown): value is Promise { + return false; +} + +export function isProxy(value: unknown): boolean { + return false; +} + +export function isRegExp(value: unknown): value is RegExp { + return false; +} + +export function isSet(value: unknown): value is Set { + return false; +} + +export function isSetIterator(value: unknown): value is IterableIterator { + return false; +} + +export function isSharedArrayBuffer(value: unknown): value is SharedArrayBuffer { + return false; +} + +export function isStringObject(value: unknown): value is String { + return false; +} + +export function isSymbolObject(value: unknown): value is Symbol { + return false; +} + +export function isTypedArray(value: unknown): value is TypedArray { + return false; +} + +export function isUint8Array(value: unknown): value is Uint8Array { + return false; +} + +export function isUint8ClampedArray(value: unknown): value is Uint8ClampedArray { + return false; +} + +export function isUint16Array(value: unknown): value is Uint16Array { + return false; +} + +export function isUint32Array(value: unknown): value is Uint32Array { + return false; +} + +export function isWeakMap(value: unknown): value is WeakMap { + return false; +} + +export function isWeakSet(value: unknown): value is WeakSet { + return false; +} + +export function isAnyArrayBuffer(value: unknown): value is ArrayBuffer | SharedArrayBuffer { + return false; +} + +export function isBoxedPrimitive(value: unknown): value is Number | String | Boolean | BigInt | Symbol { + return false; +} + + +export function getPromiseDetails(value: unknown): PromiseDetails | undefined { + return undefined +} + +export function getProxyDetails(value: unknown): ProxyDetails | undefined { + return undefined +} + +export function previewEntries(value: unknown): PreviewedEntries | undefined { + return undefined; +} + +export function getConstructorName(value: unknown): string { + return ""; +} + diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/internal/validators.ts b/JS/wasm/crates/wasmjs-engine/shims/src/internal/validators.ts new file mode 100644 index 000000000..58985a377 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/internal/validators.ts @@ -0,0 +1,189 @@ +import { isArrayBufferView } from "./internal_types"; +import { normalizeEncoding } from "./internal_utils"; +import { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_OUT_OF_RANGE, +} from "./internal_errors"; + +// TODO(someday): Not current implementing parseFileMode, validatePort + +export const isInt32 = (value: any) => value === (value | 0); +export const isUint32 = (value: any) => value === (value >>> 0); + +export function validateBuffer(buffer: unknown, name = "buffer") { + if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE( + name, + ["Buffer", "TypedArray", "DataView"], + buffer, + ); + } +}; + +export function validateInteger( + value: unknown, + name: string, + min = Number.MIN_SAFE_INTEGER, + max = Number.MAX_SAFE_INTEGER) { + if (typeof value !== "number") { + throw new ERR_INVALID_ARG_TYPE(name, "number", value); + } + if (!Number.isInteger(value)) { + throw new ERR_OUT_OF_RANGE(name, "an integer", value); + } + if (value < min || value > max) { + throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } +} + +export interface ValidateObjectOptions { + allowArray?: boolean; + allowFunction?: boolean; + nullable?: boolean; +}; + +export function validateObject(value: unknown, name: string, options?: ValidateObjectOptions) { + const useDefaultOptions = options == null; + const allowArray = useDefaultOptions ? false : options.allowArray; + const allowFunction = useDefaultOptions ? false : options.allowFunction; + const nullable = useDefaultOptions ? false : options.nullable; + if ( + (!nullable && value === null) || + (!allowArray && Array.isArray(value)) || + (typeof value !== "object" && ( + !allowFunction || typeof value !== "function" + )) + ) { + throw new ERR_INVALID_ARG_TYPE(name, "Object", value); + } +}; + +export function validateInt32(value: any, name: string, min = -2147483648, max = 2147483647) { + if (!isInt32(value)) { + if (typeof value !== "number") { + throw new ERR_INVALID_ARG_TYPE(name, "number", value); + } + + if (!Number.isInteger(value)) { + throw new ERR_OUT_OF_RANGE(name, "an integer", value); + } + + throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } + + if (value < min || value > max) { + throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } +} + +export function validateUint32(value: unknown, name: string, positive?: boolean) { + if (!isUint32(value)) { + if (typeof value !== "number") { + throw new ERR_INVALID_ARG_TYPE(name, "number", value); + } + if (!Number.isInteger(value)) { + throw new ERR_OUT_OF_RANGE(name, "an integer", value); + } + const min = positive ? 1 : 0; + // 2 ** 32 === 4294967296 + throw new ERR_OUT_OF_RANGE( + name, + `>= ${min} && < 4294967296`, + value, + ); + } + if (positive && value === 0) { + throw new ERR_OUT_OF_RANGE(name, ">= 1 && < 4294967296", value); + } +} + +export function validateString(value: unknown, name: string) { + if (typeof value !== "string") { + throw new ERR_INVALID_ARG_TYPE(name, "string", value); + } +} + +export function validateNumber(value: unknown, name: string) { + if (typeof value !== "number") { + throw new ERR_INVALID_ARG_TYPE(name, "number", value); + } +} + +export function validateBoolean(value: unknown, name: string) { + if (typeof value !== "boolean") { + throw new ERR_INVALID_ARG_TYPE(name, "boolean", value); + } +} + +export function validateOneOf(value: unknown, name: string, oneOf: any[]) { + if (!Array.prototype.includes.call(oneOf, value)) { + const allowed = Array.prototype.join.call( + Array.prototype.map.call( + oneOf, + (v) => (typeof v === "string" ? `'${v}'` : String(v)), + ), + ", ", + ); + const reason = "must be one of: " + allowed; + + throw new ERR_INVALID_ARG_VALUE(name, value, reason); + } +} + +export function validateEncoding(data: unknown, encoding: string): void { + const normalizedEncoding = normalizeEncoding(encoding); + const length = (data as any).length; + + if (normalizedEncoding === "hex" && length % 2 !== 0) { + throw new ERR_INVALID_ARG_VALUE( + "encoding", + encoding, + `is invalid for data of length ${length}`, + ); + } +} + +export function validateAbortSignal(signal: unknown, name: string) { + if ( + signal !== undefined && + (signal === null || + typeof signal !== "object" || + !("aborted" in signal)) + ) { + throw new ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); + } +}; + +export function validateFunction(value: unknown, name: string) { + if (typeof value !== "function") { + throw new ERR_INVALID_ARG_TYPE(name, "Function", value); + } +} + +export function validateArray(value: unknown, name: string, minLength = 0) { + if (!Array.isArray(value)) { + throw new ERR_INVALID_ARG_TYPE(name, "Array", value); + } + if (value.length < minLength) { + const reason = `must be longer than ${minLength}`; + throw new ERR_INVALID_ARG_VALUE(name, value, reason); + } +} + +export default { + isInt32, + isUint32, + validateAbortSignal, + validateArray, + validateBoolean, + validateBuffer, + validateFunction, + validateInt32, + validateInteger, + validateNumber, + validateObject, + validateOneOf, + validateString, + validateUint32, +}; \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/path.js b/JS/wasm/crates/wasmjs-engine/shims/src/path.js new file mode 100644 index 000000000..08cce9319 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/path.js @@ -0,0 +1,61 @@ +import { + posix, + win32, +} from './internal/internal_path'; + +const { + resolve, + normalize, + isAbsolute, + join, + relative, + toNamespacedPath, + dirname, + basename, + extname, + format, + parse, + sep, + delimiter, +} = posix; + +export { + resolve, + normalize, + isAbsolute, + join, + relative, + toNamespacedPath, + dirname, + basename, + extname, + format, + parse, + sep, + delimiter, + posix, + win32, +}; + +export { default } from './internal/internal_path' +import process from './internal/process'; + +globalThis.process = process; + +globalThis.path = { + resolve, + normalize, + isAbsolute, + join, + relative, + toNamespacedPath, + dirname, + basename, + extname, + format, + parse, + sep, + delimiter, + posix, + win32, +} diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/path/posix.ts b/JS/wasm/crates/wasmjs-engine/shims/src/path/posix.ts new file mode 100644 index 000000000..03168d917 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/path/posix.ts @@ -0,0 +1,3 @@ +import { posix } from '../internal/internal_path'; + +export default posix; diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/path/win32.ts b/JS/wasm/crates/wasmjs-engine/shims/src/path/win32.ts new file mode 100644 index 000000000..440107305 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/shims/src/path/win32.ts @@ -0,0 +1,3 @@ +import { win32 } from '../internal/internal_path'; + +export default win32; diff --git a/JS/wasm/crates/wasmjs-engine/src/main.rs b/JS/wasm/crates/wasmjs-engine/src/main.rs new file mode 100644 index 000000000..afa620a1b --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/src/main.rs @@ -0,0 +1,235 @@ +use std::{ + collections::HashMap, + env, fs, + io::{stdin, stdout, Read, Write}, +}; + +use javy::{ + json, + quickjs::{JSContextRef, JSValue, JSValueRef}, + Runtime, +}; + +wit_bindgen_rust::import!({paths: ["../../assets/wasmjs/wit/http.wit"]}); +use crate::http::*; + +pub enum RuntimeError { + InvalidBinding { invalid_export: String }, +} + +pub fn load_bindings(context: &JSContextRef, global: JSValueRef) -> Result<(), RuntimeError> { + global + .set_property( + "__send_http_request", + context + .wrap_callback(|_ctx, _this_arg, args| { + let uri = args[0].to_string(); + + let opts: HashMap = args[1].try_into()?; + let method = opts.get("method").unwrap().to_string(); + let headers = opts.get("headers").unwrap(); + let body = opts.get("body").unwrap(); + + let method = match method.as_str() { + "GET" => HttpMethod::Get, + "POST" => HttpMethod::Post, + _ => HttpMethod::Get, + }; + + let mut parsed_headers: Vec<(String, String)> = Vec::new(); + + if let JSValue::Object(headers) = headers { + for (key, val) in headers.iter() { + parsed_headers.push((key.to_string(), val.to_string())); + } + } + + let headers_slice: &[(&str, &str)] = &parsed_headers + .iter() + .map(|(k, v)| (k.as_str(), v.as_str())) + .collect::>(); + + let parsed_body: Option<&[u8]>; + + if let JSValue::ArrayBuffer(buf) = body { + parsed_body = Some(buf.as_ref()); + } else if let JSValue::String(data) = body { + parsed_body = Some(data.as_bytes()); + } else { + parsed_body = None; + } + + let req = HttpRequest { + uri: uri.as_str(), + body: parsed_body, + headers: headers_slice, + method, + params: &[], + }; + + match send_http_request(req) { + Ok(result) => { + let body = result.body.unwrap_or(Vec::new()); + let mut headers = HashMap::new(); + + for (key, val) in result.headers.iter() { + headers.insert(key.as_str(), JSValue::String(val.to_string())); + } + + let parsed_result = HashMap::from([ + ("status", JSValue::Int(result.status as i32)), + ("body", JSValue::ArrayBuffer(body)), + ("headers", JSValue::from_hashmap(headers)), + ]); + + Ok(JSValue::from_hashmap(parsed_result)) + } + Err(err) => { + let kind = match err.error { + HttpError::InvalidRequest => "Invalid Request".to_string(), + HttpError::InvalidRequestBody => "Invalid Request Body".to_string(), + HttpError::InvalidResponseBody => { + "Invalid Response Body".to_string() + } + HttpError::NotAllowed => "Not allowed".to_string(), + HttpError::InternalError => "Internal Error".to_string(), + HttpError::Timeout => "Request Timeout".to_string(), + HttpError::RedirectLoop => "Redirect Loop".to_string(), + }; + + Ok(JSValue::from_hashmap(HashMap::from([ + ("error", JSValue::Bool(true)), + ("type", JSValue::String(kind)), + ("message", JSValue::String(err.message)), + ]))) + } + } + }) + .map_err(|_| RuntimeError::InvalidBinding { + invalid_export: "send_http_request".to_string(), + })?, + ) + .map_err(|_| RuntimeError::InvalidBinding { + invalid_export: "send_http_request".to_string(), + })?; + + global + .set_property( + "__console_log", + context + .wrap_callback(|_ctx, _this_arg, args| { + let msg = args[0].to_string(); + eprintln!("{msg}"); + + Ok(JSValue::Null) + }) + .map_err(|_| RuntimeError::InvalidBinding { + invalid_export: "console_log".to_string(), + })?, + ) + .map_err(|_| RuntimeError::InvalidBinding { + invalid_export: "console_log".to_string(), + })?; + + Ok(()) +} + +static POLYFILL: &str = include_str!("../shims/dist/index.js"); +static POLYFILL_BUFFER: &str = include_str!("../shims/dist/buffer.js"); + +static POLYFILL_PATH: &str = include_str!("../shims/dist/path.js"); + +static POLYFILL_CRYPTO: &str = include_str!("../shims/dist/crypto.js"); + +fn main() { + let runtime = Runtime::default(); + let context = runtime.context(); + + let source = fs::read_to_string("/src/index.js"); + let mut contents = String::new(); + let mut buffer = String::new(); + let mut path = String::new(); + let mut crypto = String::new(); + + let mut request = String::new(); + contents.push_str(POLYFILL); + buffer.push_str(POLYFILL_BUFFER); + path.push_str(POLYFILL_PATH); + crypto.push_str(POLYFILL_CRYPTO); + + stdin().read_to_string(&mut request).unwrap(); + + let mut env_vars = HashMap::new(); + for (key, val) in env::vars() { + env_vars.insert(key, val); + } + let env_vars_json = serde_json::to_string(&env_vars).unwrap(); + contents.push_str(&format!("globalThis.env = {};", env_vars_json)); + + contents.push_str(&source.unwrap()); + + let global = context.global_object().unwrap(); + match load_bindings(context, global) { + Ok(_) => {} + Err(e) => match e { + RuntimeError::InvalidBinding { invalid_export } => { + eprintln!("There was an error adding the '{invalid_export}' binding"); + } + }, + } + + context.eval_module("buffer", &buffer).unwrap(); + context.eval_module("crypto", &crypto).unwrap(); + + match context.eval_module("path", &path) { + Ok(_) => {} + Err(err) => eprintln!("Error loading the path shim: {err}"), + }; + + context.eval_module("handler.mjs", &contents).unwrap(); + + context + .eval_module( + "runtime.mjs", + &format!( + r#" + import {{ default as handler }} from 'handler.mjs'; + addEventListener('fetch', (e) => {{ + e.respondWith(handler.fetch(e.request)) + }}); + "# + ), + ) + .unwrap(); + + let global = context.global_object().unwrap(); + let entrypoint = global.get_property("entrypoint").unwrap(); + + let input_bytes = request.as_bytes(); + let input_value = json::transcode_input(context, input_bytes).unwrap(); + + match entrypoint.call(&global, &[input_value]) { + Ok(_) => {} + Err(err) => eprintln!("Error calling the main entrypoint: {err}"), + }; + + if context.is_pending() { + if let Err(err) = context.execute_pending() { + eprintln!("Error running async methods: {err}"); + } + } + + let global = context.global_object().unwrap(); + let error_value = global.get_property("error").unwrap(); + let output_value = global.get_property("result").unwrap(); + + if !error_value.is_null_or_undefined() { + eprintln!("{}", error_value.as_str_lossy()); + } + + let output = json::transcode_output(output_value).unwrap(); + + stdout() + .write_all(&output) + .expect("Error when returning the response"); +} diff --git a/JS/wasm/crates/wasmjs-engine/taskfile.yml b/JS/wasm/crates/wasmjs-engine/taskfile.yml new file mode 100644 index 000000000..69d28cf08 --- /dev/null +++ b/JS/wasm/crates/wasmjs-engine/taskfile.yml @@ -0,0 +1,23 @@ +version: "3" + +tasks: + add:wasm32-wasi:target: rustup target add wasm32-wasi + build: + deps: + - add:wasm32-wasi:target + - build-shims + cmds: + - cargo build --target wasm32-wasi --release + - cp ./target/wasm32-wasi/release/wasmjs-engine.wasm ./ + build-shims: + deps: + - shims-install + dir: shims + cmds: + - echo pwd + - npm run build + shims-install: + dir: shims + cmds: + - pwd + - npm install \ No newline at end of file diff --git a/JS/wasm/crates/wasmjs-engine/wasmjs-engine.wasm b/JS/wasm/crates/wasmjs-engine/wasmjs-engine.wasm new file mode 100755 index 000000000..49b685141 Binary files /dev/null and b/JS/wasm/crates/wasmjs-engine/wasmjs-engine.wasm differ diff --git a/JS/wasm/crates/wasmjs-runtime/Cargo.lock b/JS/wasm/crates/wasmjs-runtime/Cargo.lock new file mode 100644 index 000000000..9477b9411 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/Cargo.lock @@ -0,0 +1,3915 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" +dependencies = [ + "bitflags 1.3.2", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-files" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d832782fac6ca7369a70c9ee9a20554623c5e51c76e190ad151780ebea1cf689" +dependencies = [ + "actix-http", + "actix-service", + "actix-utils", + "actix-web", + "askama_escape", + "bitflags 1.3.2", + "bytes", + "derive_more", + "futures-core", + "http-range", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", +] + +[[package]] +name = "actix-http" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash", + "base64", + "bitflags 2.4.1", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd 0.12.4", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn 2.0.41", +] + +[[package]] +name = "actix-router" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" +dependencies = [ + "bytestring", + "http", + "regex", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2 0.5.5", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.5.5", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "bytestring" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +dependencies = [ + "bytes", +] + +[[package]] +name = "cap-fs-ext" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b779b2d0a001c125b4584ad586268fb4b92d957bff8d26d7fe0dd78283faa814" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes", + "windows-sys 0.48.0", +] + +[[package]] +name = "cap-net-ext" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffc30dee200c20b4dcb80572226f42658e1d9c4b668656d7cc59c33d50e396e" +dependencies = [ + "cap-primitives", + "cap-std", + "rustix", + "smallvec", +] + +[[package]] +name = "cap-primitives" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf30c373a3bee22c292b1b6a7a26736a38376840f1af3d2d806455edf8c3899" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix", + "windows-sys 0.48.0", + "winx", +] + +[[package]] +name = "cap-rand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577de6cff7c2a47d6b13efe5dd28bf116bd7f8f7db164ea95b7cc2640711f522" +dependencies = [ + "ambient-authority", + "rand", +] + +[[package]] +name = "cap-std" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84bade423fa6403efeebeafe568fdb230e8c590a275fba2ba978dd112efcf6e9" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "rustix", +] + +[[package]] +name = "cap-time-ext" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f52b3c8f4abfe3252fd0a071f3004aaa3b18936ec97bdbd8763ce03aff6247" +dependencies = [ + "cap-primitives", + "once_cell", + "rustix", + "winx", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "751cbf89e513f283c0641eb7f95dc72fda5051dd95ca203d1dc45e26bc89dba8" +dependencies = [ + "cranelift-entity 0.100.1", +] + +[[package]] +name = "cranelift-codegen" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "210730edc05121e915201cc36595e1f00062094669fa07ac362340e3627b3dc5" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity 0.100.1", + "cranelift-isle", + "gimli", + "hashbrown 0.14.3", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5dc7fdf210c53db047f3eaf49b3a89efee0cc3d9a2ce0c0f0236933273d0c53" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46875cc87d963119d78fe5c19852757dc6eea3cb9622c0df69c26b242cd44b4" + +[[package]] +name = "cranelift-control" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375dca8f58d8a801a85e11730c1529c5c4a9c3593dfb12118391ac437b037155" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc619b86fe3c72f43fc417c9fd67a04ec0c98296e5940922d9fd9e6eedf72521" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "cranelift-entity" +version = "0.102.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb25f573701284fe2bcf88209d405342125df00764b396c923e11eafc94d892" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "cranelift-frontend" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb607fd19ae264da18f9f2532e7302b826f7fbf77bf88365fc075f2e3419436" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fe806a6470dddfdf79e878af6a96afb1235a09fe3e21f9e0c2f18d402820432" + +[[package]] +name = "cranelift-native" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac7f1722660b10af1f7229c0048f716bfd8bd344549b0e06e3eb6417ec3fe5b" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1b65810be56b619c3c55debade92798d999f34bf0670370c578afab5d905f06" +dependencies = [ + "cranelift-codegen", + "cranelift-entity 0.100.1", + "cranelift-frontend", + "itertools 0.10.5", + "log", + "smallvec", + "wasmparser 0.112.0", + "wasmtime-types 13.0.1", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fd-lock" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93f7a0db71c99f68398f80653ed05afb0b00e062e1a20c7ff849c4edfabbbcc" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs-set-times" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" +dependencies = [ + "io-lifetimes", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "fxprof-processed-profile" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" +dependencies = [ + "bitflags 2.4.1", + "debugid", + "fxhash", + "serde", + "serde_json", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "io-extras" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c301e73fb90e8a29e600a9f402d095765f74310d582916a952f618836a1bd1ed" +dependencies = [ + "io-lifetimes", + "windows-sys 0.52.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "ittapi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a5c0b993601cad796222ea076565c5d9f337d35592f8622c753724f06d7271" +dependencies = [ + "anyhow", + "ittapi-sys", + "log", +] + +[[package]] +name = "ittapi-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7b5e473765060536a660eed127f758cf1a810c73e49063264959c60d1727d9" +dependencies = [ + "cc", +] + +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "crc32fast", + "hashbrown 0.14.3", + "indexmap", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "pori" +version = "0.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a63d338dec139f56dacc692ca63ad35a6be6a797442479b55acd611d79e906" +dependencies = [ + "nom", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "pulldown-cmark" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regalloc2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" +dependencies = [ + "hashbrown 0.13.2", + "log", + "rustc-hash", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.20", +] + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "itoa", + "libc", + "linux-raw-sys", + "once_cell", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "394cec28fa623e00903caf7ba4fa6fb9a0e260280bb8cdbbba029611108a0190" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha256" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", + "tokio", +] + +[[package]] +name = "shellexpand" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +dependencies = [ + "dirs", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + +[[package]] +name = "stable_deref_trait" +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 = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-interface" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ce32341b2c0b70c144bbf35627fdc1ef18c76ced5e5e7b3ee8b5ba6b2ab6a0" +dependencies = [ + "bitflags 2.4.1", + "cap-fs-ext", + "cap-std", + "fd-lock", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", + "winx", +] + +[[package]] +name = "target-lexicon" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.5", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi-cap-std-sync" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c4db6155e71cfae4ed732d87c2583faf4bbdcb77372697eb77d636f46108ba" +dependencies = [ + "anyhow", + "async-trait", + "cap-fs-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "io-extras", + "io-lifetimes", + "is-terminal", + "once_cell", + "rustix", + "system-interface", + "tracing", + "wasi-common", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasi-common" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3f291b2a567f266ac488715f1742f62b2ca633524708c62ead9c0f71b7d72c" +dependencies = [ + "anyhow", + "bitflags 2.4.1", + "cap-rand", + "cap-std", + "io-extras", + "log", + "rustix", + "thiserror", + "tracing", + "wasmtime 13.0.1", + "wiggle 13.0.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.41", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wasm-encoder" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-encoder" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasmjs-runtime" +version = "0.1.0" +dependencies = [ + "actix-files", + "actix-web", + "anyhow", + "base64", + "blake3", + "clap", + "http", + "lazy_static", + "regex", + "reqwest", + "semver 0.10.0", + "serde", + "serde_json", + "sha256", + "thiserror", + "tokio", + "toml 0.8.8", + "tracing", + "wasi-common", + "wasmparser 0.115.0", + "wasmtime 13.0.1", + "wasmtime-wasi", + "wax", + "wiggle 15.0.1", + "wit-bindgen-wasmtime", +] + +[[package]] +name = "wasmparser" +version = "0.112.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e986b010f47fcce49cf8ea5d5f9e5d2737832f12b53ae8ae785bbe895d0877bf" +dependencies = [ + "indexmap", + "semver 1.0.20", +] + +[[package]] +name = "wasmparser" +version = "0.115.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e06c0641a4add879ba71ccb3a1e4278fd546f76f1eafb21d8f7b07733b547cd5" +dependencies = [ + "indexmap", + "semver 1.0.20", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap", + "semver 1.0.20", +] + +[[package]] +name = "wasmparser" +version = "0.118.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" +dependencies = [ + "indexmap", + "semver 1.0.20", +] + +[[package]] +name = "wasmprinter" +version = "0.2.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d027eb8294904fc715ac0870cebe6b0271e96b90605ee21511e7565c4ce568c" +dependencies = [ + "anyhow", + "wasmparser 0.118.1", +] + +[[package]] +name = "wasmtime" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0263693caa1486bd4d26a5f18511948a706c9290689386b81b851ce088063ce" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "bumpalo", + "cfg-if", + "encoding_rs", + "fxprof-processed-profile", + "indexmap", + "libc", + "log", + "object", + "once_cell", + "paste", + "psm", + "rayon", + "serde", + "serde_derive", + "serde_json", + "target-lexicon", + "wasm-encoder 0.32.0", + "wasmparser 0.112.0", + "wasmtime-cache", + "wasmtime-component-macro 13.0.1", + "wasmtime-component-util 13.0.1", + "wasmtime-cranelift", + "wasmtime-environ 13.0.1", + "wasmtime-fiber 13.0.1", + "wasmtime-jit 13.0.1", + "wasmtime-runtime 13.0.1", + "wasmtime-winch", + "wat", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642e12d108e800215263e3b95972977f473957923103029d7d617db701d67ba4" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "bumpalo", + "cfg-if", + "indexmap", + "libc", + "log", + "object", + "once_cell", + "paste", + "psm", + "serde", + "serde_derive", + "serde_json", + "target-lexicon", + "wasmparser 0.116.1", + "wasmtime-component-macro 15.0.1", + "wasmtime-environ 15.0.1", + "wasmtime-fiber 15.0.1", + "wasmtime-jit 15.0.1", + "wasmtime-runtime 15.0.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711e5969236ecfbe70c807804ff9ffb5206c1dbb5c55c5e8200d9f7e8e76adf" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beada8bb15df52503de0a4c58de4357bfd2f96d9a44a6e547bad11efdd988b47" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-cache" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b79f9f79188e5a26b6911b79d3171c06699d9a17ae07f6a265c51635b8d80c2" +dependencies = [ + "anyhow", + "base64", + "bincode", + "directories-next", + "log", + "rustix", + "serde", + "serde_derive", + "sha2", + "toml 0.5.11", + "windows-sys 0.48.0", + "zstd 0.11.2+zstd.1.5.2", +] + +[[package]] +name = "wasmtime-component-macro" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed724d0f41c21bcf8754651a59d0423c530069ddca4cf3822768489ad313a812" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 2.0.41", + "wasmtime-component-util 13.0.1", + "wasmtime-wit-bindgen 13.0.1", + "wit-parser 0.11.3", +] + +[[package]] +name = "wasmtime-component-macro" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ccba556991465cca68d5a54769684bcf489fb532059da55105f851642d52c1" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 2.0.41", + "wasmtime-component-util 15.0.1", + "wasmtime-wit-bindgen 15.0.1", + "wit-parser 0.13.0", +] + +[[package]] +name = "wasmtime-component-util" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7d69464b94bd312a27d93d0b482cd74bedf01f030199ef0740d6300ebca1d3" + +[[package]] +name = "wasmtime-component-util" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05492a177a6006cb73f034d6e9a6fad6da55b23c4398835cb0012b5fa51ecf67" + +[[package]] +name = "wasmtime-cranelift" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e63f53c61ba05eb815f905c1738ad82c95333dd42ef5a8cc2aa3d7dfb2b08d7" +dependencies = [ + "anyhow", + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity 0.100.1", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli", + "log", + "object", + "target-lexicon", + "thiserror", + "wasmparser 0.112.0", + "wasmtime-cranelift-shared", + "wasmtime-environ 13.0.1", + "wasmtime-versioned-export-macros 13.0.1", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f6b197d68612f7dc3a17aa9f9587533715ecb8b4755609ce9baf7fb92b74ddc" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-native", + "gimli", + "object", + "target-lexicon", + "wasmtime-environ 13.0.1", +] + +[[package]] +name = "wasmtime-environ" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e2558c8b04fd27764d8601d46b8dc39555b79720a41e626bce210a80758932" +dependencies = [ + "anyhow", + "cranelift-entity 0.100.1", + "gimli", + "indexmap", + "log", + "object", + "serde", + "serde_derive", + "target-lexicon", + "thiserror", + "wasm-encoder 0.32.0", + "wasmparser 0.112.0", + "wasmprinter", + "wasmtime-component-util 13.0.1", + "wasmtime-types 13.0.1", +] + +[[package]] +name = "wasmtime-environ" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6d33a9f421da810a070cd56add9bc51f852bd66afbb8b920489d6242f15b70e" +dependencies = [ + "anyhow", + "cranelift-entity 0.102.1", + "gimli", + "indexmap", + "log", + "object", + "serde", + "serde_derive", + "target-lexicon", + "thiserror", + "wasmparser 0.116.1", + "wasmtime-types 15.0.1", +] + +[[package]] +name = "wasmtime-fiber" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a615a2cf64a49c0dc659c7d850c6cd377b975e0abfdcf0888b282d274a82e730" +dependencies = [ + "cc", + "cfg-if", + "rustix", + "wasmtime-asm-macros 13.0.1", + "wasmtime-versioned-export-macros 13.0.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-fiber" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404741f4c6d7f4e043be2e8b466406a2aee289ccdba22bf9eba6399921121b97" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "rustix", + "wasmtime-asm-macros 15.0.1", + "wasmtime-versioned-export-macros 15.0.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-jit" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd775514b8034b85b0323bfdc60abb1c28d27dbf6e22aad083ed57dac95cf72e" +dependencies = [ + "addr2line", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli", + "ittapi", + "log", + "object", + "rustc-demangle", + "rustix", + "serde", + "serde_derive", + "target-lexicon", + "wasmtime-environ 13.0.1", + "wasmtime-jit-debug 13.0.1", + "wasmtime-jit-icache-coherence 13.0.1", + "wasmtime-runtime 13.0.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-jit" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d0994a86d6dca5f7d9740d7f2bd0568be06d2014a550361dc1c397d289d81ef" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "gimli", + "log", + "object", + "rustix", + "serde", + "serde_derive", + "target-lexicon", + "wasmtime-environ 15.0.1", + "wasmtime-jit-icache-coherence 15.0.1", + "wasmtime-runtime 15.0.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c054e27c6ce2a6191edabe89e646da013044dd5369e1d203c89f977f9bd32937" +dependencies = [ + "object", + "once_cell", + "rustix", + "wasmtime-versioned-export-macros 13.0.1", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0c4b74e606d1462d648631d5bc328e3d5b14e7f9d3ff93bc6db062fb8c5cd8" +dependencies = [ + "once_cell", + "wasmtime-versioned-export-macros 15.0.1", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f323977cddf4a262d1b856366b665c5b4d01793c57b79fb42505b9fd9e61e5b" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3090a69ba1476979e090aa7ed4bc759178bafdb65b22f98b9ba24fc6e7e578d5" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29e26461bba043f73cb4183f4ce0d606c0eaac112475867b11e5ea36fe1cac8e" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "encoding_rs", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset", + "paste", + "rand", + "rustix", + "sptr", + "wasm-encoder 0.32.0", + "wasmtime-asm-macros 13.0.1", + "wasmtime-environ 13.0.1", + "wasmtime-fiber 13.0.1", + "wasmtime-jit-debug 13.0.1", + "wasmtime-versioned-export-macros 13.0.1", + "wasmtime-wmemcheck 13.0.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b993ac8380385ed67bf71b51b9553edcf1ab0801b78a805a067de581b9a3e88a" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset", + "paste", + "rand", + "rustix", + "sptr", + "wasm-encoder 0.36.2", + "wasmtime-asm-macros 15.0.1", + "wasmtime-environ 15.0.1", + "wasmtime-fiber 15.0.1", + "wasmtime-jit-debug 15.0.1", + "wasmtime-versioned-export-macros 15.0.1", + "wasmtime-wmemcheck 15.0.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-types" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd7e9b29fee64eea5058cb5e7cb3480b52c2f1312d431d16ea8617ceebeb421" +dependencies = [ + "cranelift-entity 0.100.1", + "serde", + "serde_derive", + "thiserror", + "wasmparser 0.112.0", +] + +[[package]] +name = "wasmtime-types" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b5778112fcab2dc3d4371f4203ab8facf0c453dd94312b0a88dd662955e64e0" +dependencies = [ + "cranelift-entity 0.102.1", + "serde", + "serde_derive", + "thiserror", + "wasmparser 0.116.1", +] + +[[package]] +name = "wasmtime-versioned-export-macros" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6362c557c36d8ad4aaab735f14ed9e4f78d6b40ec85a02a88fd859af87682e52" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "wasmtime-versioned-export-macros" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50f51f8d79bfd2aa8e9d9a0ae7c2d02b45fe412e62ff1b87c0c81b07c738231" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "wasmtime-wasi" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c9e79f73320d96cd7644b021502dffee09dd92300b073f3541ae44e9ae377c" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.4.1", + "bytes", + "cap-fs-ext", + "cap-net-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "futures", + "io-extras", + "io-lifetimes", + "is-terminal", + "libc", + "once_cell", + "rustix", + "system-interface", + "thiserror", + "tokio", + "tracing", + "wasi-cap-std-sync", + "wasi-common", + "wasmtime 13.0.1", + "wiggle 13.0.1", + "windows-sys 0.48.0", +] + +[[package]] +name = "wasmtime-winch" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa5fc7212424c04c01a20bfa66c4c518e8749dde6546f5e05815dcacbec80723" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "object", + "target-lexicon", + "wasmparser 0.112.0", + "wasmtime-cranelift-shared", + "wasmtime-environ 13.0.1", + "winch-codegen", +] + +[[package]] +name = "wasmtime-wit-bindgen" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcc03bd58f77a68dc6a0b2ba2f8e64b1f902b50389d21bbcc690ef2f3bb87198" +dependencies = [ + "anyhow", + "heck 0.4.1", + "indexmap", + "wit-parser 0.11.3", +] + +[[package]] +name = "wasmtime-wit-bindgen" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b804dfd3d0c0d6d37aa21026fe7772ba1a769c89ee4f5c4f13b82d91d75216f" +dependencies = [ + "anyhow", + "heck 0.4.1", + "indexmap", + "wit-parser 0.13.0", +] + +[[package]] +name = "wasmtime-wmemcheck" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e485bf54eba675ca615f8f55788d3a8cd44e7bd09b8b4011edc22c2c41d859e" + +[[package]] +name = "wasmtime-wmemcheck" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6060bc082cc32d9a45587c7640e29e3c7b89ada82677ac25d87850aaccb368" + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "69.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ee37317321afde358e4d7593745942c48d6d17e0e6e943704de9bbee121e7a" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.38.1", +] + +[[package]] +name = "wat" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb338ee8dee4d4cd05e6426683f21c5087dc7cfc8903e839ccf48d43332da3c" +dependencies = [ + "wast 69.0.1", +] + +[[package]] +name = "wax" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d12a78aa0bab22d2f26ed1a96df7ab58e8a93506a3e20adb47c51a93b4e1357" +dependencies = [ + "const_format", + "itertools 0.11.0", + "nom", + "pori", + "regex", + "thiserror", + "walkdir", +] + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wiggle" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e81ddbdc400b38d04241d740d0406ef343bd242c460f252fe59f29ad964ad24c" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.4.1", + "thiserror", + "tracing", + "wasmtime 13.0.1", + "wiggle-macro 13.0.1", +] + +[[package]] +name = "wiggle" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91028b241e692fdf30627ac10ba9d5ac378353ea4119b4f904ac95177057a44" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.4.1", + "thiserror", + "tracing", + "wasmtime 15.0.1", + "wiggle-macro 15.0.1", + "witx", +] + +[[package]] +name = "wiggle-generate" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c993123d6db1a1908ef8352aabdf2e681a3dcdedc3656beb747e4db16d3cf08" +dependencies = [ + "anyhow", + "heck 0.4.1", + "proc-macro2", + "quote", + "shellexpand", + "syn 2.0.41", + "witx", +] + +[[package]] +name = "wiggle-generate" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e8b3d76531994513671b2ec3b29fd342bf041e2282945bb6c52eebe6aa9e7da" +dependencies = [ + "anyhow", + "heck 0.4.1", + "proc-macro2", + "quote", + "shellexpand", + "syn 2.0.41", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476e3e09bc68e82624b70a322265515523754cb9e05fcacceabd216e276bc2ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", + "wiggle-generate 13.0.1", +] + +[[package]] +name = "wiggle-macro" +version = "15.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c189fe00c67f61bb330827f2abab1af9b5925c7929535cd13a68d265ec20b02d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", + "wiggle-generate 15.0.1", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winch-codegen" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b01ca6722f7421c9cdbe4c9b62342ce864d0a9e8736d56dac717a86b1a65ae" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "regalloc2", + "smallvec", + "target-lexicon", + "wasmparser 0.112.0", + "wasmtime-environ 13.0.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "winx" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" +dependencies = [ + "bitflags 2.4.1", + "windows-sys 0.52.0", +] + +[[package]] +name = "wit-bindgen-gen-core" +version = "0.2.0" +source = "git+https://github.com/fermyon/wit-bindgen-backport?rev=598cd229bb43baceff9616d16930b8a5a3e79d79#598cd229bb43baceff9616d16930b8a5a3e79d79" +dependencies = [ + "anyhow", + "wit-parser 0.2.0", +] + +[[package]] +name = "wit-bindgen-gen-rust" +version = "0.2.0" +source = "git+https://github.com/fermyon/wit-bindgen-backport?rev=598cd229bb43baceff9616d16930b8a5a3e79d79#598cd229bb43baceff9616d16930b8a5a3e79d79" +dependencies = [ + "heck 0.3.3", + "wit-bindgen-gen-core", +] + +[[package]] +name = "wit-bindgen-gen-wasmtime" +version = "0.2.0" +source = "git+https://github.com/fermyon/wit-bindgen-backport?rev=598cd229bb43baceff9616d16930b8a5a3e79d79#598cd229bb43baceff9616d16930b8a5a3e79d79" +dependencies = [ + "heck 0.3.3", + "wit-bindgen-gen-core", + "wit-bindgen-gen-rust", +] + +[[package]] +name = "wit-bindgen-wasmtime" +version = "0.2.0" +source = "git+https://github.com/fermyon/wit-bindgen-backport?rev=598cd229bb43baceff9616d16930b8a5a3e79d79#598cd229bb43baceff9616d16930b8a5a3e79d79" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 1.3.2", + "thiserror", + "wasmtime 13.0.1", + "wit-bindgen-wasmtime-impl", +] + +[[package]] +name = "wit-bindgen-wasmtime-impl" +version = "0.2.0" +source = "git+https://github.com/fermyon/wit-bindgen-backport?rev=598cd229bb43baceff9616d16930b8a5a3e79d79#598cd229bb43baceff9616d16930b8a5a3e79d79" +dependencies = [ + "proc-macro2", + "syn 1.0.109", + "wit-bindgen-gen-core", + "wit-bindgen-gen-wasmtime", +] + +[[package]] +name = "wit-parser" +version = "0.2.0" +source = "git+https://github.com/fermyon/wit-bindgen-backport?rev=598cd229bb43baceff9616d16930b8a5a3e79d79#598cd229bb43baceff9616d16930b8a5a3e79d79" +dependencies = [ + "anyhow", + "id-arena", + "pulldown-cmark 0.8.0", + "unicode-normalization", + "unicode-xid", +] + +[[package]] +name = "wit-parser" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a39edca9abb16309def3843af73b58d47d243fe33a9ceee572446bcc57556b9a" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "pulldown-cmark 0.9.3", + "semver 1.0.20", + "serde", + "serde_json", + "unicode-xid", + "url", +] + +[[package]] +name = "wit-parser" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15df6b7b28ce94b8be39d8df5cb21a08a4f3b9f33b631aedb4aa5776f785ead3" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver 1.0.20", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "witx" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" +dependencies = [ + "anyhow", + "log", + "thiserror", + "wast 35.0.2", +] + +[[package]] +name = "zerocopy" +version = "0.7.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +dependencies = [ + "zstd-safe 6.0.6", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-safe" +version = "6.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/JS/wasm/crates/wasmjs-runtime/Cargo.toml b/JS/wasm/crates/wasmjs-runtime/Cargo.toml new file mode 100644 index 000000000..a675c8278 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "wasmjs-runtime" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +actix-web = "4" +lazy_static = "1" +reqwest = "0.11" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +clap = { version = "4", features = ["derive"] } +tokio = { version = "1", features = ["full"] } +wasmtime = "13" +wasmtime-wasi = "13" +wasi-common = "13" +wit-bindgen-wasmtime = { git = "https://github.com/fermyon/wit-bindgen-backport", rev = "598cd229bb43baceff9616d16930b8a5a3e79d79", features = [ + "async", +] } +wax = "0.6" +regex = "1" +base64 = "0.21" +sha256 = "1" +blake3 = "1.5.0" +wasmparser = "0.115.0" +toml = "0.8.6" +actix-files = "0.6.2" +http = "0.2.11" +wiggle = "15.0.0" +thiserror = "1.0" +tracing = "0.1.40" +semver = "^0.10.0" diff --git a/JS/wasm/crates/wasmjs-runtime/arakoo.toml b/JS/wasm/crates/wasmjs-runtime/arakoo.toml new file mode 100644 index 000000000..c18b6ba81 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/arakoo.toml @@ -0,0 +1,61 @@ +[local_server] +[local_server.geolocation] +use_default_loopback = false +[local_server.geolocation.mapping.InlineToml.addresses] +[local_server.geolocation.mapping.InlineToml.addresses."103.117.185.66".data] +as_name = "shree net online services private limited" +as_number = 137679 +area_code = 0 +city = "bhiwandi" +conn_speed = "broadband" +conn_type = "wifi" +continent = "AS" +country_code = "IN" +country_code3 = "IND" +country_name = "india" +latitude = 19.24 +longitude = 73.02 +metro_code = 356002 +postal_code = "421302" +proxy_description = "?" +proxy_type = "?" +region = "MH" +utc_offset = 530 +[local_server.geolocation.mapping.InlineToml.addresses."127.0.0.1".data] +as_name = "Arakoo Test" +as_number = 12345 +area_code = 123 +city = "Test City" +conn_speed = "broadband" +conn_type = "wired" +continent = "NA" +country_code = "CA" +country_code3 = "CAN" +country_name = "Canada" +latitude = 12.345 +longitude = 54.321 +metro_code = 0 +postal_code = "12345" +proxy_description = "?" +proxy_type = "?" +region = "BC" +utc_offset = -700 +[local_server.geolocation.mapping.InlineToml.addresses."0000:0000:0000:0000:0000:0000:0000:0001".data] +as_name = "Arakoo Test IPv6" +as_number = 12345 +area_code = 123 +city = "Test City IPv6" +conn_speed = "broadband" +conn_type = "wired" +continent = "NA" +country_code = "CA" +country_code3 = "CAN" +country_name = "Canada" +latitude = 12.345 +longitude = 54.321 +metro_code = 0 +postal_code = "12345" +proxy_description = "?" +proxy_type = "?" +region = "BC" +utc_offset = -700 diff --git a/JS/wasm/crates/wasmjs-runtime/src/bindings.rs b/JS/wasm/crates/wasmjs-runtime/src/bindings.rs new file mode 100644 index 000000000..3d748c6eb --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/bindings.rs @@ -0,0 +1,194 @@ +use actix_web::http::Uri; +use reqwest::Method; +use serde::Deserialize; +use tokio::runtime::Builder; +use wiggle::GuestErrorType; +use crate::error::Error; + +wit_bindgen_wasmtime::export!({paths: ["../../assets/wasmjs/wit/http.wit",], + async:[] + } +); + +wiggle::from_witx!({ + witx:["$CARGO_MANIFEST_DIR/../../assets/wasmjs/wit/arakoo-geo.witx"], + errors: { arakoo_status => Error }, +}); + +impl GuestErrorType for ArakooStatus { + fn success() -> Self { + ArakooStatus::Ok + } +} + +use self::{http::{Http, HttpRequest, HttpRequestError, HttpResponse, HttpError, HttpMethod}, types::ArakooStatus}; + +#[derive(Deserialize, Clone)] +#[serde(default)] +pub struct HttpRequestsConfig { + pub allowed_hosts: Vec, + pub allowed_methods: Vec, + pub allow_http: bool, +} + +impl Default for HttpRequestsConfig { + fn default() -> Self { + Self { + allowed_hosts: vec!["aws.connect.psdb.cloud".to_string()], + allowed_methods: Vec::from([ + String::from("GET"), + String::from("POST"), + String::from("PUT"), + String::from("PATCH"), + String::from("DELETE"), + ]), + allow_http: false, + } + } +} + +pub struct HttpBindings { + pub http_config: HttpRequestsConfig, +} + +impl From for reqwest::Method { + fn from(value: HttpMethod) -> Self { + match value { + HttpMethod::Get => Method::GET, + HttpMethod::Post => Method::POST, + HttpMethod::Put => Method::PUT, + HttpMethod::Patch => Method::PATCH, + HttpMethod::Delete => Method::DELETE, + HttpMethod::Options => Method::OPTIONS, + HttpMethod::Head => Method::HEAD, + } + } +} + +impl From for HttpError { + fn from(value: reqwest::Error) -> Self { + if value.is_timeout() { + HttpError::Timeout + } else if value.is_redirect() { + HttpError::RedirectLoop + } else if value.is_request() { + HttpError::InvalidRequest + } else if value.is_body() { + HttpError::InvalidRequestBody + } else if value.is_decode() { + HttpError::InvalidResponseBody + } else { + HttpError::InternalError + } + } +} + +impl Http for HttpBindings { + fn send_http_request( + &mut self, + req: HttpRequest<'_>, + ) -> Result { + let mut headers = Vec::new(); + let url = req.uri.to_string(); + let body = req.body.unwrap_or(&[]).to_vec(); + let uri = url.parse::().map_err(|e| HttpRequestError { + error: HttpError::InvalidRequest, + message: e.to_string(), + })?; + let method: Method = req.method.into(); + + if uri.host().is_some() + && !self + .http_config + .allowed_hosts + .contains(&uri.host().unwrap().to_string()) + { + return Err(HttpRequestError { + error: HttpError::NotAllowed, + message: format!("'{}' is not in the allowed hosts list", uri.host().unwrap()), + }); + } + + if uri.scheme().is_some() + && (!self.http_config.allow_http && uri.scheme_str().unwrap() == "http") + { + return Err(HttpRequestError { + error: HttpError::NotAllowed, + message: "Only https is allowed".to_string(), + }); + } + + if !self + .http_config + .allowed_methods + .contains(&method.to_string()) + { + return Err(HttpRequestError { + error: HttpError::NotAllowed, + message: format!("'{}' method not allowed", method.as_str()), + }); + } + + for (key, value) in req.headers { + headers.push((key.to_string(), value.to_string())); + } + + let thread_result = std::thread::spawn(move || { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + let client = reqwest::Client::new(); + + let mut builder = client.request(method, url); + + for (key, value) in headers { + builder = builder.header(key, value); + } + + builder = builder.body(body); + + match builder.send().await { + Ok(res) => { + let mut headers = Vec::new(); + let status = res.status().as_u16(); + + for (name, value) in res.headers().iter() { + headers + .push((name.to_string(), value.to_str().unwrap().to_string())); + } + + let body = res.bytes().await; + + Ok(HttpResponse { + headers, + status, + body: Some(body.unwrap().to_vec()), + }) + } + Err(e) => { + let message = e.to_string(); + + Err(HttpRequestError { + error: e.into(), + message, + }) + } + } + }) + }) + .join(); + + match thread_result { + Ok(res) => match res { + Ok(res) => Ok(res), + Err(err) => Err(err), + }, + Err(_) => Err(HttpRequestError { + error: HttpError::InternalError, + message: "Could not process the request".to_string(), + }), + } + } +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/config.rs b/JS/wasm/crates/wasmjs-runtime/src/config.rs new file mode 100644 index 000000000..69123fd29 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/config.rs @@ -0,0 +1,181 @@ +use crate::bindings::HttpRequestsConfig; +use anyhow::Result; +use serde::Deserialize; +use serde::Deserializer; +use std::collections::HashMap; +use std::{env, fs}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use toml::Table; +use crate::error::ArakooConfigError; +use crate::geolocation::Geolocation; + +#[derive(Deserialize, Clone, Default)] +#[serde(default)] +pub struct Features { + pub http_requests: HttpRequestsConfig, + pub geo: ArakooConfig +} + +#[derive(Deserialize, Clone, Default)] +pub struct Folder { + #[serde(deserialize_with = "deserialize_path", default)] + pub from: PathBuf, + pub to: String, +} + +fn deserialize_path<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let result: Result = Deserialize::deserialize(deserializer); + + match result { + Ok(value) => { + let split = if value.contains('/') { + value.split('/') + } else { + value.split('\\') + }; + + Ok(split.fold(PathBuf::new(), |mut acc, el| { + acc.push(el); + acc + })) + } + Err(err) => Err(err), + } +} + +#[derive(Debug, Clone, Deserialize)] +pub struct ArakooConfig { + pub(crate) local_server: LocalServerConfig, +} + +impl Default for ArakooConfig { + fn default() -> Self { + ArakooConfig{ + local_server: LocalServerConfig::default() + } + } +} + +impl ArakooConfig { + pub fn geolocation(&self) -> &Geolocation { + &self.local_server.geolocation + } + pub fn from_file(path: impl AsRef) -> Result { + fs::read_to_string(path.as_ref()) + .map_err(|err| ArakooConfigError::IoError { + path: path.as_ref().display().to_string(), + err, + }) + .and_then(Self::from_str) + } + + /// Parse a string containing TOML data into a `ArakooConfig`. + fn from_str(toml: impl AsRef) -> Result { + toml::from_str::(toml.as_ref()) + .map_err(Into::into) + .and_then(TryInto::try_into) + } + +} + +impl FromStr for ArakooConfig { + type Err = ArakooConfigError; + fn from_str(s: &str) -> Result { + Self::from_str(s) + } +} + +#[derive(Deserialize)] +struct TomlArakooConfig { + local_server: Option, +} + +impl TryInto for TomlArakooConfig { + type Error = ArakooConfigError; + fn try_into(self) -> Result { + let Self { + local_server, + } = self; + let local_server: LocalServerConfig = local_server + .map(TryInto::try_into) + .transpose()? + .unwrap_or_default(); + Ok(ArakooConfig { + local_server, + }) + } +} + +#[derive(Deserialize, Clone, Default)] +pub struct Config { + pub name: Option, + #[serde(default)] + pub features: Features, + pub folders: Option>, + #[serde(deserialize_with = "read_environment_variables", default)] + pub vars: HashMap, +} + +fn read_environment_variables<'de, D>( + deserializer: D, +) -> core::result::Result, D::Error> +where + D: Deserializer<'de>, +{ + let result: core::result::Result>, D::Error> = + Deserialize::deserialize(deserializer); + + match result { + Ok(value) => match value { + Some(mut options) => { + for (_, value) in options.iter_mut() { + if value.starts_with('$') && !value.contains(' ') { + value.remove(0); + + match env::var(&value) { + Ok(env_value) => *value = env_value, + Err(_) => *value = String::new(), + } + } + } + + Ok(options) + } + None => Ok(HashMap::new()), + }, + Err(err) => Err(err), + } +} + +#[derive(Clone, Debug, Default, Deserialize, )] +pub struct LocalServerConfig { + geolocation: Geolocation, +} + + +#[derive(Deserialize)] +struct RawLocalServerConfig { + geolocation: Option, +} + +impl TryInto for RawLocalServerConfig { + type Error = ArakooConfigError; + fn try_into(self) -> Result { + let Self { + geolocation, + } = self; + let geolocation = if let Some(geolocation) = geolocation { + geolocation.try_into()? + } else { + Geolocation::default() + }; + + Ok(LocalServerConfig { + geolocation, + }) + } +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/error.rs b/JS/wasm/crates/wasmjs-runtime/src/error.rs new file mode 100644 index 000000000..944a5eff2 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/error.rs @@ -0,0 +1,103 @@ +//! Error types. + +use thiserror::Error; +use wiggle::GuestError; + +#[derive(Debug, Error)] +#[non_exhaustive] +pub enum Error { + #[error("Buffer length error: {buf} too long to fit in {len}")] + BufferLengthError { + buf: &'static str, + len: &'static str, + }, + #[error("Invalid argument given")] + InvalidArgument, + #[error(transparent)] + GeolocationError(#[from] crate::wiggle_abi::geo_impl::GeolocationError), + #[error("Shared memory not supported yet")] + SharedMemory, + #[error("Guest error: [{0}]")] + GuestError(#[from] GuestError), +} + +/// Errors that may occur while validating geolocation configurations. +#[derive(Debug, Error)] +pub enum GeolocationConfigError { + #[error(transparent)] + IoError(std::io::Error), + + #[error("definition was not provided as a TOML table")] + InvalidEntryType, + + #[error("missing 'file' field")] + MissingFile, + + #[error("'file' field is empty")] + EmptyFileEntry, + + #[error("missing 'addresses' field")] + MissingAddresses, + + #[error("inline geolocation value was not a string")] + InvalidInlineEntryType, + + #[error("'file' field was not a string")] + InvalidFileEntry, + + #[error("'addresses' was not provided as a TOML table")] + InvalidAddressesType, + + // #[error("unrecognized key '{0}'")] + // UnrecognizedKey(String), + + // #[error("missing 'format' field")] + // MissingFormat, + + #[error("'format' field was not a string")] + InvalidFormatEntry, + + #[error("IP address not valid: '{0}'")] + InvalidAddressEntry(String), + + #[error("'{0}' is not a valid format for the geolocation mapping. Supported format(s) are: 'inline-toml', 'json'.")] + InvalidGeolocationMappingFormat(String), + + #[error( + "The file is of the wrong format. The file is expected to contain a single JSON Object" + )] + GeolocationFileWrongFormat, + + #[error("'format' field is empty")] + EmptyFormatEntry, + + // #[error("Item value under key named '{key}' is of the wrong format. The value is expected to be a JSON String")] + // GeolocationItemValueWrongFormat { key: String }, +} + +#[derive(Debug, Error)] +pub enum ArakooConfigError { + + #[error("invalid configuration for '{name}': {err}")] + InvalidGeolocationDefinition { + name: String, + #[source] + err: GeolocationConfigError, + }, + + #[error("error parsing `edge.toml`: {0}")] + InvalidArakooToml(#[from] toml::de::Error), + + #[error("invalid manifest version: {0}")] + InvalidManifestVersion(#[from] semver::SemVerError), + + /// An I/O error that occurred while reading the file. + #[error("error reading '{path}': {err}")] + IoError { + path: String, + #[source] + err: std::io::Error, + }, + // #[error("error reading: {err}")] + // TomlError { err: toml::de::Error }, +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/geolocation.rs b/JS/wasm/crates/wasmjs-runtime/src/geolocation.rs new file mode 100644 index 000000000..72ffdc2a7 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/geolocation.rs @@ -0,0 +1,308 @@ +use serde::{Deserialize, Serialize}; +use { + crate::error::GeolocationConfigError, + serde_json::{ + Map, Number, Value as SerdeValue, Value::Number as SerdeNumber, + Value::String as SerdeString, + }, + std::{collections::HashMap, fs, iter::FromIterator, net::IpAddr, path::Path, path::PathBuf}, +}; + +#[derive(Clone, Debug,Deserialize)] +pub struct Geolocation { + mapping: GeolocationMapping, + use_default_loopback: bool, +} + +#[derive(Clone, Debug, Deserialize)] +pub enum GeolocationMapping { + Empty, + InlineToml { + addresses: HashMap, + }, + Json { + file: PathBuf, + }, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct GeolocationData { + pub(crate) data: Map, +} + +impl Default for Geolocation { + fn default() -> Self { + Self { + mapping: GeolocationMapping::default(), + use_default_loopback: true, + } + } +} + +impl Geolocation { + + pub fn lookup(&self, addr: &IpAddr) -> Option { + self.mapping.get(addr).or_else(|| { + if self.use_default_loopback && addr.is_loopback() { + Some(GeolocationData::default()) + } else { + None + } + }) + } +} + +mod deserialization { + use std::{net::IpAddr, str::FromStr}; + + use serde_json::Number; + + use { + super::{Geolocation, GeolocationData, GeolocationMapping}, + crate::error::{ArakooConfigError, GeolocationConfigError}, + serde_json::Value as SerdeValue, + std::path::PathBuf, + std::{collections::HashMap, convert::TryFrom}, + toml::value::{Table, Value}, + }; + + impl TryFrom
for Geolocation { + type Error = ArakooConfigError; + + fn try_from(toml: Table) -> Result { + fn process_config(mut toml: Table) -> Result { + let use_default_loopback = toml.remove("use_default_loopback").map_or( + Ok(true), + |use_default_loopback| match use_default_loopback { + Value::Boolean(use_default_loopback) => Ok(use_default_loopback), + _ => Err(GeolocationConfigError::InvalidEntryType), + }, + )?; + + let mapping = match toml.remove("format") { + Some(Value::String(value)) => match value.as_str() { + "inline-toml" => process_inline_toml_dictionary(&mut toml)?, + "json" => process_json_entries(&mut toml)?, + "" => return Err(GeolocationConfigError::EmptyFormatEntry), + format => { + return Err(GeolocationConfigError::InvalidGeolocationMappingFormat( + format.to_string(), + )) + } + }, + Some(_) => return Err(GeolocationConfigError::InvalidFormatEntry), + None => GeolocationMapping::Empty, + }; + + Ok(Geolocation { + mapping, + use_default_loopback, + }) + } + + process_config(toml).map_err(|err| ArakooConfigError::InvalidGeolocationDefinition { + name: "geolocation_mapping".to_string(), + err, + }) + } + } + + pub fn parse_ip_address(address: &str) -> Result { + IpAddr::from_str(address) + .map_err(|err| GeolocationConfigError::InvalidAddressEntry(err.to_string())) + } + + fn process_inline_toml_dictionary( + toml: &mut Table, + ) -> Result { + fn convert_value_to_json(value: Value) -> Option { + match value { + Value::String(value) => Some(SerdeValue::String(value)), + Value::Integer(value) => Number::try_from(value).ok().map(SerdeValue::Number), + Value::Float(value) => Number::from_f64(value).map(SerdeValue::Number), + Value::Boolean(value) => Some(SerdeValue::Bool(value)), + _ => None, + } + } + + // Take the `addresses` field from the provided TOML table. + let toml = match toml + .remove("addresses") + .ok_or(GeolocationConfigError::MissingAddresses)? + { + Value::Table(table) => table, + _ => return Err(GeolocationConfigError::InvalidAddressesType), + }; + + let mut addresses = HashMap::::with_capacity(toml.len()); + + for (address, value) in toml { + let address = parse_ip_address(address.as_str())?; + let table = value + .as_table() + .ok_or(GeolocationConfigError::InvalidInlineEntryType)? + .to_owned(); + + let mut geolocation_data = GeolocationData::new(); + + for (field, value) in table { + let value = convert_value_to_json(value) + .ok_or(GeolocationConfigError::InvalidInlineEntryType)?; + geolocation_data.insert(field, value); + } + + addresses.insert(address, geolocation_data); + } + + Ok(GeolocationMapping::InlineToml { addresses }) + } + + fn process_json_entries( + toml: &mut Table, + ) -> Result { + let file: PathBuf = match toml + .remove("file") + .ok_or(GeolocationConfigError::MissingFile)? + { + Value::String(file) => { + if file.is_empty() { + return Err(GeolocationConfigError::EmptyFileEntry); + } else { + file.into() + } + } + _ => return Err(GeolocationConfigError::InvalidFileEntry), + }; + + GeolocationMapping::read_json_contents(&file)?; + + Ok(GeolocationMapping::Json { file }) + } +} + +impl Default for GeolocationMapping { + fn default() -> Self { + Self::Empty + } +} + +impl GeolocationMapping { + pub fn get(&self, address: &IpAddr) -> Option { + match self { + Self::Empty => None, + Self::InlineToml { addresses } => addresses + .get(address) + .map(|geolocation_data| geolocation_data.to_owned()), + Self::Json { file } => Self::read_json_contents(file) + .ok() + .map(|addresses| { + addresses + .get(address) + .map(|geolocation_data| geolocation_data.to_owned()) + }) + .unwrap(), + } + } + + pub fn read_json_contents( + file: &Path, + ) -> Result, GeolocationConfigError> { + let data = fs::read_to_string(file).map_err(GeolocationConfigError::IoError)?; + + // Deserialize the contents of the given JSON file. + let json = match serde_json::from_str(&data) + .map_err(|_| GeolocationConfigError::GeolocationFileWrongFormat)? + { + // Check that we were given an object. + serde_json::Value::Object(obj) => obj, + _ => { + return Err(GeolocationConfigError::GeolocationFileWrongFormat); + } + }; + + let mut addresses = HashMap::::with_capacity(json.len()); + + for (address, value) in json { + let address = deserialization::parse_ip_address(address.as_str())?; + let table = value + .as_object() + .ok_or(GeolocationConfigError::InvalidInlineEntryType)? + .to_owned(); + + let geolocation_data = GeolocationData::from(&table); + + addresses.insert(address, geolocation_data); + } + + Ok(addresses) + } +} + +impl Default for GeolocationData { + fn default() -> Self { + let default_entries = HashMap::<&str, SerdeValue>::from([ + ("as_name", SerdeString(String::from("Arakoo Cloud, Inc"))), + ("as_number", SerdeNumber(Number::from(54113))), + ("area_code", SerdeNumber(Number::from(415))), + ("city", SerdeString(String::from("San Francisco"))), + ("conn_speed", SerdeString(String::from("broadband"))), + ("conn_type", SerdeString(String::from("wired"))), + ("continent", SerdeString(String::from("NA"))), + ("country_code", SerdeString(String::from("US"))), + ("country_code3", SerdeString(String::from("USA"))), + ( + "country_name", + SerdeString(String::from("United States of America")), + ), + ("latitude", SerdeNumber(Number::from_f64(37.77869).unwrap())), + ( + "longitude", + SerdeNumber(Number::from_f64(-122.39557).unwrap()), + ), + ("metro_code", SerdeNumber(Number::from(0))), + ("postal_code", SerdeString(String::from("94107"))), + ("proxy_description", SerdeString(String::from("?"))), + ("proxy_type", SerdeString(String::from("?"))), + ("region", SerdeString(String::from("CA"))), + ("utc_offset", SerdeNumber(Number::from(-700))), + ]); + + Self::from(default_entries) + } +} + +impl From> for GeolocationData { + fn from(value: HashMap<&str, SerdeValue>) -> Self { + let entries = value + .iter() + .map(|(&field, value)| (field.to_string(), value.to_owned())); + + Self { + data: Map::from_iter(entries), + } + } +} + +impl From<&Map> for GeolocationData { + fn from(data: &Map) -> Self { + Self { + data: data.to_owned(), + } + } +} + +impl GeolocationData { + pub fn new() -> Self { + Self { data: Map::new() } + } + + pub fn insert(&mut self, field: String, value: SerdeValue) { + self.data.insert(field, value); + } +} + +impl ToString for GeolocationData { + fn to_string(&self) -> String { + serde_json::to_string(&self.data).unwrap_or_else(|_| "".to_string()) + } +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/handlers.rs b/JS/wasm/crates/wasmjs-runtime/src/handlers.rs new file mode 100644 index 000000000..ec7a7416b --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/handlers.rs @@ -0,0 +1,159 @@ +use crate::{io::WasmOutput, routes::WORKERS, server::AppData}; +use actix_files::NamedFile; +use actix_web::{ + http::StatusCode, + web::{Bytes, Data}, + HttpRequest, HttpResponse, +}; +use std::{ + io::{Error, ErrorKind}, + path::{Component, Path, PathBuf}, +}; +use std::net::{IpAddr, Ipv4Addr}; +use std::str::FromStr; +use crate::geolocation::GeolocationData; + +fn clean_up_path(uri: &str) -> PathBuf { + let path = PathBuf::from_iter(uri.split('/')); + + let valid_components: Vec> = path + .components() + .filter(|c| matches!(c, Component::Normal(_))) + .collect(); + + PathBuf::from_iter(valid_components) +} + +fn retrieve_asset_path(root_path: &Path, file_path: &Path, index_folder: bool) -> Option { + let public_folder = root_path.join("public"); + let asset_path = if index_folder { + public_folder.join(file_path).join("index.html") + } else { + public_folder.join(file_path) + }; + + if asset_path.starts_with(public_folder) && asset_path.exists() && asset_path.is_file() { + Some(asset_path) + } else { + None + } +} + +pub async fn handle_assets(req: &HttpRequest) -> Result { + let root_path = &req + .app_data::>() + .expect("error fetching app data") + .root_path; + let uri_path = req.path(); + + let parsed_path = clean_up_path(uri_path); + + if let Some(file_path) = retrieve_asset_path(root_path, &parsed_path, false) { + NamedFile::open_async(file_path).await + } else if let Some(index_folder_path) = retrieve_asset_path(root_path, &parsed_path, true) { + NamedFile::open_async(index_folder_path).await + } else { + Err(Error::new(ErrorKind::NotFound, "The file is not present")) + } +} + +pub async fn handle_not_found(req: &HttpRequest) -> HttpResponse { + let root_path = &req + .app_data::>() + .expect("error fetching app data") + .root_path; + let public_404_path = root_path.join("public").join("404.html"); + + if let Ok(file) = NamedFile::open_async(public_404_path).await { + file.into_response(req) + } else { + HttpResponse::NotFound().body("") + } +} + +const CORS_HEADER: &str = "Access-Control-Allow-Origin"; + +pub async fn handle_worker(req: HttpRequest, body: Bytes) -> HttpResponse { + let app_data = req + .app_data::>() + .expect("error fetching app data"); + + let selected_route = app_data.routes.retrieve_best_route(req.path()); + let worker = if let Some(route) = selected_route { + if route.is_dynamic() { + if let Ok(existing_file) = handle_assets(&req).await { + return existing_file.into_response(&req); + } + } + + let workers = WORKERS + .read() + .expect("error locking worker lock for reading"); + + Some( + workers + .get(&route.worker) + .expect("unexpected missing worker") + .clone(), + ) + } else { + None + }; + + if worker.is_none() { + return handle_not_found(&req).await; + }; + let worker = worker.unwrap(); + + let body_str = String::from_utf8(body.to_vec()).unwrap_or_else(|_| String::from("")); + + let vars = &worker.config.vars; + let geolocation = worker.config.features.geo.geolocation(); + + let client = req.connection_info(); + let ip = client.realip_remote_addr(); + + let loop_back_ip = IpAddr::V4(Ipv4Addr::new(127,0,0,1)); + + let look_up_ip = match ip { + None => { loop_back_ip } + Some(result) => {match IpAddr::from_str(result) { + Ok(ip_addr) => {ip_addr} + Err(_) => {loop_back_ip} + }} + }; + + let geo_details = match geolocation.lookup(&look_up_ip) { + None => { GeolocationData::default() } + Some(details) => {details} + }; + + let handler_result = match worker.run(&req, &body_str, vars, geo_details).await { + Ok(output) => output, + Err(err) => WasmOutput::failed( + err, + worker.config.name.clone(), + selected_route.map(|route| route.path.clone()), + ), + }; + + let mut builder = + HttpResponse::build(StatusCode::from_u16(handler_result.status).unwrap_or(StatusCode::OK)); + builder.insert_header(("Content-Type", "text/html")); + + if let Some(origins) = app_data.cors_origins.as_ref() { + if !handler_result.headers.contains_key(CORS_HEADER) { + let header_value = origins.join(","); + builder.insert_header((CORS_HEADER, header_value)); + } + } + + for (key, val) in handler_result.headers.iter() { + builder.insert_header((key.replace('_', "-").as_str(), val.as_str())); + } + + match handler_result.body() { + Ok(res) => builder.body(res), + Err(_) => HttpResponse::ServiceUnavailable().body("There was an error running the worker"), + } +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/io.rs b/JS/wasm/crates/wasmjs-runtime/src/io.rs new file mode 100644 index 000000000..4058e9f90 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/io.rs @@ -0,0 +1,97 @@ +use actix_web::{ + http::{header::HeaderMap, StatusCode, Uri}, + HttpRequest, +}; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, fmt::Debug}; +use serde_json::{Map, Value as SerdeValue}; +use crate::geolocation::GeolocationData; + +#[derive(Serialize, Deserialize, Debug)] +pub struct WasmInput<'a> { + url: String, + method: &'a str, + headers: HashMap, + body: &'a str, + params: HashMap, + geo: Map +} + +impl<'a> WasmInput<'a> { + pub fn new(request: &'a HttpRequest, body: &'a str, geo_details: GeolocationData) -> Self { + let mut params = HashMap::new(); + + for (k, v) in request.match_info().iter() { + params.insert(k.to_string(), v.to_string()); + } + + let url = Self::build_url(request); + + Self { + url, + method: request.method().as_str(), + headers: Self::build_headers_hash(request.headers()), + body, + params, + geo: geo_details.data.clone() + } + } + + fn build_url(request: &HttpRequest) -> String { + match Uri::builder() + .scheme(request.connection_info().scheme()) + .authority(request.connection_info().host()) + .path_and_query(request.uri().to_string()) + .build() + { + Ok(uri) => uri.to_string(), + Err(_) => request.uri().to_string(), + } + } + + fn build_headers_hash(headers: &HeaderMap) -> HashMap { + let mut parsed_headers = HashMap::new(); + + for (key, value) in headers.iter() { + parsed_headers.insert( + String::from(key.as_str()), + String::from(value.to_str().unwrap()), + ); + } + + parsed_headers + } +} + +#[derive(Deserialize, Debug)] +pub struct WasmOutput { + pub headers: HashMap, + pub status: u16, + data: String, +} + +impl WasmOutput { + pub fn new(body: &str, headers: HashMap, status: u16) -> Self { + Self { + data: String::from(body), + headers, + status, + } + } + + pub fn failed(err: anyhow::Error, worker_name: Option, route: Option) -> Self { + eprintln!( + "Error running {:?} at route {:?}: {:?}", + worker_name, route, err + ); + Self::new( + "err", + HashMap::from([("content-type".to_string(), "text/html".to_string())]), + StatusCode::INTERNAL_SERVER_ERROR.as_u16(), + ) + } + + pub fn body(&self) -> anyhow::Result> { + Ok(self.data.as_bytes().into()) + } +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/main.rs b/JS/wasm/crates/wasmjs-runtime/src/main.rs new file mode 100644 index 000000000..15f5a3303 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/main.rs @@ -0,0 +1,98 @@ +use std::path::PathBuf; + +use clap::{Args, Parser, Subcommand}; + +use routes::Routes; +use server::run; + +use crate::server::{serve, ServeOptions}; + +mod bindings; +mod config; +mod error; +mod geolocation; +mod handlers; +mod io; +mod routes; +mod runtime; +mod server; +mod store; +mod wiggle_abi; +mod workers; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +#[command(propagate_version = true)] +#[command(args_conflicts_with_subcommands = true)] +pub struct Opts { + #[command(subcommand)] + pub command: Option, + #[command(flatten)] + pub args: ServeArgs, +} + +#[derive(Debug, Clone, Subcommand)] +pub enum Command { + Server(ServeArgs), + Run(ServeArgs), +} + +#[derive(Debug, Args, Clone)] +pub struct ServeArgs { + #[arg(value_parser, default_value = ".")] + path: PathBuf, + #[arg(short = 'C', long = "config")] + config_path: Option, + #[arg(long = "host", default_value = "0.0.0.0")] + hostname: String, + #[arg(short, long, default_value_t = 8080)] + port: u16, + #[arg(long)] + cors: Option>, +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + // let args = Args::parse(); + let opt = Opts::parse(); + let cmd = opt.command.unwrap_or(Command::Server(opt.args)); + match cmd { + Command::Server(args) => { + let routes = Routes::new(&args.path, &args); + for route in routes.routes.iter() { + println!( + "- http://{}:{}{}\n => {}", + &args.hostname, + args.port, + route.path, + route.handler.to_string_lossy() + ); + } + + let server = serve(ServeOptions { + root_path: args.path.clone(), + config_path: args.config_path, + base_routes: routes, + hostname: args.hostname, + port: args.port, + cors_origins: args.cors, + }) + .await + .unwrap(); + + server.await + } + Command::Run(args) => { + let routes = Routes::new(&args.path, &args); + let serve_options = ServeOptions { + root_path: args.path.clone(), + config_path: args.config_path, + base_routes: routes, + hostname: args.hostname, + port: args.port, + cors_origins: args.cors, + }; + run(serve_options).await.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) + } + } +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/routes.rs b/JS/wasm/crates/wasmjs-runtime/src/routes.rs new file mode 100644 index 000000000..c2718dd12 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/routes.rs @@ -0,0 +1,232 @@ +use lazy_static::lazy_static; +use regex::Regex; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::sync::RwLock; +use std::{collections::HashMap, ffi::OsStr, path::Component, sync::Arc}; +use wax::{Glob, WalkEntry}; +use crate::ServeArgs; + +use crate::store::STORE_FOLDER; +use crate::workers::Worker; + +pub struct Files<'t> { + root: PathBuf, + include_pattern: Glob<'t>, + ignore_patterns: Vec>, +} + +impl<'t> Files<'t> { + const DEFAULT_EXTENSIONS: [&'static str; 1] = ["js"]; + + pub fn new(root: &Path, file_extensions: Vec) -> Self { + Self { + root: root.to_path_buf(), + include_pattern: Self::build_include_pattern(file_extensions), + ignore_patterns: Self::build_ignore_patterns(vec![]), + } + } + + pub fn walk(&self) -> Vec { + self.include_pattern + .walk(&self.root) + .not(self.ignore_patterns.clone()) + .expect("Failed to walk the tree when processing files in the current directory") + .map(|e| e.unwrap()) + .collect() + } + + fn build_include_pattern(file_extensions: Vec) -> Glob<'t> { + let mut file_extensions = file_extensions; + for default_extension in Self::DEFAULT_EXTENSIONS { + file_extensions.push(default_extension.to_string()); + } + + let include_pattern = format!("**/*.{{{}}}", file_extensions.join(",")); + Glob::from_str(include_pattern.as_str()).unwrap() + } + + fn build_ignore_patterns(ignore_patterns: Vec) -> Vec> { + let default_ignore_patterns = vec![format!("**/{}/**", STORE_FOLDER)]; + + let mut result = default_ignore_patterns; + result.extend(ignore_patterns); + result + .iter() + .map(|s| Glob::from_str(s.as_str()).unwrap()) + .collect() + } +} + +#[derive(Clone, Default, Debug)] +pub struct Routes { + pub routes: Vec, +} + +impl Routes { + pub fn new(path: &Path, args: &ServeArgs) -> Self { + let mut routes = Vec::new(); + let runtime_extensions = vec![String::from("js")]; + + let files = Files::new(path, runtime_extensions); + + let mut route_paths: Vec = Vec::new(); + for entry in files.walk() { + route_paths.push(entry.into_path()); + } + + for route_path in route_paths { + routes.push(Route::new(path, route_path, args)); + } + + Self { routes } + } + + pub fn iter(&self) -> impl Iterator { + self.routes.iter() + } + + pub fn retrieve_best_route<'a>(&'a self, path: &str) -> Option<&'a Route> { + self.iter().find(|r| r.can_manage(path)) + } +} + +lazy_static! { + static ref PARAMETER_REGEX: Regex = + Regex::new(r"\[(?P\.{3})?(?P\w+)\]").unwrap(); + pub static ref WORKERS: RwLock = RwLock::new(WorkerSet::default()); +} + +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum RouteType { + Tail { number_of_segments: usize }, +} + +impl From<&String> for RouteType { + fn from(route_path: &String) -> Self { + let number_of_segments = route_path.chars().filter(|&c| c == '/').count(); + RouteType::Tail { number_of_segments } + } +} + +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum Segment { + Tail(String), +} + +impl From<&str> for Segment { + fn from(segment: &str) -> Self { + Segment::Tail(segment.to_owned()) + } +} + +#[derive(Clone, Debug)] +pub struct Route { + pub handler: PathBuf, + pub path: String, + pub route_type: RouteType, + pub segments: Vec, + pub worker: String, +} + +#[derive(Default)] +pub struct WorkerSet { + workers: HashMap>, +} + +impl WorkerSet { + pub fn get(&self, worker_id: &str) -> Option<&Arc> { + self.workers.get(worker_id) + } + + pub fn register(&mut self, worker_id: String, worker: Worker) { + self.workers.insert(worker_id, Arc::new(worker)); + } +} + +impl Route { + pub fn new(base_path: &Path, filepath: PathBuf, args: &ServeArgs) -> Self { + let worker = Worker::new(base_path, &filepath, args).unwrap(); + let worker_id = worker.id.clone(); + + WORKERS.write().unwrap().register(worker_id.clone(), worker); + let route_path = Self::retrieve_route(base_path, &filepath); + Self { + handler: filepath, + route_type: RouteType::from(&route_path), + segments: Self::get_segments(&route_path), + path: route_path, + worker: worker_id.clone(), + } + } + + fn retrieve_route(base_path: &Path, path: &Path) -> String { + let n_path = Self::normalize_path_to_url(path); + let n_base_path = Self::normalize_path_to_url(base_path); + + match n_path.strip_prefix(&n_base_path) { + Some(worker_path) => if worker_path.is_empty() { + "/" + } else { + worker_path + } + .into(), + None => String::from("/unknown"), + } + } + + fn normalize_path_to_url(path: &Path) -> String { + path.with_extension("") + .components() + .filter_map(|c| match c { + Component::Normal(os_str) if os_str != OsStr::new("index") => os_str + .to_str() + .map(|parsed_str| String::from("/") + parsed_str), + _ => None, + }) + .collect() + } + + fn get_segments(route_path: &str) -> Vec { + route_path.split('/').skip(1).map(Segment::from).collect() + } + + pub fn can_manage(&self, path: &str) -> bool { + let path_number_of_segments = path.chars().filter(|&c| c == '/').count(); + + match self.route_type { + RouteType::Tail { number_of_segments } + if number_of_segments > path_number_of_segments => + { + false + } + RouteType::Tail { .. } => true, + } + } + + pub fn actix_path(&self) -> String { + PARAMETER_REGEX + .replace_all(&self.path, |caps: ®ex::Captures| { + match (caps.name("ellipsis"), caps.name("segment")) { + (Some(_), Some(segment)) => format!("{{{}:.*}}", segment.as_str()), + (_, Some(segment)) => format!("{{{}}}", segment.as_str()), + _ => String::new(), + } + }) + .into() + } + + pub fn is_dynamic(&self) -> bool { + match self.route_type { + RouteType::Tail { .. } => true, + } + } +} + +impl Eq for Route {} + +impl PartialEq for Route { + fn eq(&self, other: &Self) -> bool { + self.path == other.path + } +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/runtime.rs b/JS/wasm/crates/wasmjs-runtime/src/runtime.rs new file mode 100644 index 000000000..ca48e2bc0 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/runtime.rs @@ -0,0 +1,70 @@ +use std::path::{Path, PathBuf}; + +use anyhow::Result; +use wasmtime_wasi::{ambient_authority, Dir, WasiCtxBuilder}; + +use crate::store::Store; + +static JS_ENGINE_WASM: &[u8] = include_bytes!("../../wasmjs-engine/wasmjs-engine.wasm"); + +pub struct JavaScriptRuntime { + path: PathBuf, + store: Store, +} + +impl JavaScriptRuntime { + pub fn new(project_root: &Path, path: PathBuf) -> Result { + let hash = Store::file_hash(&path)?; + let store = Store::create(project_root, &["js", &hash])?; + + Ok(Self { path, store }) + } +} + +impl Runtime for JavaScriptRuntime { + fn prepare(&self) -> Result<()> { + self.store.copy(&self.path, &["index.js"])?; + + Ok(()) + } + + fn prepare_wasi_ctx(&self, builder: &mut CtxBuilder) -> Result<()> { + match builder { + CtxBuilder::Preview1(ref mut builder) => { + builder.preopened_dir( + Dir::open_ambient_dir(&self.store.folder, ambient_authority())?, + "/src", + )?; + } + } + + Ok(()) + } + + fn module_bytes(&self) -> Result> { + Ok(JS_ENGINE_WASM.to_vec()) + } +} + +pub enum CtxBuilder { + Preview1(WasiCtxBuilder), +} + +pub trait Runtime { + fn prepare(&self) -> Result<()> { + Ok(()) + } + + fn prepare_wasi_ctx(&self, _builder: &mut CtxBuilder) -> Result<()> { + Ok(()) + } + + fn module_bytes(&self) -> Result>; +} + +pub fn init_runtime(project_root: &Path, path: &Path) -> Result> { + Ok(Box::new(JavaScriptRuntime::new( + project_root, + path.to_path_buf(), + )?)) +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/server.rs b/JS/wasm/crates/wasmjs-runtime/src/server.rs new file mode 100644 index 000000000..f24e78ea9 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/server.rs @@ -0,0 +1,81 @@ +use std::fmt::Debug; +use std::path::PathBuf; + +use actix_web::{ + App, + HttpServer, + middleware, web::{self, Data}, +}; +use actix_web::dev::Server; +use actix_web::HttpRequest; +use actix_web::web::Bytes; +use anyhow::{Ok, Result}; + +use crate::handlers::handle_worker; +use crate::routes::Routes; + +#[derive(Clone)] +pub struct ServeOptions { + pub root_path: PathBuf, + pub config_path: Option, + pub base_routes: Routes, + pub hostname: String, + pub port: u16, + pub cors_origins: Option>, +} + +#[derive(Default)] +pub struct AppData { + pub routes: Routes, + pub root_path: PathBuf, + pub cors_origins: Option>, +} + +impl From for AppData { + fn from(serve_options: ServeOptions) -> Self { + AppData { + routes: serve_options.base_routes, + root_path: serve_options.root_path.clone(), + cors_origins: serve_options.cors_origins, + } + } +} + +pub async fn serve(serve_options: ServeOptions) -> Result { + let (hostname, port) = (serve_options.hostname.clone(), serve_options.port); + let serve_options = serve_options.clone(); + + let server = HttpServer::new(move || { + let app_data: Data = + Data::new(>::try_into(serve_options.clone()).unwrap()); + + let mut app = App::new() + .wrap(middleware::Logger::default()) + .wrap(middleware::NormalizePath::trim()) + .app_data(Data::clone(&app_data)); + + for route in app_data.routes.iter() { + app = app.service(web::resource(route.actix_path()).to(handle_worker)); + } + + app + }) + .bind(format!("{}:{}", hostname, port))?; + + Ok(server.run()) +} + +pub async fn run(serve_options: ServeOptions) -> Result<()> { + + let app_data: Data = + Data::new(>::try_into(serve_options).unwrap()); + let request = actix_web::test::TestRequest::with_uri("/").app_data(app_data).to_http_request(); + let body: Bytes = Bytes::from(""); + let req = HttpRequest::from(request); + let res = handle_worker(req, body).await; + // print body of response + let res_body = res.body(); + + println!("{:?}", res_body); + Ok(()) +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/store.rs b/JS/wasm/crates/wasmjs-runtime/src/store.rs new file mode 100644 index 000000000..744576cf7 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/store.rs @@ -0,0 +1,45 @@ +use anyhow::Result; +use std::{ + fs, + path::{Path, PathBuf}, +}; + +pub const STORE_FOLDER: &str = ".wasmjs"; + +pub struct Store { + pub folder: PathBuf, +} + +impl Store { + pub fn create(project_root: &Path, folder: &[&str]) -> Result { + let folder = Self::build_root_path(project_root, folder); + + fs::create_dir_all(&folder)?; + + Ok(Self { folder }) + } + + pub fn copy(&self, source: &Path, dest: &[&str]) -> Result<()> { + let file_path = self.build_folder_path(dest); + fs::copy(source, file_path)?; + Ok(()) + } + + pub fn build_folder_path(&self, source: &[&str]) -> PathBuf { + source + .iter() + .fold(self.folder.clone(), |acc, comp| acc.join(comp)) + } + + pub fn file_hash(path: &Path) -> Result { + let content = fs::read(path)?; + + Ok(blake3::hash(&content).to_string()) + } + + fn build_root_path(root: &Path, source: &[&str]) -> PathBuf { + source + .iter() + .fold(root.join(STORE_FOLDER), |acc, comp| acc.join(comp)) + } +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/wiggle_abi.rs b/JS/wasm/crates/wasmjs-runtime/src/wiggle_abi.rs new file mode 100644 index 000000000..f0fbdddfb --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/wiggle_abi.rs @@ -0,0 +1,70 @@ +pub mod geo_impl; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::sync::Arc; +use wiggle::GuestPtr; + +use crate::bindings::arakoo_geo::ArakooGeo; +use crate::geolocation::Geolocation; +use crate::wiggle_abi::geo_impl::GeolocationError; +use crate::error::Error; + +pub struct Session { + geolocation: Arc, +} + +impl Session { + + pub fn geolocation_lookup(&self, addr: &IpAddr) -> Option { + self.geolocation.lookup(addr).map(|data| data.to_string()) + } +} + +impl ArakooGeo for Session { + fn lookup( + &mut self, + addr_octets: &GuestPtr, + addr_len: u32, + buf: &GuestPtr, + buf_len: u32, + nwritten_out: &GuestPtr, + ) -> Result<(), Error> { + let octets = addr_octets + .as_array(addr_len) + .iter() + .map(|v| v.unwrap().read().unwrap()) + .collect::>(); + + let ip_addr: IpAddr = match addr_len { + 4 => IpAddr::V4(Ipv4Addr::from( + TryInto::<[u8; 4]>::try_into(octets).unwrap(), + )), + 16 => IpAddr::V6(Ipv6Addr::from( + TryInto::<[u8; 16]>::try_into(octets).unwrap(), + )), + _ => return Err(Error::InvalidArgument), + }; + + let result = self + .geolocation_lookup(&ip_addr) + .ok_or_else(|| GeolocationError::NoGeolocationData(ip_addr.to_string()))?; + + if result.len() > buf_len as usize { + return Err(Error::BufferLengthError { + buf: "geolocation_lookup", + len: "geolocation_lookup_max_len", + }); + } + + let result_len = + u32::try_from(result.len()).expect("smaller than value_max_len means it must fit"); + + let mut buf_ptr = buf + .as_array(result_len) + .as_slice_mut()? + .ok_or(Error::SharedMemory)?; + buf_ptr.copy_from_slice(result.as_bytes()); + nwritten_out.write(result_len)?; + Ok(()) + } +} + diff --git a/JS/wasm/crates/wasmjs-runtime/src/wiggle_abi/geo_impl.rs b/JS/wasm/crates/wasmjs-runtime/src/wiggle_abi/geo_impl.rs new file mode 100644 index 000000000..58fefa2ae --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/wiggle_abi/geo_impl.rs @@ -0,0 +1,22 @@ +//! edge_geo` hostcall implementations. + + + +use crate::bindings::types::ArakooStatus; + +#[derive(Debug, thiserror::Error)] +pub enum GeolocationError { + /// Geolocation data for given address not found. + #[error("No geolocation data: {0}")] + NoGeolocationData(String), +} + +impl GeolocationError { + /// Convert to an error code representation suitable for passing across the ABI boundary. + pub fn to_aradoo_status(&self) -> ArakooStatus { + use GeolocationError::*; + match self { + NoGeolocationData(_) => ArakooStatus::None, + } + } +} diff --git a/JS/wasm/crates/wasmjs-runtime/src/workers.rs b/JS/wasm/crates/wasmjs-runtime/src/workers.rs new file mode 100644 index 000000000..fd8fd7b19 --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/src/workers.rs @@ -0,0 +1,185 @@ +use crate::bindings::HttpBindings; +use crate::config::{ArakooConfig, Config}; +use crate::io::{WasmInput, WasmOutput}; +use crate::runtime::{init_runtime, CtxBuilder, Runtime}; +use crate::bindings::http; +use actix_web::HttpRequest; +use sha256::digest as sha256_digest; +use std::path::PathBuf; +use std::{collections::HashMap, path::Path}; +use wasmtime::Linker; +use wasmtime::{Config as WasmtimeConfig, Engine, Module, Store}; +use wasmtime_wasi::{ambient_authority, Dir, WasiCtxBuilder}; + +use anyhow::Result; +use std::io::Cursor; +use wasi_common::pipe::{ReadPipe, WritePipe}; +use crate::ServeArgs; +use crate::geolocation::GeolocationData; + +pub struct Stdio { + pub stdin: Vec, + pub stdout: WritePipe>>, +} + +impl Stdio { + pub fn new(input: &str) -> Self { + Self { + stdin: Vec::from(input), + stdout: WritePipe::new_in_memory(), + } + } + + pub fn configure_wasi_ctx(&self, mut builder: CtxBuilder) -> CtxBuilder { + match builder { + CtxBuilder::Preview1(ref mut wasi_builder) => { + wasi_builder + .stdin(Box::new(ReadPipe::from(self.stdin.clone()).clone())) + .stdout(Box::new(self.stdout.clone())) + .inherit_stderr(); + } + } + builder + } +} + +pub struct Worker { + pub id: String, + engine: Engine, + runtime: Box, + module: Module, + pub config: Config, + path: PathBuf, +} + +#[derive(Default)] +struct Host { + pub wasi_preview1_ctx: Option, + pub http: Option, +} + +impl Worker { + pub fn new(project_root: &Path, path: &Path, args: &ServeArgs) -> Result { + let id = sha256_digest(project_root.join(path).to_string_lossy().as_bytes()); + + let mut config=Config::default(); + match &args.config_path { + Some(path) => config.features.geo = ArakooConfig::from_file(&path)?, + _ => {}, + } + config.vars = std::env::vars().collect(); + + let engine = Engine::new(WasmtimeConfig::default().async_support(true))?; + let runtime = init_runtime(project_root, path)?; + let bytes = runtime.module_bytes()?; + + let module = if wasmparser::Parser::is_core_wasm(&bytes) { + Ok(Module::from_binary(&engine, &bytes)?) + } else { + Err("Invalid module".to_string()) + } + .map_err(|e| anyhow::anyhow!(e))?; + + runtime.prepare()?; + + Ok(Self { + id, + engine, + runtime, + module, + config, + path: path.to_path_buf(), + }) + } + + pub fn prepare_wasi_context( + &self, + environment_variables: &[(String, String)], + wasi_builder: &mut CtxBuilder, + ) -> Result<()> { + match wasi_builder { + CtxBuilder::Preview1(wasi_builder) => { + wasi_builder.envs(environment_variables)?; + + if let Some(folders) = self.config.folders.as_ref() { + for folder in folders { + if let Some(base) = &self.path.parent() { + let dir = Dir::open_ambient_dir( + base.join(&folder.from), + ambient_authority(), + )?; + wasi_builder.preopened_dir(dir, &folder.to)?; + } else { + panic!("Failed to initialize") + } + } + } + } + } + + Ok(()) + } + + pub async fn run( + &self, + request: &HttpRequest, + body: &str, + vars: &HashMap, + geo_details: GeolocationData, + ) -> Result { + let input = serde_json::to_string(&WasmInput::new(request, body, geo_details)).unwrap(); + + let mut linker = Linker::new(&self.engine); + + wasmtime_wasi::add_to_linker(&mut linker, |host: &mut Host| { + host.wasi_preview1_ctx.as_mut().unwrap() + })?; + + http::add_to_linker(&mut linker, |host: &mut Host| host.http.as_mut().unwrap())?; + + let environment_variables: Vec<(String, String)> = + vars.iter().map(|(k, v)| (k.clone(), v.clone())).collect(); + + let mut wasi_builder = CtxBuilder::Preview1(WasiCtxBuilder::new()); + + self.prepare_wasi_context(&environment_variables, &mut wasi_builder)?; + + let stdio = Stdio::new(&input); + let mut wasi_builder = stdio.configure_wasi_ctx(wasi_builder); + + self.runtime.prepare_wasi_ctx(&mut wasi_builder)?; + + let host = match wasi_builder { + CtxBuilder::Preview1(mut wasi_builder) => Host { + wasi_preview1_ctx: Some(wasi_builder.build()), + http: Some(HttpBindings { + http_config: self.config.features.http_requests.clone(), + }), + ..Host::default() + }, + }; + + let contents = { + let mut store = Store::new(&self.engine, host); + linker.module_async(&mut store, "", &self.module).await?; + + linker + .get_default(&mut store, "")? + .typed::<(), ()>(&store)? + .call_async(&mut store, ()) + .await?; + + drop(store); + + stdio + .stdout + .try_into_inner() + .unwrap_or_default() + .into_inner() + }; + + let output: WasmOutput = serde_json::from_slice(&contents)?; + + Ok(output) + } +} diff --git a/JS/wasm/examples/ec-wasmjs-hono/.gitignore b/JS/wasm/examples/ec-wasmjs-hono/.gitignore new file mode 100644 index 000000000..a198a78d5 --- /dev/null +++ b/JS/wasm/examples/ec-wasmjs-hono/.gitignore @@ -0,0 +1,2 @@ +bin/ +node_modules/ \ No newline at end of file diff --git a/JS/wasm/examples/ec-wasmjs-hono/Dockerfile b/JS/wasm/examples/ec-wasmjs-hono/Dockerfile new file mode 100644 index 000000000..c1eacd0aa --- /dev/null +++ b/JS/wasm/examples/ec-wasmjs-hono/Dockerfile @@ -0,0 +1,18 @@ +FROM node:20 as builder + +WORKDIR /app + +COPY package*.json ./ + +RUN npm install + +COPY build.js ./ +COPY src ./src + +RUN npm run build + +FROM tanjim/wasmjs:latest + +COPY --from=builder --chown=cnb:cnb --chmod=0777 /app/bin /app + +ENTRYPOINT [ "wasmjs-runtime", "/app" ] diff --git a/JS/wasm/examples/ec-wasmjs-hono/build.js b/JS/wasm/examples/ec-wasmjs-hono/build.js new file mode 100644 index 000000000..c11ff5890 --- /dev/null +++ b/JS/wasm/examples/ec-wasmjs-hono/build.js @@ -0,0 +1,14 @@ +import { build } from 'esbuild' + +build({ + entryPoints: ['src/index.js'], + bundle: true, + minify: true, + outfile: 'bin/[...app].js', + format: "esm", + target: "esnext", + platform: "node" +}).catch((error) => { + console.error(error) + process.exit(1) +}) \ No newline at end of file diff --git a/JS/wasm/examples/ec-wasmjs-hono/package-lock.json b/JS/wasm/examples/ec-wasmjs-hono/package-lock.json new file mode 100644 index 000000000..cf6c18ec9 --- /dev/null +++ b/JS/wasm/examples/ec-wasmjs-hono/package-lock.json @@ -0,0 +1,554 @@ +{ + "name": "hono", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hono", + "dependencies": { + "@hono/node-server": "^1.3.1", + "axios": "^1.6.2", + "crypto": "^1.0.1", + "http": "^0.0.1-security", + "stream": "^0.0.2" + }, + "devDependencies": { + "@planetscale/database": "^1.4.0", + "esbuild": "^0.19", + "hono": "^3.9" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", + "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", + "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", + "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", + "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", + "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", + "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", + "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", + "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", + "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", + "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", + "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", + "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", + "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", + "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", + "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", + "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", + "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", + "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", + "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", + "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", + "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", + "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@hono/node-server": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.3.1.tgz", + "integrity": "sha512-eQBCDbH1Vv/TiYXNP8aGfJTuXi9xGhEd/EZg9u6dhr7zC5/WKKztcBmbrOTtixVBvvV6bfcay6KEginwiqHyXg==", + "engines": { + "node": ">=18.14.1" + } + }, + "node_modules/@planetscale/database": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@planetscale/database/-/database-1.11.0.tgz", + "integrity": "sha512-aWbU+D/IRHoDE9975y+Q4c+EwwAWxCPwFId+N1AhQVFXzbeJMkj6KN2iQtoi03elcLMRdfT+V3i9Z4WRw+/oIA==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/emitter-component": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", + "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esbuild": { + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", + "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.5", + "@esbuild/android-arm64": "0.19.5", + "@esbuild/android-x64": "0.19.5", + "@esbuild/darwin-arm64": "0.19.5", + "@esbuild/darwin-x64": "0.19.5", + "@esbuild/freebsd-arm64": "0.19.5", + "@esbuild/freebsd-x64": "0.19.5", + "@esbuild/linux-arm": "0.19.5", + "@esbuild/linux-arm64": "0.19.5", + "@esbuild/linux-ia32": "0.19.5", + "@esbuild/linux-loong64": "0.19.5", + "@esbuild/linux-mips64el": "0.19.5", + "@esbuild/linux-ppc64": "0.19.5", + "@esbuild/linux-riscv64": "0.19.5", + "@esbuild/linux-s390x": "0.19.5", + "@esbuild/linux-x64": "0.19.5", + "@esbuild/netbsd-x64": "0.19.5", + "@esbuild/openbsd-x64": "0.19.5", + "@esbuild/sunos-x64": "0.19.5", + "@esbuild/win32-arm64": "0.19.5", + "@esbuild/win32-ia32": "0.19.5", + "@esbuild/win32-x64": "0.19.5" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hono": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/hono/-/hono-3.9.0.tgz", + "integrity": "sha512-MSGmerYD69jwDkBa631lY7y3xtEwbKFg2huFZ0TruzCdfJlmPd/rFzyvmMheePKsiA7CZp/KqL3mflBOzEmjLA==", + "dev": true, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/http": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz", + "integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/stream": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", + "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", + "dependencies": { + "emitter-component": "^1.1.1" + } + } + } +} diff --git a/JS/wasm/examples/ec-wasmjs-hono/package.json b/JS/wasm/examples/ec-wasmjs-hono/package.json new file mode 100644 index 000000000..49e60ff54 --- /dev/null +++ b/JS/wasm/examples/ec-wasmjs-hono/package.json @@ -0,0 +1,20 @@ +{ + "name": "hono", + "type": "module", + "main": "bin/[...app].js", + "scripts": { + "build": "node ./build.js" + }, + "devDependencies": { + "@planetscale/database": "^1.4.0", + "esbuild": "^0.19", + "hono": "^3.9" + }, + "dependencies": { + "@hono/node-server": "^1.3.1", + "axios": "^1.6.2", + "crypto": "^1.0.1", + "http": "^0.0.1-security", + "stream": "^0.0.2" + } +} diff --git a/JS/wasm/examples/ec-wasmjs-hono/src/index.js b/JS/wasm/examples/ec-wasmjs-hono/src/index.js new file mode 100644 index 000000000..9b747676c --- /dev/null +++ b/JS/wasm/examples/ec-wasmjs-hono/src/index.js @@ -0,0 +1,42 @@ +import { Hono } from 'hono' +import { connect } from '@planetscale/database' + +const app = new Hono(); + +app.get('/', (c) => { + const geo = c.req.raw.geo + return c.text( + `Your from ${geo.city}, ${geo.country_name}!` + ) +}) + +app.get('/hello/:name', async (c) => { + const name = c.req.param('name'); + return c.text(`Async Hello ${name}!`) +}); + +app.get('/env/:key', async (c) => { + const key = c.req.param('key'); + return c.text(env[key]) +}) + +const config = { + host: env['PLANETSCALE_HOST'], + username: env['PLANETSCALE_USERNAME'], + password: env['PLANETSCALE_PASSWORD'], +} +const conn = connect(config) + +app.get('/db', async (c) => { + const result = await conn.execute('SHOW TABLES') + + return c.json(result) +}); + +app.notFound((c) => { + return c.text('404 not found', 404) +}) + +export default app; + +