diff --git a/.cargo/config.toml b/.cargo/config.toml index aceda7e..75199fd 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,22 +2,13 @@ target = "wasm32-unknown-emscripten" target-dir = "target/workers" +# worker-build --emscripten --tokio supplies these (and the link settings) +# through RUSTFLAGS when building; they are repeated here so `cargo check` +# sees the same cfgs. [target.wasm32-unknown-emscripten] rustflags = [ - # Static linking and unwind semantics are required by the hosted runtime. "-Crelocation-model=static", + "-Cllvm-args=-wasm-use-legacy-eh=false", "--cfg=tokio_unstable", - "-Clink-arg=-sWASM_BINDGEN=auto", - "-Clink-arg=-sEXPORT_ES6", - # Generated host glue uses Workers' Node compatibility APIs. - "-Clink-arg=-sENVIRONMENT=node", - "-Clink-arg=-sNODERAWSOCKETS", - "-Clink-arg=-sNODERAWFS", - "-Clink-arg=-sJSPI", - "-Clink-arg=-sASSERTIONS=0", - "-Clink-arg=-sSTACK_SIZE=8MB", - "-Clink-arg=-sALLOW_MEMORY_GROWTH=1", - # Each Durable Object creates its own wasm instance and mounted filesystem. - "-Clink-arg=-sMODULARIZE=1", - "-Clink-arg=-sEXPORT_NAME=createPumpkinModule", + "--cfg=worker_tokio=\"event_loop\"", ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3bb185c..8640b26 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,8 +15,8 @@ concurrency: jobs: smoke: - runs-on: macos-14 - timeout-minutes: 75 + runs-on: ubuntu-latest + timeout-minutes: 90 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -25,14 +25,15 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.13' - - name: Install Emscripten backend - run: brew install emscripten + - run: sudo apt-get install -y ninja-build - uses: actions/cache@v4 with: path: | ~/.cargo/registry ~/.cargo/git - key: minecraft-cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml') }} + .work/emsdk + .work/binaryen/build + key: minecraft-toolchain-${{ runner.os }}-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml', 'scripts/setup.sh') }} - name: Setup pinned sources and toolchain run: bash scripts/setup.sh - name: Install Worker dependencies diff --git a/.gitignore b/.gitignore index 4a53fb4..cca1d50 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /.work/ /target/ **/target/ +/build/ node_modules/ .wrangler/ /worker/env.d.ts diff --git a/AGENTS.md b/AGENTS.md index a5418ab..21bbc74 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,25 +5,26 @@ Read [README.md](README.md), [architecture](docs/architecture.md), and ## Project layout -- `src/`: Pumpkin entry points and configuration. The hosted Tokio bridge lives in workers-rs. -- `worker/`: TCP ingress, Durable Object lifecycle, and SQLite filesystem. -- `tests/`: protocol clients, integration tests, and isolated filesystem fixtures. +- `src/`: the Worker: TCP ingress, the Durable Object, the SQLite filesystem + mount (`js/mount.js`), Pumpkin configuration, and the Emscripten JS library + (`workerd.js`). +- `tests/`: protocol clients and the integration test. - `scripts/setup.sh`: provision pinned sources and build the toolchain. - `scripts/{build,serve,test}.sh`: build, run, and validate the Workers server. -Use Node 24+ (26 recommended) and the pinned Rust nightly. Run `npm test` after +Use Node 24+ (26 recommended) and the pinned Rust toolchain. Run `npm test` after build/runtime changes; it must print `PUMPKIN-DO-SQLITE-RESTART-OK` after verifying -player and chunk restoration. Keep test fixtures out of the production bundle. +player and chunk restoration. Scripts bind to loopback and fail if their ports are occupied. Do not kill another process to free a port. ## Reproducibility and data -Changes to patched checkouts (Pumpkin, wasm-bindgen, workers-rs, wasm-streams) must -be reflected in `patches/`. Keep unpatched checkouts unmodified; update pins for +Changes to the patched Pumpkin checkout must be reflected in `patches/`. Keep unpatched checkouts unmodified; update pins for upstream changes. Preserve the unified ticker and cooperative scheduler unless -the task requires a runtime change. Do not reintroduce old Tokio/libc networking -patches. +the task requires a runtime change. Keep the event-loop model: exports +return promises and nothing blocks or suspends; do not reintroduce JSPI, old +Tokio/libc networking patches, or a JS-side driver. Worlds under `.data/` are user data; do not remove or copy them into commits. Playable databases live in `.data/workers/server/`; tests use `.data/probes/`. diff --git a/Cargo.lock b/Cargo.lock index 30dfaff..58057bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.44" +version = "0.4.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "515a1f282e33d55983c499d7e9e87082e81cbc32974825bf9032f928392d5844" +checksum = "ef217a77a86a6e3dab9a5b3c81dc445b603fe743a90c1cb10a2f2144628d8cfa" dependencies = [ "compression-codecs", "compression-core", @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.91" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", @@ -107,9 +107,9 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bitflags" -version = "2.13.1" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" [[package]] name = "block-buffer" @@ -164,9 +164,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.3.0" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" +checksum = "a3eb0f42d6c360dc3f8a821f6bf2fdea7f72bfd36b3076eb0e6d1e9e0752fff4" dependencies = [ "find-msvc-tools", "shlex", @@ -195,9 +195,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chacha20" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", "cpufeatures", @@ -261,9 +261,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.39" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe67f2944eef52fc7b106b8c9450d243a88701a0c065f7f57235e76abaed7df" +checksum = "257c7085cbb71be72d8fb97edff08b03d86d5d9f2222b9cc34a6bec87093bc10" dependencies = [ "compression-core", "flate2", @@ -305,9 +305,9 @@ checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" [[package]] name = "cpufeatures" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" dependencies = [ "libc", ] @@ -324,9 +324,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" dependencies = [ "cfg-if", ] @@ -339,9 +339,9 @@ checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crossbeam" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +checksum = "e71406cd8807725f7ac2f999a4cdd32e98f829fdf65f528343cebf945e41df1e" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -352,18 +352,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.16" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" +checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" +checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -371,27 +371,27 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.20" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" +checksum = "03e8bd762f7479489c70ed6c768ddca99d7296857de437a68dcb2a94365b3fae" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" [[package]] name = "crypto-bigint" @@ -493,9 +493,9 @@ dependencies = [ [[package]] name = "der" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" +checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a" dependencies = [ "const-oid", "pem-rfc7468", @@ -532,13 +532,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.5", ] [[package]] @@ -579,9 +579,9 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "elliptic-curve" @@ -654,9 +654,9 @@ checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" [[package]] name = "flate2" @@ -686,9 +686,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -701,9 +701,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -711,15 +711,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -728,38 +728,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.5", ] [[package]] name = "futures-sink" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -807,9 +807,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.15" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" dependencies = [ "atomic-waker", "bytes", @@ -882,9 +882,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -902,9 +902,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", @@ -921,9 +921,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hybrid-array" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" dependencies = [ "subtle", "typenum", @@ -932,9 +932,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" dependencies = [ "atomic-waker", "bytes", @@ -991,9 +991,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", @@ -1005,9 +1005,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -1018,9 +1018,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1032,16 +1032,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -1052,15 +1053,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -1107,9 +1108,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", "hashbrown 0.17.1", @@ -1126,9 +1127,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.12.1" +version = "2.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" [[package]] name = "itertools" @@ -1196,7 +1197,7 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.105" dependencies = [ "cfg-if", "futures-util", @@ -1212,12 +1213,13 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.189" +source = "git+https://github.com/rust-lang/libc?branch=libc-0.2#31503352774deadf65cc53257b59ee4f3299f644" [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "lock_api" @@ -1230,9 +1232,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "lz4-java-wrc" @@ -1309,8 +1311,8 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.2" -source = "git+https://github.com/guybedford/mio?rev=a62c9e46833fc255c9217ab9aa362c6221ed4401#a62c9e46833fc255c9217ab9aa362c6221ed4401" +version = "1.2.3" +source = "git+https://github.com/guybedford/mio?branch=emscripten#0788dbb67883cb6191503af999471def54e3fb77" dependencies = [ "libc", "wasi", @@ -1569,9 +1571,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -1845,6 +1847,8 @@ dependencies = [ "rayon", "tokio", "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", "worker", ] @@ -1950,9 +1954,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -1967,11 +1971,11 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "reqwest" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "bytes", "futures-core", "h2", @@ -2014,7 +2018,8 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.14" +version = "0.17.16000" +source = "git+https://github.com/guybedford/ring?branch=emscripten#a1930ce8d9b8006e5060eda14cacf7cf6f7401c1" dependencies = [ "cc", "cfg-if", @@ -2069,9 +2074,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.42" +version = "0.23.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" dependencies = [ "log", "once_cell", @@ -2096,9 +2101,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] @@ -2132,9 +2137,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -2414,14 +2419,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.2" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" [[package]] name = "socket2" version = "0.6.5" -source = "git+https://github.com/rust-lang/socket2?rev=239dd83a4ced08e514d2c38942aab99791119f0d#239dd83a4ced08e514d2c38942aab99791119f0d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys", @@ -2535,18 +2541,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", @@ -2564,9 +2570,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.54" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "libc", @@ -2596,9 +2602,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -2606,7 +2612,7 @@ dependencies = [ [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" dependencies = [ "bytes", "libc", @@ -2619,11 +2625,11 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.5", ] [[package]] @@ -2653,9 +2659,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.5+spec-1.1.0" +version = "1.1.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12c0ba9680044b4ce98d391a62094047eada0d64860b80166c39f4a6b5640785" +checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a" dependencies = [ "serde_core", "serde_spanned", @@ -2848,9 +2854,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.26.0" +version = "1.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" +checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce" dependencies = [ "getrandom", "js-sys", @@ -2892,7 +2898,7 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.128" dependencies = [ "cfg-if", "once_cell", @@ -2903,15 +2909,16 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.78" dependencies = [ "js-sys", + "tokio", "wasm-bindgen", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.128" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2919,18 +2926,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.128" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.5", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.128" dependencies = [ "unicode-ident", ] @@ -2938,6 +2945,7 @@ dependencies = [ [[package]] name = "wasm-streams" version = "0.6.0" +source = "git+https://github.com/guybedford/wasm-streams?branch=rlib-only#073b6497be60d7e91fc5b1f23f24c825296b4c88" dependencies = [ "futures-util", "js-sys", @@ -2948,7 +2956,7 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.105" dependencies = [ "js-sys", "wasm-bindgen", @@ -3007,7 +3015,7 @@ dependencies = [ [[package]] name = "worker" -version = "0.8.5" +version = "0.8.6" dependencies = [ "async-trait", "bytes", @@ -3036,7 +3044,7 @@ dependencies = [ [[package]] name = "worker-macros" -version = "0.8.5" +version = "0.8.6" dependencies = [ "async-trait", "proc-macro2", @@ -3050,7 +3058,7 @@ dependencies = [ [[package]] name = "worker-sys" -version = "0.8.5" +version = "0.8.6" dependencies = [ "cfg-if", "js-sys", @@ -3060,9 +3068,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "xxhash-rust" @@ -3122,9 +3130,9 @@ checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -3133,9 +3141,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -3144,13 +3152,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.5", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ce459aa..ca12b30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,45 +12,48 @@ autobins = false exclude = [".work"] resolver = "2" -[features] -test-fixtures = [] - -[[bin]] -name = "filesystem-probe" -path = "tests/fixtures/filesystem.rs" -required-features = ["test-fixtures"] - [[bin]] name = "pumpkin-do" path = "src/main.rs" [dependencies] -wasm-bindgen = { path = ".work/wasm-bindgen" } -js-sys = { path = ".work/wasm-bindgen/crates/js-sys" } +worker = { path = ".work/workers-rs/worker" } +wasm-bindgen = "0.2.128" +wasm-bindgen-futures = { version = "0.4.78", features = ["tokio"] } +js-sys = "0.3.105" +web-sys = { version = "0.3.105", features = ["AbortController", "AbortSignal", "ReadableStream", "StreamPipeOptions", "WritableStream", "console"] } tokio = { version = "1", default-features = false, features = ["rt", "macros", "sync", "time", "net", "io-util"] } pumpkin = { path = ".work/pumpkin/crates/pumpkin", default-features = false, features = ["single-threaded"] } pumpkin-config = { path = ".work/pumpkin/crates/pumpkin-config", default-features = false } rayon = "1.12" -worker = { path = ".work/workers-rs/worker", default-features = false } +# wasm32-unknown-emscripten support pending upstream releases. [patch.crates-io] -js-sys = { path = ".work/wasm-bindgen/crates/js-sys" } -wasm-bindgen = { path = ".work/wasm-bindgen" } -wasm-bindgen-futures = { path = ".work/wasm-bindgen/crates/futures" } -wasm-bindgen-macro-support = { path = ".work/wasm-bindgen/crates/macro-support" } -wasm-bindgen-shared = { path = ".work/wasm-bindgen/crates/shared" } -web-sys = { path = ".work/wasm-bindgen/crates/web-sys" } -wasm-streams = { path = ".work/wasm-streams" } -libc = { path = ".work/libc" } -mio = { git = "https://github.com/guybedford/mio", rev = "a62c9e46833fc255c9217ab9aa362c6221ed4401" } -ring = { path = ".work/ring" } -socket2 = { git = "https://github.com/rust-lang/socket2", rev = "239dd83a4ced08e514d2c38942aab99791119f0d" } +# LocalEventLoop: the host event loop drives Tokio (tokio-rs/tokio#8484). tokio = { path = ".work/tokio/tokio" } tokio-macros = { path = ".work/tokio/tokio-macros" } - +# tokio-rs/mio#1969; its emscripten epoll bindings come from libc's unreleased libc-0.2 branch. +mio = { git = "https://github.com/guybedford/mio", branch = "emscripten" } +libc = { git = "https://github.com/rust-lang/libc", branch = "libc-0.2" } +# getrandom-backed SystemRandom on emscripten. +ring = { git = "https://github.com/guybedford/ring", branch = "emscripten" } +# rlib-only: cargo would otherwise also link its cdylib, which emcc cannot produce. +wasm-streams = { git = "https://github.com/guybedford/wasm-streams", branch = "rlib-only" } +# `#[wasm_bindgen(tokio)]` exports on the event loop (wasm-bindgen/wasm-bindgen#5334), +# from the workers-rs checkout so the worker crate shares one wasm-bindgen. +wasm-bindgen = { path = ".work/workers-rs/wasm-bindgen" } +wasm-bindgen-macro-support = { path = ".work/workers-rs/wasm-bindgen/crates/macro-support" } +wasm-bindgen-shared = { path = ".work/workers-rs/wasm-bindgen/crates/shared" } +wasm-bindgen-futures = { path = ".work/workers-rs/wasm-bindgen/crates/futures" } +js-sys = { path = ".work/workers-rs/wasm-bindgen/crates/js-sys" } +web-sys = { path = ".work/workers-rs/wasm-bindgen/crates/web-sys" } + +# mio's own libc source, unified with the crates.io users above. [patch."https://github.com/guybedford/libc"] -libc = { path = ".work/libc" } +libc = { git = "https://github.com/rust-lang/libc", branch = "libc-0.2" } +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(worker_tokio, values("event_loop", "jspi"))'] } [profile.release] opt-level = "s" diff --git a/README.md b/README.md index 3fc6595..5bccc96 100644 --- a/README.md +++ b/README.md @@ -13,20 +13,20 @@ Minecraft → Workers TCP ingress → MinecraftWorld → Pumpkin + SQLite ## Try it -The supported build host is macOS with Homebrew. Install Git, -[rustup](https://rustup.rs/), Python 3.11+, and Node 24+ (26 recommended). -From a checkout of this repository: +Linux and macOS build hosts are supported. Install Git, +[rustup](https://rustup.rs/), Python 3.11+, and Node 24+ (26 recommended). From a checkout of this repository: ```sh -brew install emscripten npm ci bash scripts/setup.sh npm run dev ``` -Setup installs the pinned Rust toolchain, fetches dependency sources, and builds -the matching wasm-bindgen CLI. `npm run dev` compiles Pumpkin and starts Wrangler. -The first build takes several minutes; later builds use Cargo's cache. +Setup installs the pinned Rust toolchain, fetches dependency sources and the +Emscripten toolchain under `.work/`, and builds the +worker-build and wasm-bindgen CLIs. `npm run dev` compiles Pumpkin with +`worker-build --emscripten --tokio` and starts Wrangler. The first build takes +several minutes; later builds use Cargo's cache. Connect **Minecraft Java 26.2** to **`localhost:25565`**. Status is available at **http://localhost:8787/**. @@ -35,18 +35,18 @@ The example uses offline mode, with encryption and compression disabled. Local listeners bind to loopback. `WORLD_NAME` in [wrangler.jsonc](wrangler.jsonc) selects the Durable Object; server settings are in [src/config.rs](src/config.rs). -World data lives under `.data/workers/server/`. After the last connection closes, -the server saves and stops. The next connection restores the same world. Wait for -status to report `phase: "idle"` and a checkpoint timestamp before stopping -Wrangler; changes held in memory can be lost if the process is terminated early. +World data lives under `.data/workers/server/`, written through to the Durable +Object's SQLite storage as Pumpkin saves. After the last connection closes, the +server saves and stops; the next connection starts it on the same world. Wait for +status to report `phase: "idle"` before stopping Wrangler so the final save +completes. ## Build and contribute ```sh -npm run build # Compile the server without starting Wrangler +npm run build # Compile the Worker without starting Wrangler npm test # Two-player gameplay, a persistent block edit, and restart npm run test:scheduler -npm run typecheck ``` The tests use separate data under `.data/probes/`. They verify that two clients @@ -72,9 +72,10 @@ Provision the public TCP endpoint separately and route it to this Worker's ## Credits and license Built on [Pumpkin](https://github.com/Pumpkin-MC/Pumpkin), -[Guy Bedford's Rust/Emscripten work](https://github.com/guybedford), -[workers-rs](https://github.com/cloudflare/workers-rs), Thomas Rubini's TCP ingress -work, and [worker-fs-mount](https://github.com/danlapid/worker-fs-mount). +[workers-rs](https://github.com/cloudflare/workers-rs), +[Tokio](https://github.com/tokio-rs/tokio), [Emscripten](https://emscripten.org/), +[wasm-bindgen](https://github.com/wasm-bindgen/wasm-bindgen), and +[Guy Bedford's Rust/Emscripten work](https://github.com/guybedford). Licensed under [GPL-3.0-only](LICENSE), consistent with Pumpkin. The [original MIT notice](LICENSES/rust-workers-minecraft-MIT.txt) is retained for diff --git a/build.rs b/build.rs index 413100c..a6139d6 100644 --- a/build.rs +++ b/build.rs @@ -1,17 +1,22 @@ use std::{env, path::PathBuf}; +// worker-build supplies the common emscripten link settings; these are the +// application's own. fn main() { - println!("cargo::rerun-if-changed=worker/fs-library.js"); + println!("cargo::rerun-if-changed=src/workerd.js"); if env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("emscripten") { return; } - // Keep growth headroom bounded after generation's temporary allocations. - println!("cargo::rustc-link-arg-bins=-sMEMORY_GROWTH_LINEAR_STEP=2097152"); - let library = - PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("worker/fs-library.js"); - println!( - "cargo::rustc-link-arg-bins=-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[\"$workerFilesystem\"]" - ); - println!("cargo::rustc-link-arg-bins=--js-library"); + for arg in [ + "-sNODERAWFS", + "-sSTACK_SIZE=8MB", + // Keep growth headroom bounded after generation's temporary allocations. + "-sMEMORY_GROWTH_LINEAR_STEP=2097152", + "-sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[\"$workerdFs\"]", + "--js-library", + ] { + println!("cargo::rustc-link-arg-bins={arg}"); + } + let library = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("src/workerd.js"); println!("cargo::rustc-link-arg-bins={}", library.display()); } diff --git a/docs/architecture.md b/docs/architecture.md index e41d442..898af32 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -2,18 +2,44 @@ ## Runtime and connections -The Worker forwards TCP streams to a named Durable Object using the platform's -stream-piping API. The object lazily creates an Emscripten module and passes a -workers-rs `Socket` to Pumpkin's injected connection entry point. - -Each object owns separate wasm memory, Rust statics, filesystem descriptors, and -one hosted Tokio runtime, supplied by workers-rs. Pumpkin's `single-threaded` feature selects cooperative -inline chunk generation and the async ticker. The same scheduler loop serves -native builds, which dispatch generation onto Rayon. Save batches await queue -capacity; shutdown drains results with an elapsed-time timeout before the final -flush. Bounded CPU tasks use -regular Tokio tasks because the pinned fork's public `spawn_blocking` still -requires OS threads. JSPI supplies stack suspension, not threads. +The whole Worker is Rust: `worker-build --emscripten --tokio` links the +`pumpkin-do` bin for `wasm32-unknown-emscripten` through emcc and emits the +module Wrangler serves. The entrypoint's `connect` handler forwards each TCP +connection to the named `MinecraftWorld` object with `stub.connect` and pipes +both directions; `fetch` (a `worker` crate `#[event(fetch)]`) serves status. + +`MinecraftWorld` is a `#[wasm_bindgen]` class constructed with the object's +state. Its `connect` is a `#[wasm_bindgen(tokio)]` export: the future runs on +the thread's Tokio `LocalEventLoop` (tokio-rs/tokio#8484), the current-thread +scheduler and drivers with the host event loop as their wait, and the export +returns the Promise of its outcome. Nothing blocks or suspends. The first +`connect` after idle runs the server lifetime: it starts Pumpkin on the mounted +world, awaits the server's return after its final save, awaits `storage.sync()`, +and settles the connection's promise. Later connections and `status` calls enter +the same instance as ordinary calls while that future is parked. A connection +arriving while the server is starting or stopping waits for the next phase +change and then retries, so a client that connects as the previous server +checkpoints starts the next one. The runtime's epoll and timer waits register +with the host through Emscripten's Node backend (emscripten-core/emscripten#27547), +so a readiness or timer callback resumes the scheduler on the host loop. + +Pumpkin binds its stock `TcpListener` on port 25565. Emscripten's Node socket +backend implements that with `net.BoundSocket`/`net.Server`, which workerd scopes +to the Durable Object's own port table. `connect` routes each inbound socket to +that listener with `handleAsNodeConnection`; the accepted connection surfaces +through epoll readiness and Pumpkin's normal accept loop, reporting the bound +address and an unspecified peer. + +One wasm instance serves the isolate, so one object hosts one server at a time; +`WORLD_NAME` selects it. When the last connection closes, the server root calls +`stop_server`, Pumpkin saves and returns, and `reset_stop` clears the process-wide +stop state so the next connection can start a fresh server in the same instance. + +Pumpkin's `single-threaded` feature selects cooperative inline chunk generation +and the async ticker. The same scheduler loop serves native builds, which +dispatch generation onto Rayon. Save batches await queue capacity; shutdown +drains results with an elapsed-time timeout before the final flush. Bounded CPU +tasks use regular Tokio tasks because `spawn_blocking` requires OS threads. Structure templates store palette indices and share their block arrays between the placement and query APIs. Block-entity NBT is shared until a placement needs @@ -23,41 +49,22 @@ neighborhood compact without changing its dependency rules. Emscripten grows memory in 2 MiB increments, configured by `build.rs`, while retaining the 8 MiB stack. See [memory measurements](memory-reduction.md). -Emscripten factory mode (`MODULARIZE=1`) isolates module instances. Wrangler -provides the compiled wasm through `instantiateWasm`; the generated host glue uses -Workers' Node compatibility APIs. The SDK's standalone-loader initialization and -recovery hook is disabled for this embedding. - -## Filesystem and checkpoints - -The host mounts `LocalDOFilesystem(ctx.storage)` from durable-object-fs at `/data`. -Emscripten's `NODERAWFS` routes Rust filesystem calls through worker-fs-mount's -synchronous Node API, including descriptors, positional I/O, rename, and sync. -World metadata, chunks, entities, players, and configuration use this mount. -Logging goes to the host console. - -Each wasm instance retains its own mount context across requests and Tokio -callbacks using `AsyncLocalStorage.snapshot()`. Descriptors opened at startup -therefore remain valid until that runtime shuts down, without exposing one world's -mount to another object. - -The Emscripten compatibility library in `worker/fs-library.js` supplies the mounted Node -filesystem to the factory: the generated loader's runtime `createRequire` calls -cannot use Wrangler's build-time aliases. It also initializes file flags from -public `fs.constants` and implements synchronous `fd_sync`. Paths resolve against -the wasm instance's working directory (initially `/data`), without changing the -isolate-wide Node process directory. - -The upstream `entries` table stores metadata, and `file_pages` stores contents in -64 KiB rows. Descriptor writes update only affected pages. Truncation supports -sparse files, and file writes and native rename use SQLite transactions. Whole-file -reads still require a buffer large enough for the result. - -After the final connection leaves, Pumpkin saves and shuts down, and the object -awaits `storage.sync()`. New connections wait for this checkpoint, then create a -fresh runtime from the stored files. Checkpoint failures remain visible in status. -This preserves issued writes; terminating a server with active clients can still -lose changes in Pumpkin's in-memory caches. +## Filesystem + +The world is a SQLite-backed filesystem: the object constructor mounts +`durable-object-fs`'s `LocalDOFilesystem(storage)` at `/data` through +`worker-fs-mount`, whose `node:fs` implementation routes that prefix to the +object's storage and every other path (stdio, `/tmp`) to workerd's own +filesystem. `src/workerd.js` rebinds Emscripten's `NODERAWFS` to it, resolves +relative paths against Emscripten's working directory (`/data`, distinct from +the isolate's `process.cwd()`), and disables the VFS's own permission checks +since the host enforces them. Every Pumpkin write is therefore durable as it +happens, under the object's transaction semantics; after the final save the +server awaits `storage.sync()` before the last connection is reported closed, +and status shows that time. + +Logging goes to the process's stdout and stderr through `NODERAWFS`, which +Wrangler relays as `stdout:` and `stderr:` lines. ## Development interfaces @@ -65,35 +72,25 @@ The supplied configuration listens on loopback TCP port 25565 and HTTP port 8787 The world name comes from `WORLD_NAME` in `wrangler.jsonc`. - `GET /health`: Worker readiness. -- `GET /`: phase, connection count, runtime statistics, and last checkpoint time. +- `GET /`: phase, connection count, runtime statistics, and last save time. Runtime statistics include Wasm capacity and allocator in-use/free/arena bytes. Allocator counters include allocation metadata and unused container capacity; Wasm capacity additionally includes static data, stack, and growth headroom. -The public status path reads no filesystem internals and remains available when -startup fails. Checkpointing is internal to the connection lifecycle. The -`worker/pumpkin.ts` facade binds exports to the module context once, so the -Durable Object deals with `connect`, `stop`, and `status` methods. - Local databases live in `.data/workers/server/`. Integration tests use separate -storage under `.data/probes/` and restart Wrangler to verify restoration. The echo -and large-file test fixture has its own Worker configuration and wasm binary; it -is excluded from the application bundle. - -Current workerd versions can report `Network connection lost` when TCP clients -disconnect. Rust panics, Rust error logs, and runtime crashes fail the integration -test. +storage under `.data/probes/` and restart Wrangler to verify restoration. Rust +panics, Rust error logs, and runtime crashes fail the integration test. ## Build constraints -- Use the pinned Rust nightly and matching wasm-bindgen CLI from setup. -- Keep static relocation, unwind semantics, the 8 MiB stack, and memory growth. -- Pass Emscripten link settings through rustc so they do not affect C compilation. -- Keep the compatibility initializer linked even when Rust does not call `fd_sync`; - `build.rs` configures this and tracks the library as a build input. +- Use the pinned Rust toolchain, Emscripten, worker-build and wasm-bindgen CLI + from setup. +- Keep static relocation, exnref exception handling on both the C and Rust + sides, the 8 MiB stack, and memory growth. worker-build supplies the common + codegen and link settings (including `--cfg tokio_unstable`; `LocalEventLoop` + is unstable API); `build.rs` adds the application's own: `NODERAWFS`, the + stack size, the growth step, and the JS library. - Prefix Rust exports to avoid collisions with libc and Emscripten symbols. -- The synchronous SQLite mount supplies `fd_sync`; checkpoint completion separately - awaits Durable Object storage synchronization. [Dependency pins and patch maintenance](dependencies.md) diff --git a/docs/dependencies.md b/docs/dependencies.md index 6dbb09c..bac9834 100644 --- a/docs/dependencies.md +++ b/docs/dependencies.md @@ -1,20 +1,18 @@ # Dependency sources and pins A clean checkout is self-contained after `bash scripts/setup.sh`. Dependency sources -are ignored under `.work/`. Both Cargo and npm dependency graphs are locked. +and toolchains are ignored under `.work/`. Both Cargo and npm dependency graphs are +locked. ## Direct checkouts | Component | Source / branch | Commit | Local changes or purpose | | --- | --- | --- | --- | -| emscripten | [cf](https://github.com/guybedford/emscripten) | `21166256c4c4d73d39b3685c8973d7cbe427ce8c` | Fork frontend: epoll, JSPI, wasm-bindgen post-link | -| wasm-bindgen | [emscripten-non-identifier-names](https://github.com/guybedford/wasm-bindgen) | `4b69f3b3ba4212c857be6854f77fa5aec8b62871` | Closure-finalizer string escaping | -| tokio | [emscripten-layering](https://github.com/guybedford/tokio) | `7c1d4977c510866775ed6164b58b2218a6a2955b` | HostedRuntime and normal Tokio TCP/UDP APIs; unmodified | -| libc | [libc-0.2-emscripten](https://github.com/guybedford/libc) | `4091fe0b0dc5f9c1a27bed75be1ff02bb27e756d` | Emscripten epoll and socket support; unmodified | -| ring | [emscripten](https://github.com/guybedford/ring) | `6671f7cfbb13f249b571ffa6326275a8596e0ca2` | Portable Emscripten crypto backend; unmodified | -| pumpkin | [master](https://github.com/Pumpkin-MC/Pumpkin) | `b5b9b9d7010e793806a83c495af223c67e1d35ee` | Headless embedding, shared async scheduler, compact templates/generation chunks, and build-script fixes | -| workers-rs | [connect-bindings](https://github.com/ThomasRubini/workers-rs) | `7db011ec97658a5d907f3e3102028ce86c044f19` | TCP ingress PR #1041; pinned ABI, Emscripten Tokio promise adapter, host-owned initialization, and socket shutdown flushing | -| wasm-streams | [v0.6.0](https://github.com/MattiasBuelens/wasm-streams) | `35665f7b1da830b5ac51b4c6c3ff13f5c1a09ccb` | Build only the Rust library; its standalone `cdylib` is incompatible with static Emscripten linking | +| pumpkin | [master](https://github.com/Pumpkin-MC/Pumpkin) | `b5b9b9d7010e793806a83c495af223c67e1d35ee` | Headless embedding, shared async scheduler, restartable stop signal, compact templates/generation chunks, and build-script fixes | +| tokio | [emscripten-event-loop-host](https://github.com/guybedford/tokio) | `067f92b60cbba5c9fea585806af4b07fd838427e` | `LocalEventLoop` (tokio-rs/tokio#8484) and `net` over epoll on Emscripten; unmodified. | +| workers-rs | [gbedford/worker-build-emscripten](https://github.com/cloudflare/workers-rs/pull/1061) | `f4546f5379a933fbf3607bce205a8b372811d465` | cloudflare/workers-rs#1061: the `worker` crate and `worker-build --emscripten --tokio`; its `wasm-bindgen` submodule ([gbedford/emscripten-stack](https://github.com/wasm-bindgen/wasm-bindgen) `d48e82c4b3f7a182223fb1b1c7b99f59cf9b7c17`) carries `#[wasm_bindgen(tokio)]` (wasm-bindgen/wasm-bindgen#5334) and supplies every wasm-bindgen crate; unmodified | +| emscripten | [cf-final](https://github.com/guybedford/emscripten) | `462303990a2f62e2c065dcc3ba0794cb7bb2e6c4` | Upstream main plus emscripten-core/emscripten#27547 (epoll listeners on the host loop), #27742 (async DNS lookup) and #27724 (blocking accept under pthreads); unmodified | +| emsdk | [main](https://github.com/emscripten-core/emsdk) | `5eb0bde7585670252e8ba05e9d361627bffd08b5` | LLVM, Binaryen and Node from emscripten-releases build `8324e94759a0292e342577007021b4b47106333b`, paired with the frontend's main | Patches are relative to the pinned commits above. Setup checks reverse application before applying a patch and refuses to repin a modified checkout. @@ -26,26 +24,42 @@ commit. ## Cargo-managed forks -| Crate | Source | Commit | +| Crate | Source | Purpose | | --- | --- | --- | -| mio | https://github.com/guybedford/mio | `a62c9e46833fc255c9217ab9aa362c6221ed4401` | -| socket2 | https://github.com/rust-lang/socket2 | `239dd83a4ced08e514d2c38942aab99791119f0d` | +| mio | https://github.com/guybedford/mio branch `emscripten` | Emscripten epoll selector (tokio-rs/mio#1969) | +| libc | https://github.com/rust-lang/libc branch `libc-0.2` | Emscripten epoll bindings, unreleased | +| ring | https://github.com/guybedford/ring branch `emscripten` | getrandom-backed `SystemRandom` on Emscripten | +| wasm-streams | https://github.com/guybedford/wasm-streams branch `rlib-only` | MattiasBuelens/wasm-streams#40; a `worker` dependency that must not also link a cdylib | -The root Cargo.toml applies these overrides and the local checkouts. Cargo.lock -pins the full application graph. `toolchain/wasm-bindgen.Cargo.lock` separately pins -the host CLI graph, since that upstream workspace does not commit a lockfile. +The root Cargo.toml applies these overrides and the local checkouts, and +patches the wasm-bindgen crates to the workers-rs submodule so the `worker` +crate and the application share one wasm-bindgen. Cargo.lock pins the full +application graph, including the branch commits. ## Host tools -- Rust: `nightly-2026-07-20`, target `wasm32-unknown-emscripten`; setup installs both through rustup. +- Rust: `beta` channel (1.99; `OwnedFd::try_clone` on Emscripten), target + `wasm32-unknown-emscripten`; setup installs both through rustup. rustc needs a + larger compile-thread stack for pumpkin-data's generated tables; the scripts + set `RUST_MIN_STACK`. +- worker-build: built by setup from the workers-rs checkout into `.work/bin/`. + It drives cargo and emcc with the common link settings, wraps the exports + into the entrypoint and Durable Object classes, and emits `build/`. +- wasm-bindgen CLI: the release matching the `wasm-bindgen` crate version in + Cargo.toml, installed by setup into `.work/bin/`; worker-build is pointed at + it with `WASM_BINDGEN_BIN`. emcc runs it as a post-link step under + `-sWASM_BINDGEN`. - Node: 24+; 26 recommended and selected in CI. Used for build tools and tests. -- Python: 3.11+ (Emscripten scripts and TOML parsing). -- Backend: Homebrew `emscripten`, detected with `brew --prefix emscripten`. - Homebrew backend versions are external prerequisites, not pinned source files. - -Setup deliberately uses the fork frontend with Homebrew's LLVM/binaryen backend; -emsdk is not needed. It writes a machine-local `.emscripten_cf`, builds wasm-bindgen -explicitly for the host target, and places the CLI in `.work/bin/`. +- Python: 3.11+ (Emscripten scripts, emsdk and TOML parsing). +- Emscripten backend: LLVM, Binaryen and Node through emsdk under `.work/emsdk` + (or an activated emsdk selected with `EMSDK`). `scripts/common.sh` hands the + frontend checkout and emsdk to worker-build through `EMSCRIPTEN` and `EMSDK`; + it uses the frontend unpatched. +- workerd 1.20260918.1 (Wrangler's bundled version, also pinned directly in + `package.json`): the first release with `net.Server` inbound routing into + Durable Objects (`handleAsNodeConnection`, cloudflare/workerd#7306, #7313) and + the `node:fs` fixes for positional buffer I/O (#7368), `O_TRUNC` (#7369), + `O_CREAT` (#7393), and rename over an existing path (#7394). ## Updating a patch @@ -54,14 +68,12 @@ upstream base from the repository root. Write it under `.work/` to preserve the commit description at the top of the maintained patch: ```sh +git -C .work/pumpkin add -N crates/pumpkin/src/net/bedrock/nethernet_stub.rs git -C .work/pumpkin diff b5b9b9d7010e793806a83c495af223c67e1d35ee -- \ . ':(exclude)Cargo.lock' ':(exclude)crates/pumpkin-world/src/generation' \ > .work/pumpkin-emscripten.diff git -C .work/pumpkin diff b5b9b9d7010e793806a83c495af223c67e1d35ee -- \ crates/pumpkin-world/src/generation > .work/pumpkin-memory.diff -git -C .work/wasm-bindgen diff 4b69f3b3ba4212c857be6854f77fa5aec8b62871 -- crates/cli-support/src/js/mod.rs > .work/wasm-bindgen-emscripten-closures.diff -git -C .work/workers-rs diff 7db011ec97658a5d907f3e3102028ce86c044f19 > .work/workers-rs-emscripten-toolchain.diff -git -C .work/wasm-streams diff 35665f7b1da830b5ac51b4c6c3ff13f5c1a09ccb > .work/wasm-streams-rlib.diff ``` Replace the corresponding patch's contents from its first `diff --git` line onward @@ -72,14 +84,14 @@ The Pumpkin path filters reflect the current separation: all memory-patch files are under `crates/pumpkin-world/src/generation/`. Adjust the filters if that scope changes, keeping platform support and memory optimizations in their own patches. -`git diff` omits untracked files, including files created by existing patches. -Preserve those added-file diffs when regenerating (for example, -`nethernet_stub.rs` in the compatibility patch). If you add a file in a dependency -checkout, include it explicitly and verify application on a fresh copy of the base. -Run `bash scripts/test.sh` after runtime changes. Keep unpatched checkouts unmodified. -Setup refuses to repin a modified checkout rather than discarding local work. +`git diff` omits untracked files, including files created by existing patches; +`add -N` above includes `nethernet_stub.rs`. If you add a file in a dependency +checkout, include it the same way and verify application on a fresh copy of the +base. Run `bash scripts/test.sh` after runtime changes. Keep unpatched checkouts +unmodified. Setup refuses to repin a modified checkout rather than discarding +local work. -For source/patch validation without rebuilding the toolchain: +For source/patch validation without provisioning the toolchain: ```sh bash scripts/setup.sh --sources-only @@ -87,11 +99,11 @@ bash scripts/setup.sh --sources-only ## JavaScript dependencies -`package-lock.json` pins Wrangler 4.129.0, worker-fs-mount 0.2.0, -durable-object-fs 1.0.0, TypeScript, and their dependencies. The npm override for -durable-object-fs replaces its published `workspace:*` peer dependency with the -selected worker-fs-mount version. Generated runtime/binding types are recreated by -`npm run types` and are not committed. +`package-lock.json` pins Wrangler 4.135.0, workerd 1.20260918.1, `worker-fs-mount` 0.2.0 and +`durable-object-fs` 1.0.0 (the SQLite filesystem mount, imported by +`src/js/mount.js` and bundled by worker-build). worker-build generates +`build/index.js`, which wraps the exports into the entrypoint and derives the +Durable Object class from `DurableObject` for RPC. After moving a checkout with cached build output, run `cargo clean` before rebuilding. Generated data can contain absolute paths. This leaves databases under `.data/` intact. diff --git a/docs/development.md b/docs/development.md index f27b4bb..9dd4121 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,14 +1,17 @@ # Development Follow the [README setup instructions](../README.md#try-it) to install the pinned -Rust toolchain, provision dependency sources, and build the matching wasm-bindgen CLI. +Rust toolchain, provision dependency sources and Emscripten, and build the +worker-build and wasm-bindgen CLIs. The initial Pumpkin compilation takes several minutes. Later builds use Cargo's -cache. `build.rs` owns the Emscripten library arguments and tracks changes to the -compatibility library. +cache. worker-build owns the common codegen and link settings; `build.rs` adds +the application's own and the JS library (`src/workerd.js`), tracking changes to +it. `.cargo/config.toml` repeats the codegen cfgs so `cargo check` sees them. +Wrangler runs `worker-build --emscripten --tokio --release` as its build command +and serves `build/index.js`. ```sh -npm run build # Compile the production server -npm run typecheck # Regenerate Worker types and check TypeScript +npm run build # Compile the production Worker npm test # Full Wrangler integration suite npm run test:scheduler # Native queue/backpressure and shutdown regressions npm run test:memory # Two-player memory probe on a fresh, fixed-seed world @@ -18,9 +21,9 @@ Tests fail if their loopback ports (25565 and 8787) are occupied. They start and stop only their own Wrangler process groups. Their databases and logs live in `.data/probes/`; playable worlds are separate under `.data/workers/server/`. -`npm test` must finish with `PUMPKIN-DO-SQLITE-RESTART-OK`. It checks filesystem -isolation, synchronous descriptors, large files, failed-startup status, two-player -gameplay, a real block edit, and player/block restoration after a restart. +`npm test` must finish with `PUMPKIN-DO-SQLITE-RESTART-OK`. It checks two-player +gameplay, a real block edit, and player/block restoration after a restart, which +exercises the SQLite-mounted world across a restart. The memory probe waits for both clients to receive 81 chunks, observes 30 seconds of stationary play, then checkpoints. Each run creates a fresh isolated world diff --git a/package-lock.json b/package-lock.json index 5c4e381..cb12ab0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,8 @@ "worker-fs-mount": "0.2.0" }, "devDependencies": { - "@cloudflare/workers-types": "5.20260905.1", - "@types/node": "26.4.1", - "typescript": "~5.9.3", - "wrangler": "4.129.0" + "workerd": "1.20260918.1", + "wrangler": "4.135.0" }, "engines": { "node": ">=24" @@ -46,9 +44,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20260903.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260903.1.tgz", - "integrity": "sha512-FG+4mGxAXhKiL/1temH42alevIkumtYXNidhTa//3yULpzux6APw5UNVocI/vCQ1yG1YOEZRfbVy8lyuipM9MQ==", + "version": "1.20260918.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260918.1.tgz", + "integrity": "sha512-H5Em6Wd0jjxaloYh2rp+WLBl2eWbkk7nSP1svGt6K1RSv/rNVqmKdpjZPJTBSavOmeYGa88qvtOWwS+33OHTqQ==", "cpu": [ "x64" ], @@ -63,9 +61,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20260903.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260903.1.tgz", - "integrity": "sha512-o241VefnjG8eG+kGap5CjgV4zOT5UaAmS3OR1VZCpNj7vkXGxvp9KftKvtQgcCsIqJKaKx+7Xd7xq0L1DjkfPQ==", + "version": "1.20260918.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260918.1.tgz", + "integrity": "sha512-CR9JRZEQo93fNgBVF4Df2H2/VYO4n7rxSseSwCVv3bJQEb0huADUSCj2FK4ipxar8ToOEB4wawyw0vJo5U/rQQ==", "cpu": [ "arm64" ], @@ -80,9 +78,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20260903.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260903.1.tgz", - "integrity": "sha512-/VEvvtQ/XKf6HlBbg6FbvpwcpfYUcx6Fv6RkASY8DyEmUyuJ8rc7Qxil83ClRFoBzz/GY0BV94UZ6F+wers/hg==", + "version": "1.20260918.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260918.1.tgz", + "integrity": "sha512-UQ2nnY3qpXLzQ80frmWO+8HvtqyWaQILe8QYZwpemdjT+sqwCz4Dz+0/WVkFco0v/04kIIqikVeLTY/7gEhmkw==", "cpu": [ "x64" ], @@ -97,9 +95,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20260903.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260903.1.tgz", - "integrity": "sha512-OWhihGC6KoTXF4u2C1AonfpgXeM4/7p/1IXuALqXESmFUpLLP5gZhRzjSk/gWW+mrCZDfSrvnjifl+lRqselbA==", + "version": "1.20260918.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260918.1.tgz", + "integrity": "sha512-4rib51MaLNWweUIUxM/Xj558M5QmyZoBSf0ffv+lYah5VTrvwnez3XGxX72pElnBKHROvAPOi5msQ5Ts8JSI0A==", "cpu": [ "arm64" ], @@ -114,9 +112,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20260903.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260903.1.tgz", - "integrity": "sha512-soPMF9/aMHlHKK7M0vq5HrRRicPbnZO1F6ZZ7JWN8EltxWJU5L7CEq7CxjO878DzyPx7gYvqBFy3SVgdZKZjMQ==", + "version": "1.20260918.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260918.1.tgz", + "integrity": "sha512-sATrMx5ShYYgmgUGrcTmvsFSJBFuN95NEkX3xwb1qk8w1A6h5N11sea7yN2IeibwPyPmXKjWNjXOno0hZAM71Q==", "cpu": [ "x64" ], @@ -130,13 +128,6 @@ "node": ">=16" } }, - "node_modules/@cloudflare/workers-types": { - "version": "5.20260905.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-5.20260905.1.tgz", - "integrity": "sha512-ebDpVTgrnee245IRBTazyfjfmZas+HJehZyVQkKmUozsBN8RzvXXMI0XjjcT2rQjw+0USewfEGGLYiHO+TL0Sg==", - "dev": true, - "license": "MIT OR Apache-2.0" - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -614,9 +605,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.2.tgz", - "integrity": "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", + "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", "cpu": [ "arm64" ], @@ -633,13 +624,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.3.1" + "@img/sharp-libvips-darwin-arm64": "1.3.3" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.2.tgz", - "integrity": "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", + "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", "cpu": [ "x64" ], @@ -656,13 +647,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.3.1" + "@img/sharp-libvips-darwin-x64": "1.3.3" } }, "node_modules/@img/sharp-freebsd-wasm32": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.2.tgz", - "integrity": "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", + "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -670,7 +661,7 @@ "freebsd" ], "dependencies": { - "@img/sharp-wasm32": "0.35.2" + "@img/sharp-wasm32": "0.35.4" }, "engines": { "node": ">=20.9.0" @@ -680,9 +671,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz", - "integrity": "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", "cpu": [ "arm64" ], @@ -697,9 +688,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz", - "integrity": "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", + "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", "cpu": [ "x64" ], @@ -714,16 +705,13 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz", - "integrity": "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", + "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", "cpu": [ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -734,16 +722,13 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz", - "integrity": "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", + "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -754,16 +739,13 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz", - "integrity": "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", + "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", "cpu": [ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -774,16 +756,13 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.1.tgz", - "integrity": "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", + "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", "cpu": [ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -794,16 +773,13 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.1.tgz", - "integrity": "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", + "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", "cpu": [ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -814,16 +790,13 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.1.tgz", - "integrity": "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", + "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -834,16 +807,13 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.1.tgz", - "integrity": "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", + "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -854,16 +824,13 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz", - "integrity": "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", + "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -874,16 +841,13 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.2.tgz", - "integrity": "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", + "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", "cpu": [ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -896,20 +860,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.3.1" + "@img/sharp-libvips-linux-arm": "1.3.3" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.2.tgz", - "integrity": "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", + "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -922,20 +883,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.3.1" + "@img/sharp-libvips-linux-arm64": "1.3.3" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.2.tgz", - "integrity": "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", + "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", "cpu": [ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -948,20 +906,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.3.1" + "@img/sharp-libvips-linux-ppc64": "1.3.3" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.2.tgz", - "integrity": "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", + "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", "cpu": [ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -974,20 +929,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.3.1" + "@img/sharp-libvips-linux-riscv64": "1.3.3" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.2.tgz", - "integrity": "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", + "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", "cpu": [ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1000,20 +952,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.3.1" + "@img/sharp-libvips-linux-s390x": "1.3.3" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.2.tgz", - "integrity": "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", + "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1026,20 +975,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.3.1" + "@img/sharp-libvips-linux-x64": "1.3.3" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.2.tgz", - "integrity": "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", + "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", "cpu": [ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1052,20 +998,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.2.tgz", - "integrity": "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", + "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", "cpu": [ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -1078,18 +1021,18 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.3.1" + "@img/sharp-libvips-linuxmusl-x64": "1.3.3" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz", - "integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", + "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.11.1" + "@emnapi/runtime": "^1.11.3" }, "engines": { "node": ">=20.9.0" @@ -1099,9 +1042,9 @@ } }, "node_modules/@img/sharp-webcontainers-wasm32": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.2.tgz", - "integrity": "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", + "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", "cpu": [ "wasm32" ], @@ -1109,7 +1052,7 @@ "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/sharp-wasm32": "0.35.2" + "@img/sharp-wasm32": "0.35.4" }, "engines": { "node": ">=20.9.0" @@ -1119,9 +1062,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz", - "integrity": "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", + "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", "cpu": [ "arm64" ], @@ -1139,9 +1082,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz", - "integrity": "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", + "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", "cpu": [ "ia32" ], @@ -1159,9 +1102,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz", - "integrity": "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", + "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", "cpu": [ "x64" ], @@ -1255,16 +1198,6 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/@types/node": { - "version": "26.4.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.4.1.tgz", - "integrity": "sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~8.3.0" - } - }, "node_modules/blake3-wasm": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", @@ -1386,16 +1319,16 @@ } }, "node_modules/miniflare": { - "version": "5.20260903.0-alpha", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20260903.0-alpha.tgz", - "integrity": "sha512-VCZIFxOqFXeibRBJWwTlppqbv2lkeO10IX3QBRGK9j1QiMAfH/OSsCvbjp1MslBMhVZmy2VTRlVaVnsKnbDp6A==", + "version": "5.20260918.0-alpha", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20260918.0-alpha.tgz", + "integrity": "sha512-vyIes7yW/OTzHtz4GhcBCTohZfqk2eQNiIkngZ07VRA5Qu24aNgW/TrFwlPkMLMjWDQ8R4JJBBHR/KX+liSDtg==", "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", - "sharp": "0.35.2", + "sharp": "0.35.4", "undici": "7.29.0", - "workerd": "1.20260903.1", + "workerd": "1.20260918.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, @@ -1431,15 +1364,15 @@ } }, "node_modules/sharp": { - "version": "0.35.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.2.tgz", - "integrity": "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", + "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.8.4" + "semver": "^7.8.5" }, "engines": { "node": ">=20.9.0" @@ -1448,31 +1381,36 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.35.2", - "@img/sharp-darwin-x64": "0.35.2", - "@img/sharp-freebsd-wasm32": "0.35.2", - "@img/sharp-libvips-darwin-arm64": "1.3.1", - "@img/sharp-libvips-darwin-x64": "1.3.1", - "@img/sharp-libvips-linux-arm": "1.3.1", - "@img/sharp-libvips-linux-arm64": "1.3.1", - "@img/sharp-libvips-linux-ppc64": "1.3.1", - "@img/sharp-libvips-linux-riscv64": "1.3.1", - "@img/sharp-libvips-linux-s390x": "1.3.1", - "@img/sharp-libvips-linux-x64": "1.3.1", - "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", - "@img/sharp-libvips-linuxmusl-x64": "1.3.1", - "@img/sharp-linux-arm": "0.35.2", - "@img/sharp-linux-arm64": "0.35.2", - "@img/sharp-linux-ppc64": "0.35.2", - "@img/sharp-linux-riscv64": "0.35.2", - "@img/sharp-linux-s390x": "0.35.2", - "@img/sharp-linux-x64": "0.35.2", - "@img/sharp-linuxmusl-arm64": "0.35.2", - "@img/sharp-linuxmusl-x64": "0.35.2", - "@img/sharp-webcontainers-wasm32": "0.35.2", - "@img/sharp-win32-arm64": "0.35.2", - "@img/sharp-win32-ia32": "0.35.2", - "@img/sharp-win32-x64": "0.35.2" + "@img/sharp-darwin-arm64": "0.35.4", + "@img/sharp-darwin-x64": "0.35.4", + "@img/sharp-freebsd-wasm32": "0.35.4", + "@img/sharp-libvips-darwin-arm64": "1.3.3", + "@img/sharp-libvips-darwin-x64": "1.3.3", + "@img/sharp-libvips-linux-arm": "1.3.3", + "@img/sharp-libvips-linux-arm64": "1.3.3", + "@img/sharp-libvips-linux-ppc64": "1.3.3", + "@img/sharp-libvips-linux-riscv64": "1.3.3", + "@img/sharp-libvips-linux-s390x": "1.3.3", + "@img/sharp-libvips-linux-x64": "1.3.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", + "@img/sharp-libvips-linuxmusl-x64": "1.3.3", + "@img/sharp-linux-arm": "0.35.4", + "@img/sharp-linux-arm64": "0.35.4", + "@img/sharp-linux-ppc64": "0.35.4", + "@img/sharp-linux-riscv64": "0.35.4", + "@img/sharp-linux-s390x": "0.35.4", + "@img/sharp-linux-x64": "0.35.4", + "@img/sharp-linuxmusl-arm64": "0.35.4", + "@img/sharp-linuxmusl-x64": "0.35.4", + "@img/sharp-webcontainers-wasm32": "0.35.4", + "@img/sharp-win32-arm64": "0.35.4", + "@img/sharp-win32-ia32": "0.35.4", + "@img/sharp-win32-x64": "0.35.4" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/supports-color": { @@ -1496,20 +1434,6 @@ "license": "0BSD", "optional": true }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/undici": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", @@ -1520,13 +1444,6 @@ "node": ">=20.18.1" } }, - "node_modules/undici-types": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", - "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", - "dev": true, - "license": "MIT" - }, "node_modules/unenv": { "version": "2.0.0-rc.24", "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", @@ -1547,9 +1464,9 @@ } }, "node_modules/workerd": { - "version": "1.20260903.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260903.1.tgz", - "integrity": "sha512-xJzt2RnCy7ulOULmZy/4JLbEPg1uisp9lVoOUsEz+UVhDsTmrSQ0rBXZMGcXuhr2HCGKs89bg8nnbbzvBSX1Ig==", + "version": "1.20260918.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260918.1.tgz", + "integrity": "sha512-NsjfQlBNQ0iEniv/STOy4zbp8s5k60PzL1Ter02Eg44arbDhHtf6UOs03E31XDRmmBFxSIkAZuCyld3RKH1wqA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -1560,17 +1477,17 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20260903.1", - "@cloudflare/workerd-darwin-arm64": "1.20260903.1", - "@cloudflare/workerd-linux-64": "1.20260903.1", - "@cloudflare/workerd-linux-arm64": "1.20260903.1", - "@cloudflare/workerd-windows-64": "1.20260903.1" + "@cloudflare/workerd-darwin-64": "1.20260918.1", + "@cloudflare/workerd-darwin-arm64": "1.20260918.1", + "@cloudflare/workerd-linux-64": "1.20260918.1", + "@cloudflare/workerd-linux-arm64": "1.20260918.1", + "@cloudflare/workerd-windows-64": "1.20260918.1" } }, "node_modules/wrangler": { - "version": "4.129.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.129.0.tgz", - "integrity": "sha512-PGPvs9UPoFrwxT0VogpESSZGvZIctAuTK3wGsLLPHtHsSgS85kNdvtpa2d14UzG8gwLWD64XUGFPGG9tOXG9VQ==", + "version": "4.135.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.135.0.tgz", + "integrity": "sha512-WrNBQSfIG6YcILJcodYr5ty8vgkzGsV8YX+kfd+uZ5/Bd8cUW0GnUIRKAVfn5kYKqh1m/BPcji9h1d7mE8euFw==", "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { @@ -1578,10 +1495,10 @@ "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", - "miniflare": "5.20260903.0-alpha", + "miniflare": "5.20260918.0-alpha", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", - "workerd": "1.20260903.1" + "workerd": "1.20260918.1" }, "bin": { "cf-wrangler": "bin/cf-wrangler.js", @@ -1595,7 +1512,7 @@ "fsevents": "2.3.3" }, "peerDependencies": { - "@cloudflare/workers-types": "^5.20260903.1" + "@cloudflare/workers-types": "^5.20260918.1" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { diff --git a/package.json b/package.json index eac72cc..68cd6dd 100644 --- a/package.json +++ b/package.json @@ -10,23 +10,19 @@ "worker-fs-mount": "0.2.0" }, "devDependencies": { - "@cloudflare/workers-types": "5.20260905.1", - "@types/node": "26.4.1", - "typescript": "~5.9.3", - "wrangler": "4.129.0" - }, - "overrides": { - "durable-object-fs": { - "worker-fs-mount": "$worker-fs-mount" - } + "workerd": "1.20260918.1", + "wrangler": "4.135.0" }, "scripts": { "build": "bash scripts/build.sh", "dev": "bash scripts/serve.sh", "test": "bash scripts/test.sh", "test:memory": "npm run build && node tests/memory.mjs", - "types": "wrangler types worker/env.d.ts --strict-vars=false", - "typecheck": "npm run types && tsc --noEmit", "test:scheduler": "bash scripts/test-scheduler.sh" + }, + "overrides": { + "durable-object-fs": { + "worker-fs-mount": "$worker-fs-mount" + } } } diff --git a/patches/README.md b/patches/README.md index 945c45b..5d6f0da 100644 --- a/patches/README.md +++ b/patches/README.md @@ -9,14 +9,10 @@ diff below it. Preserve the description when regenerating the diff. | Patch | Checkout | Purpose | | --- | --- | --- | -| `pumpkin-emscripten.patch` | `.work/pumpkin` | Headless optional subsystems, single-threaded runtime and blocking-task helper, shared async chunk scheduler with backpressure and timed draining, unified ticker, and platform guards | +| `pumpkin-emscripten.patch` | `.work/pumpkin` | Headless optional subsystems, single-threaded runtime and blocking-task helper, shared async chunk scheduler with backpressure and timed draining, unified ticker, restartable stop signal, and platform guards | | `pumpkin-memory.patch` | `.work/pumpkin` | Shared indexed structure templates, immutable block-entity NBT, paletted generation chunks, and a temporary dense noise buffer | -| `wasm-bindgen-emscripten-closures.patch` | `.work/wasm-bindgen` | Escape generated closure-finalizer postsets as JavaScript strings | -| `workers-rs-emscripten-toolchain.patch` | `.work/workers-rs` | Match the pinned wasm-bindgen ABI, provide the Emscripten Tokio promise adapter, leave initialization/recovery to the host, and flush pending writes before socket shutdown | -| `wasm-streams-rlib.patch` | `.work/wasm-streams` | Omit the standalone cdylib when embedding the stream adapter in Emscripten | -`pumpkin-emscripten.patch` includes the injected-stream connection entry point used -by the DO. Setup applies it before `pumpkin-memory.patch`. The two patches use the +Setup applies `pumpkin-emscripten.patch` before `pumpkin-memory.patch`. The two patches use the same pinned base and currently modify separate files, so each can also be applied and checked independently. diff --git a/patches/pumpkin-emscripten.patch b/patches/pumpkin-emscripten.patch index ce284ef..b516429 100644 --- a/patches/pumpkin-emscripten.patch +++ b/patches/pumpkin-emscripten.patch @@ -1,17 +1,20 @@ Subject: [PATCH] Support headless Pumpkin on a cooperative runtime -Embedding Pumpkin in a Durable Object requires accepting host-provided -streams and running without OS worker threads or native plugin runtimes. -Add an injected-stream connection entry point and optional subsystems for -NetherNet, plugin loading, the console, and system crash reporting. Native -builds retain these subsystems and thread-backed execution by default. +Embedding Pumpkin in a Durable Object requires running without OS worker +threads or native plugin runtimes. Add optional subsystems for NetherNet, +plugin loading, the console, and system crash reporting. Native builds +retain these subsystems and thread-backed execution by default. Share asynchronous ticker and chunk-scheduler loops between native and single-threaded builds. Route Rayon and blocking jobs through helpers that can run on the current thread, await scheduler notifications and results, -and track cooperative tasks through shutdown. Match the Tokio requirement -to the hosted-runtime fork and guard unsupported Emscripten system calls -and synchronous HTTP lookups. +and track cooperative tasks through shutdown. Drop the Tokio features that +need OS threads and signals from single-threaded builds and guard +unsupported Emscripten system calls and synchronous HTTP lookups. + +Make the process-wide stop token replaceable behind a StopSignal wrapper +and add reset_stop, so an embedding can start another server in the same +process after one has stopped. Reserve save-queue capacity before transferring chunks or marking writes pending, and widen pending-write counters to usize. Drain late generation @@ -27,7 +30,7 @@ Base-commit: b5b9b9d7010e793806a83c495af223c67e1d35ee --- diff --git a/Cargo.toml b/Cargo.toml -index 143ba8a36..57314ca35 100644 +index 143ba8a3..57314ca3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -126,7 +126,7 @@ strip = false @@ -40,7 +43,7 @@ index 143ba8a36..57314ca35 100644 thiserror = { version = "2.0", default-features = false } diff --git a/crates/pumpkin-util/Cargo.toml b/crates/pumpkin-util/Cargo.toml -index f4fb121fb..0a4a30761 100644 +index f4fb121f..0a4a3076 100644 --- a/crates/pumpkin-util/Cargo.toml +++ b/crates/pumpkin-util/Cargo.toml @@ -6,11 +6,13 @@ rust-version.workspace = true @@ -67,7 +70,7 @@ index f4fb121fb..0a4a30761 100644 [[bench]] diff --git a/crates/pumpkin-util/src/lib.rs b/crates/pumpkin-util/src/lib.rs -index 5119797bc..a1ae947a2 100644 +index 5119797b..a1ae947a 100644 --- a/crates/pumpkin-util/src/lib.rs +++ b/crates/pumpkin-util/src/lib.rs @@ -310,3 +310,42 @@ impl TryFrom for Hand { @@ -114,7 +117,7 @@ index 5119797bc..a1ae947a2 100644 + } +} diff --git a/crates/pumpkin-world/Cargo.toml b/crates/pumpkin-world/Cargo.toml -index f1675a8f2..82c219179 100644 +index f1675a8f..82c21917 100644 --- a/crates/pumpkin-world/Cargo.toml +++ b/crates/pumpkin-world/Cargo.toml @@ -17,11 +17,9 @@ bytes.workspace = true @@ -145,7 +148,7 @@ index f1675a8f2..82c219179 100644 tempfile.workspace = true criterion = { workspace = true, features = ["html_reports", "async_tokio"] } diff --git a/crates/pumpkin-world/src/chunk/format/anvil.rs b/crates/pumpkin-world/src/chunk/format/anvil.rs -index 38de745f9..60cedf53d 100644 +index 38de745f..60cedf53 100644 --- a/crates/pumpkin-world/src/chunk/format/anvil.rs +++ b/crates/pumpkin-world/src/chunk/format/anvil.rs @@ -798,7 +798,7 @@ impl ChunkSerializer for AnvilChunkFile< @@ -168,7 +171,7 @@ index 38de745f9..60cedf53d 100644 }); }); diff --git a/crates/pumpkin-world/src/chunk/format/linear.rs b/crates/pumpkin-world/src/chunk/format/linear.rs -index 3927c66fc..fd76019bf 100644 +index 3927c66f..fd76019b 100644 --- a/crates/pumpkin-world/src/chunk/format/linear.rs +++ b/crates/pumpkin-world/src/chunk/format/linear.rs @@ -599,7 +599,7 @@ impl ChunkSerializer for LinearV2File @@ -191,7 +194,7 @@ index 3927c66fc..fd76019bf 100644 }); }); diff --git a/crates/pumpkin-world/src/chunk/format/pump.rs b/crates/pumpkin-world/src/chunk/format/pump.rs -index f1c44adc7..b5a8dd1d7 100644 +index f1c44adc..b5a8dd1d 100644 --- a/crates/pumpkin-world/src/chunk/format/pump.rs +++ b/crates/pumpkin-world/src/chunk/format/pump.rs @@ -147,7 +147,7 @@ where @@ -214,7 +217,7 @@ index f1c44adc7..b5a8dd1d7 100644 }); }); diff --git a/crates/pumpkin-world/src/chunk/io/mod.rs b/crates/pumpkin-world/src/chunk/io/mod.rs -index c4daf5bb5..78271c344 100644 +index c4daf5bb..78271c34 100644 --- a/crates/pumpkin-world/src/chunk/io/mod.rs +++ b/crates/pumpkin-world/src/chunk/io/mod.rs @@ -13,7 +13,7 @@ where @@ -227,7 +230,7 @@ index c4daf5bb5..78271c344 100644 /// The result of loading a chunk data. diff --git a/crates/pumpkin-world/src/chunk_system/channel.rs b/crates/pumpkin-world/src/chunk_system/channel.rs -index 190d578fa..c138b1f50 100644 +index 190d578f..c138b1f5 100644 --- a/crates/pumpkin-world/src/chunk_system/channel.rs +++ b/crates/pumpkin-world/src/chunk_system/channel.rs @@ -14,6 +14,7 @@ pub type LevelChange = ( @@ -281,7 +284,7 @@ index 190d578fa..c138b1f50 100644 } } diff --git a/crates/pumpkin-world/src/chunk_system/mod.rs b/crates/pumpkin-world/src/chunk_system/mod.rs -index f670ad37b..b9d9a8671 100644 +index f670ad37..b9d9a867 100644 --- a/crates/pumpkin-world/src/chunk_system/mod.rs +++ b/crates/pumpkin-world/src/chunk_system/mod.rs @@ -13,7 +13,7 @@ pub type HashSetType = rustc_hash::FxHashSet; @@ -294,7 +297,7 @@ index f670ad37b..b9d9a8671 100644 )>; diff --git a/crates/pumpkin-world/src/chunk_system/schedule.rs b/crates/pumpkin-world/src/chunk_system/schedule.rs -index 145e15de8..2942a1d31 100644 +index 145e15de..2942a1d3 100644 --- a/crates/pumpkin-world/src/chunk_system/schedule.rs +++ b/crates/pumpkin-world/src/chunk_system/schedule.rs @@ -69,13 +69,14 @@ pub struct GenerationSchedule { @@ -863,7 +866,7 @@ index 145e15de8..2942a1d31 100644 + } +} diff --git a/crates/pumpkin-world/src/chunk_system/worker_logic.rs b/crates/pumpkin-world/src/chunk_system/worker_logic.rs -index b50ac05c9..281fa41f0 100644 +index b50ac05c..281fa41f 100644 --- a/crates/pumpkin-world/src/chunk_system/worker_logic.rs +++ b/crates/pumpkin-world/src/chunk_system/worker_logic.rs @@ -90,7 +90,7 @@ fn process_loaded_chunk(chunk: Arc, level: &Level) -> C @@ -876,7 +879,7 @@ index b50ac05c9..281fa41f0 100644 lock: IOLock, ) { diff --git a/crates/pumpkin-world/src/level.rs b/crates/pumpkin-world/src/level.rs -index fbc69749a..533b09d21 100644 +index fbc69749..533b09d2 100644 --- a/crates/pumpkin-world/src/level.rs +++ b/crates/pumpkin-world/src/level.rs @@ -316,7 +316,7 @@ impl Level { @@ -969,7 +972,7 @@ index fbc69749a..533b09d21 100644 } diff --git a/crates/pumpkin-world/src/world_info/anvil.rs b/crates/pumpkin-world/src/world_info/anvil.rs -index ebbcb49b6..46975c6c5 100644 +index ebbcb49b..46975c6c 100644 --- a/crates/pumpkin-world/src/world_info/anvil.rs +++ b/crates/pumpkin-world/src/world_info/anvil.rs @@ -425,6 +425,11 @@ impl WorldInfoWriter for AnvilLevelInfo { @@ -985,7 +988,7 @@ index ebbcb49b6..46975c6c5 100644 let path = level_folder.join(LEVEL_DAT_FILE_NAME); let path_new = level_folder.join("level.dat_new"); diff --git a/crates/pumpkin/Cargo.toml b/crates/pumpkin/Cargo.toml -index 6ade6bfcb..c9230c03a 100644 +index 6ade6bfc..c9230c03 100644 --- a/crates/pumpkin/Cargo.toml +++ b/crates/pumpkin/Cargo.toml @@ -17,6 +17,14 @@ LegalCopyright = "Copyright © 2026 Aleksander Medvedev" @@ -1109,7 +1112,7 @@ index 6ade6bfcb..c9230c03a 100644 [lints] diff --git a/crates/pumpkin/build.rs b/crates/pumpkin/build.rs -index 5314180a1..a6accb815 100644 +index 5314180a..a6accb81 100644 --- a/crates/pumpkin/build.rs +++ b/crates/pumpkin/build.rs @@ -23,8 +23,21 @@ fn main() { @@ -1137,7 +1140,7 @@ index 5314180a1..a6accb815 100644 println!("cargo::rustc-env=GIT_HASH_FULL={git_hash_full}"); } diff --git a/crates/pumpkin/src/command/commands/pumpkin.rs b/crates/pumpkin/src/command/commands/pumpkin.rs -index 0045511a9..9586e5dc5 100644 +index 0045511a..9586e5dc 100644 --- a/crates/pumpkin/src/command/commands/pumpkin.rs +++ b/crates/pumpkin/src/command/commands/pumpkin.rs @@ -56,6 +56,7 @@ fn fetch_all_contributors_cached() -> Vec { @@ -1176,7 +1179,7 @@ index 0045511a9..9586e5dc5 100644 if let Ok(mut guard) = DONATORS_CACHE.lock() { *guard = Some(DonatorCache { diff --git a/crates/pumpkin/src/crash.rs b/crates/pumpkin/src/crash.rs -index 6f88670e3..9969598c2 100644 +index 6f88670e..9969598c 100644 --- a/crates/pumpkin/src/crash.rs +++ b/crates/pumpkin/src/crash.rs @@ -13,6 +13,7 @@ use pumpkin_util::text::{ @@ -1204,7 +1207,7 @@ index 6f88670e3..9969598c2 100644 writeln_output!(output); let cpus = sys.cpus(); diff --git a/crates/pumpkin/src/data/datapack/mod.rs b/crates/pumpkin/src/data/datapack/mod.rs -index 2f8f72349..eefa123d0 100644 +index 2f8f7234..eefa123d 100644 --- a/crates/pumpkin/src/data/datapack/mod.rs +++ b/crates/pumpkin/src/data/datapack/mod.rs @@ -253,7 +253,7 @@ impl DatapackManager { @@ -1217,7 +1220,7 @@ index 2f8f72349..eefa123d0 100644 format!("Failed to open structure '{display_path}': {error}") })?; diff --git a/crates/pumpkin/src/data/player_server.rs b/crates/pumpkin/src/data/player_server.rs -index 0899fa9c1..733e66780 100644 +index 0899fa9c..733e6678 100644 --- a/crates/pumpkin/src/data/player_server.rs +++ b/crates/pumpkin/src/data/player_server.rs @@ -87,7 +87,7 @@ impl ServerPlayerData { @@ -1230,7 +1233,7 @@ index 0899fa9c1..733e66780 100644 if let Err(e) = storage.save_player_data(&uuid, nbt) { error!("Failed to save player data for {uuid}: {e}"); diff --git a/crates/pumpkin/src/entity/mod.rs b/crates/pumpkin/src/entity/mod.rs -index 7d64f9b1c..ca28e83b4 100644 +index 7d64f9b1..ca28e83b 100644 --- a/crates/pumpkin/src/entity/mod.rs +++ b/crates/pumpkin/src/entity/mod.rs @@ -2361,7 +2361,7 @@ impl Entity { @@ -1243,7 +1246,7 @@ index 7d64f9b1c..ca28e83b4 100644 let Some(entity_arc) = world_clone.get_entity_by_id(entity_id) else { return; diff --git a/crates/pumpkin/src/entity/player.rs b/crates/pumpkin/src/entity/player.rs -index a19825bc2..45d6783e8 100644 +index a19825bc..45d6783e 100644 --- a/crates/pumpkin/src/entity/player.rs +++ b/crates/pumpkin/src/entity/player.rs @@ -572,6 +572,7 @@ impl Player { @@ -1277,7 +1280,7 @@ index a19825bc2..45d6783e8 100644 || { self.chunk_sender diff --git a/crates/pumpkin/src/lib.rs b/crates/pumpkin/src/lib.rs -index 7110bc9e4..3438afb65 100644 +index 7110bc9e..f45cadbe 100644 --- a/crates/pumpkin/src/lib.rs +++ b/crates/pumpkin/src/lib.rs @@ -8,12 +8,14 @@ extern crate pumpkin_macros; @@ -1341,7 +1344,66 @@ index 7110bc9e4..3438afb65 100644 }); assert!( -@@ -251,7 +263,7 @@ impl PumpkinServer { +@@ -204,16 +216,57 @@ pub fn init_logger(advanced_config: &AdvancedConfiguration) { + } + + pub static SHOULD_STOP: AtomicBool = AtomicBool::new(false); +-pub static STOP_INTERRUPT: LazyLock = LazyLock::new(CancellationToken::new); ++pub static STOP_INTERRUPT: StopSignal = StopSignal(std::sync::RwLock::new(None)); + pub static SERVER_IS_STOPPING: AtomicBool = AtomicBool::new(false); + pub static CRASH_REPORT: OnceLock = OnceLock::new(); + pub static SERVER_EXIT_CODE: AtomicI32 = AtomicI32::new(0); + ++/// The process-wide stop token, replaceable so an embedding can start another ++/// server in the same process after one has stopped. ++pub struct StopSignal(std::sync::RwLock>); ++ ++impl StopSignal { ++ fn token(&self) -> CancellationToken { ++ if let Some(token) = self.0.read().unwrap_or_else(|e| e.into_inner()).as_ref() { ++ return token.clone(); ++ } ++ self.0 ++ .write() ++ .unwrap_or_else(|e| e.into_inner()) ++ .get_or_insert_with(CancellationToken::new) ++ .clone() ++ } ++ ++ pub fn cancel(&self) { ++ self.token().cancel(); ++ } ++ ++ pub fn is_cancelled(&self) -> bool { ++ self.token().is_cancelled() ++ } ++ ++ pub fn cancelled(&self) -> tokio_util::sync::WaitForCancellationFutureOwned { ++ self.token().cancelled_owned() ++ } ++ ++ #[allow(clippy::should_implement_trait)] ++ pub fn clone(&self) -> CancellationToken { ++ self.token() ++ } ++} ++ + pub fn stop_server() { + SHOULD_STOP.store(true, Ordering::Relaxed); + STOP_INTERRUPT.cancel(); + } + ++/// Clears the stop state so `PumpkinServer::start` can run again in this process. ++pub fn reset_stop() { ++ SHOULD_STOP.store(false, Ordering::Relaxed); ++ SERVER_IS_STOPPING.store(false, Ordering::Release); ++ *STOP_INTERRUPT.0.write().unwrap_or_else(|e| e.into_inner()) = Some(CancellationToken::new()); ++} ++ + pub fn stop_or_exit_server() { + if SERVER_IS_STOPPING.load(Ordering::Acquire) { + // Server is already stopping, so we forcefully exit. +@@ -251,7 +304,7 @@ impl PumpkinServer { ) -> Self { let server = Server::new(basic_config, advanced_config, vanilla_data).await; @@ -1350,7 +1412,7 @@ index 7110bc9e4..3438afb65 100644 adjust_file_descriptor_limit(); let rcon = server.advanced_config.networking.rcon.clone(); -@@ -320,19 +332,30 @@ impl PumpkinServer { +@@ -320,19 +373,30 @@ impl PumpkinServer { None }; @@ -1383,7 +1445,7 @@ index 7110bc9e4..3438afb65 100644 let (bedrock_status, ice_socket) = Self::bind_bedrock_status(&server).await; let nethernet_listener = Self::bind_nethernet(&server, ice_socket).await; -@@ -345,6 +368,15 @@ impl PumpkinServer { +@@ -345,6 +409,15 @@ impl PumpkinServer { } } @@ -1399,7 +1461,7 @@ index 7110bc9e4..3438afb65 100644 async fn bind_nethernet( server: &Arc, ice_socket: Option, -@@ -441,6 +473,7 @@ impl PumpkinServer { +@@ -441,6 +514,7 @@ impl PumpkinServer { } pub async fn start(&self) { @@ -1407,7 +1469,7 @@ index 7110bc9e4..3438afb65 100644 if self.server.advanced_config.commands.use_console && let Some((wrapper, _, _)) = LOGGER_IMPL.wait() { -@@ -527,6 +560,7 @@ impl PumpkinServer { +@@ -527,6 +601,7 @@ impl PumpkinServer { info!("Completed save!"); @@ -1415,141 +1477,7 @@ index 7110bc9e4..3438afb65 100644 if let Some((wrapper, _, _)) = LOGGER_IMPL.wait() && let Some(rl) = wrapper.take_readline() { -@@ -534,6 +568,72 @@ impl PumpkinServer { - } - } - -+ /// Run the normal Java client lifecycle for a host-provided byte stream. -+ pub async fn serve_connection(&self, connection: S, client_addr: SocketAddr, client_id: u64) -+ where -+ S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Sync + Unpin + 'static, -+ { -+ let server_clone = self.server.clone(); -+ let packet_limiter = PacketRateLimiter::from_config( -+ &server_clone.advanced_config.networking.java.packet_limiter, -+ ); -+ let mut pending = -+ PendingConnection::from_stream(connection, client_addr, client_id, packet_limiter); -+ let login_result = pending.handle_login_sequence(&server_clone).await; -+ -+ match login_result { -+ PacketHandlerResult::Stop => { -+ pending.close(); -+ } -+ PacketHandlerResult::ReadyToPlay(profile, config) => { -+ let mut java_client = -+ JavaClient::from_pending(pending, profile.clone(), config.clone()); -+ java_client.start_outgoing_packet_task(); -+ -+ if let Some((player, world)) = server_clone.add_player( -+ Arc::new(ClientPlatform::Java(java_client)), -+ profile, -+ Some(config), -+ ) { -+ if let ClientPlatform::Java(client) = player.client.as_ref() { -+ client.set_player(player.clone()); -+ } -+ world -+ .spawn_java_player(&server_clone.basic_config, &player, &server_clone) -+ .await; -+ -+ if let ClientPlatform::Java(client) = player.client.as_ref() { -+ client.progress_player_packets(&player, &server_clone).await; -+ -+ // Close when done -+ client.close(); -+ client.await_tasks().await; -+ } -+ player.remove().await; -+ server_clone.remove_player(&player); -+ if let Err(e) = server_clone -+ .player_data_storage -+ .handle_player_leave(&player) -+ { -+ error!("Failed to save player data on disconnect: {e}"); -+ } -+ if let Err(e) = server_clone.advancement_manager.save_player(&player).await { -+ error!("Failed to save player advancement on disconnect: {e}"); -+ } -+ } -+ } -+ } -+ } -+ -+ fn clone_for_connection(&self) -> Self { -+ Self { -+ server: self.server.clone(), -+ tcp_listener: None, -+ bedrock_status: None, -+ nethernet_listener: None, -+ } -+ } -+ - #[allow(clippy::too_many_lines)] - pub async fn unified_listener_task( - &self, -@@ -559,66 +659,13 @@ impl PumpkinServer { - format!("{client_addr}") - }; - debug!("Accepted connection from Java Edition: {formatted_address} (id {client_id})"); -- let server_clone = self.server.clone(); -- -+ let server = self.clone_for_connection(); - tasks.spawn(async move { -- let packet_limiter = PacketRateLimiter::from_config( -- &server_clone.advanced_config.networking.java.packet_limiter, -- ); -- let mut pending = PendingConnection::new( -- connection, -- client_addr, -- client_id, -- packet_limiter, -- ); -- let login_result = pending.handle_login_sequence(&server_clone).await; -- -- match login_result { -- PacketHandlerResult::Stop => { -- pending.close(); -- }, -- PacketHandlerResult::ReadyToPlay(profile, config) => { -- let mut java_client = JavaClient::from_pending(pending, profile.clone(), config.clone()); -- java_client.start_outgoing_packet_task(); -- -- if let Some((player, world)) = server_clone -- .add_player(Arc::new(ClientPlatform::Java(java_client)), profile, Some(config)) -- { -- -- if let ClientPlatform::Java(client) = player.client.as_ref() { -- client.set_player(player.clone()); -- } -- world -- .spawn_java_player(&server_clone.basic_config, &player, &server_clone) -- .await; -- -- if let ClientPlatform::Java(client) = player.client.as_ref() { -- client.progress_player_packets(&player, &server_clone).await; -- -- // Close when done -- client.close(); -- client.await_tasks().await; -- } -- player.remove().await; -- server_clone.remove_player(&player); -- if let Err(e) = server_clone -- .player_data_storage -- .handle_player_leave(&player) -- { -- error!("Failed to save player data on disconnect: {e}"); -- } -- if let Err(e) = server_clone.advancement_manager -- .save_player(&player) -- .await { -- error!("Failed to save player advancement on disconnect: {e}"); -- } -- } -- }, -- } -+ server.serve_connection(connection, client_addr, client_id).await; +@@ -618,7 +693,7 @@ impl PumpkinServer { }); } Err(e) => { @@ -1558,7 +1486,7 @@ index 7110bc9e4..3438afb65 100644 if e.raw_os_error() == Some(libc::EMFILE) { error!( "Too many open files! Server reached file descriptor limit. \ -@@ -719,6 +766,7 @@ impl PumpkinServer { +@@ -719,6 +794,7 @@ impl PumpkinServer { } } @@ -1566,7 +1494,7 @@ index 7110bc9e4..3438afb65 100644 fn setup_stdin_console(server: &Arc) { let (tx, mut rx) = tokio::sync::mpsc::channel(1); let rt = tokio::runtime::Handle::current(); -@@ -759,6 +807,7 @@ fn setup_stdin_console(server: &Arc) { +@@ -759,6 +835,7 @@ fn setup_stdin_console(server: &Arc) { }); } @@ -1574,7 +1502,7 @@ index 7110bc9e4..3438afb65 100644 fn setup_console(mut rl: Editor, server: Arc) { let (tx, mut rx) = tokio::sync::mpsc::channel(1); let (tx_reply, mut rx_reply) = tokio::sync::mpsc::channel(1); -@@ -839,7 +888,7 @@ fn scrub_address(ip: &str) -> String { +@@ -839,7 +916,7 @@ fn scrub_address(ip: &str) -> String { .collect() } @@ -1584,7 +1512,7 @@ index 7110bc9e4..3438afb65 100644 let mut rlim = libc::rlimit { rlim_cur: 0, diff --git a/crates/pumpkin/src/logging.rs b/crates/pumpkin/src/logging.rs -index 85b51a45a..4006f6eca 100644 +index 85b51a45..4006f6ec 100644 --- a/crates/pumpkin/src/logging.rs +++ b/crates/pumpkin/src/logging.rs @@ -2,11 +2,17 @@ @@ -1741,7 +1669,7 @@ index 85b51a45a..4006f6eca 100644 type Candidate = String; diff --git a/crates/pumpkin/src/net/authentication.rs b/crates/pumpkin/src/net/authentication.rs -index f344d1978..b32c3bc2a 100644 +index f344d197..b32c3bc2 100644 --- a/crates/pumpkin/src/net/authentication.rs +++ b/crates/pumpkin/src/net/authentication.rs @@ -311,6 +311,7 @@ pub async fn lookup_profile_by_name( @@ -1771,7 +1699,7 @@ index f344d1978..b32c3bc2a 100644 uuid: Uuid, auth_config: &AuthenticationConfig, diff --git a/crates/pumpkin/src/net/bedrock/mod.rs b/crates/pumpkin/src/net/bedrock/mod.rs -index 5ea3e41ac..fd913a72b 100644 +index 5ea3e41a..fd913a72 100644 --- a/crates/pumpkin/src/net/bedrock/mod.rs +++ b/crates/pumpkin/src/net/bedrock/mod.rs @@ -1,3 +1,7 @@ @@ -1782,8 +1710,70 @@ index 5ea3e41ac..fd913a72b 100644 pub mod nethernet; pub mod play; pub mod status; +diff --git a/crates/pumpkin/src/net/bedrock/nethernet_stub.rs b/crates/pumpkin/src/net/bedrock/nethernet_stub.rs +new file mode 100644 +index 00000000..90f0989c +--- /dev/null ++++ b/crates/pumpkin/src/net/bedrock/nethernet_stub.rs +@@ -0,0 +1,56 @@ ++//! WebRTC-free stub of the NetherNet transport, compiled when the `nethernet` ++//! feature is off (e.g. the emscripten/embedded Java-only build). The types ++//! exist so the always-compiled Bedrock client code type-checks, but no Bedrock ++//! connection is ever created (the listener is never bound), so the method ++//! bodies are unreachable. ++use std::net::SocketAddr; ++use std::sync::Arc; ++ ++use bytes::Bytes; ++use pumpkin_util::p384::PublicKey; ++ ++pub type IncomingSession = (Arc, SocketAddr); ++ ++/// Stub listener: never bound when `nethernet` is off, so `accept` never yields. ++pub struct NetherNetListener { ++ _private: (), ++} ++ ++impl NetherNetListener { ++ pub async fn accept(&self) -> Option { ++ None ++ } ++ ++ pub const fn local_addr(&self) -> SocketAddr { ++ SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED), 0) ++ } ++} ++ ++/// Stub session. Never constructed when `nethernet` is off. ++pub struct NetherNetSession { ++ _private: (), ++} ++ ++impl NetherNetSession { ++ pub async fn recv(&self) -> Option { ++ None ++ } ++ ++ pub async fn send(&self, _data: Bytes) -> Result<(), String> { ++ Err("NetherNet transport is disabled in this build".to_string()) ++ } ++ ++ pub async fn send_unreliable(&self, _data: Bytes) -> Result<(), String> { ++ Err("NetherNet transport is disabled in this build".to_string()) ++ } ++ ++ pub const fn is_closed(&self) -> bool { ++ true ++ } ++ ++ pub async fn close(&self) {} ++ ++ pub const fn client_public_key(&self) -> Option<&PublicKey> { ++ None ++ } ++} diff --git a/crates/pumpkin/src/net/java/config/known_packs.rs b/crates/pumpkin/src/net/java/config/known_packs.rs -index 7e45c3db9..8a4d5af4d 100644 +index 7e45c3db..8a4d5af4 100644 --- a/crates/pumpkin/src/net/java/config/known_packs.rs +++ b/crates/pumpkin/src/net/java/config/known_packs.rs @@ -14,7 +14,7 @@ impl JavaClient { @@ -1796,67 +1786,10 @@ index 7e45c3db9..8a4d5af4d 100644 let mut packets = Vec::new(); let mut sent_dimension_type = false; diff --git a/crates/pumpkin/src/net/java/mod.rs b/crates/pumpkin/src/net/java/mod.rs -index fb0e9c901..96e3b1101 100644 +index fb0e9c90..6583eb12 100644 --- a/crates/pumpkin/src/net/java/mod.rs +++ b/crates/pumpkin/src/net/java/mod.rs -@@ -42,7 +42,6 @@ use pumpkin_util::text::TextComponent; - use pumpkin_util::version::JavaMinecraftVersion; - use tokio::{ - io::{BufReader, BufWriter}, -- net::tcp::{OwnedReadHalf, OwnedWriteHalf}, - sync::oneshot, - }; - use tokio::{ -@@ -53,6 +52,9 @@ use tokio_util::sync::CancellationToken; - use tokio_util::task::TaskTracker; - use tracing::{debug, error, warn}; - -+pub type ConnectionReader = Box; -+pub type ConnectionWriter = Box; -+ - pub mod config; - pub mod handshake; - pub mod login; -@@ -103,9 +105,9 @@ pub struct JavaClient { - /// A high-priority queue of serialized packets to send to the network. - outgoing_packet_priority_recv: Option>, - /// The packet encoder for outgoing packets. -- network_writer: std::sync::Mutex>>>, -+ network_writer: std::sync::Mutex>>>, - /// The packet decoder for incoming packets. -- network_reader: std::sync::Mutex>>>, -+ network_reader: std::sync::Mutex>>>, - /// Keep Alive: - /// - /// Whether we are waiting for a response after sending a keep alive packet. -@@ -155,10 +157,10 @@ fn take_frame_batch(packets: &mut VecDeque) -> Vec>, -+ mut writer: TCPNetworkEncoder>, - batch: &[OutgoingPacket], - ) -> ( -- TCPNetworkEncoder>, -+ TCPNetworkEncoder>, - Vec, - Option, - ) { -@@ -174,11 +176,11 @@ fn frame_packet_batch( - } - - async fn frame_batch_maybe_offload( -- writer: TCPNetworkEncoder>, -+ writer: TCPNetworkEncoder>, - packet_batch: Vec, - ) -> Result< - ( -- TCPNetworkEncoder>, -+ TCPNetworkEncoder>, - Vec, - Vec, - Option, -@@ -190,7 +192,7 @@ async fn frame_batch_maybe_offload( +@@ -190,7 +190,7 @@ async fn frame_batch_maybe_offload( .any(|packet| writer.is_compressing_packet(&packet.data)); if needs_offload { @@ -1865,7 +1798,7 @@ index fb0e9c901..96e3b1101 100644 let (writer, frame, frame_err) = frame_packet_batch(writer, &packet_batch); (writer, packet_batch, frame, frame_err) }) -@@ -406,7 +408,7 @@ impl JavaClient { +@@ -406,7 +406,7 @@ impl JavaClient { let version = self.version.load(); let (tx, rx) = oneshot::channel(); @@ -1874,77 +1807,8 @@ index fb0e9c901..96e3b1101 100644 let mut serialized = Vec::with_capacity(valid_chunks.len()); for chunk in valid_chunks { let mut buf = Vec::with_capacity(32 * 1024); -@@ -532,7 +534,7 @@ impl JavaClient { - - pub async fn get_packet_with_reader( - &self, -- network_reader: &mut TCPNetworkDecoder>, -+ network_reader: &mut TCPNetworkDecoder>, - ) -> Option { - tokio::select! { - () = self.await_close_interrupt() => { -diff --git a/crates/pumpkin/src/net/java/pending.rs b/crates/pumpkin/src/net/java/pending.rs -index 2522573e0..a627b07b8 100644 ---- a/crates/pumpkin/src/net/java/pending.rs -+++ b/crates/pumpkin/src/net/java/pending.rs -@@ -23,10 +23,7 @@ use pumpkin_protocol::{ - use pumpkin_util::{Hand, text::TextComponent, version::JavaMinecraftVersion}; - use tokio::{ - io::{BufReader, BufWriter}, -- net::{ -- TcpStream, -- tcp::{OwnedReadHalf, OwnedWriteHalf}, -- }, -+ net::TcpStream, - }; - use tokio_util::sync::CancellationToken; - use tracing::{debug, error, warn}; -@@ -40,7 +37,7 @@ use crate::{ - server::Server, - }; - --use super::JavaClient; -+use super::{ConnectionReader, ConnectionWriter, JavaClient}; - - const BRAND_CHANNEL_PREFIX: &str = "minecraft:brand"; - -@@ -61,8 +58,8 @@ pub struct PendingConnection { - pub version: AtomicCell, - pub connection_state: AtomicCell, - pub close_token: CancellationToken, -- pub network_writer: TCPNetworkEncoder>, -- pub network_reader: TCPNetworkDecoder>, -+ pub network_writer: TCPNetworkEncoder>, -+ pub network_reader: TCPNetworkDecoder>, - pub gameprofile: Option, - pub config: Option, - pub brand: Option, -@@ -78,7 +75,22 @@ impl PendingConnection { - id: u64, - packet_limiter: PacketRateLimiter, - ) -> Self { -- let (read, write) = tcp_stream.into_split(); -+ Self::from_stream(tcp_stream, address, id, packet_limiter) -+ } -+ -+ /// Accept an injected byte stream while retaining the native TCP constructor. -+ pub fn from_stream( -+ stream: S, -+ address: SocketAddr, -+ id: u64, -+ packet_limiter: PacketRateLimiter, -+ ) -> Self -+ where -+ S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Sync + Unpin + 'static, -+ { -+ let (read, write) = tokio::io::split(stream); -+ let read: ConnectionReader = Box::new(read); -+ let write: ConnectionWriter = Box::new(write); - Self { - id, - address, diff --git a/crates/pumpkin/src/net/java/play/chat_message.rs b/crates/pumpkin/src/net/java/play/chat_message.rs -index 589277311..ba28c7450 100644 +index 58927731..ba28c745 100644 --- a/crates/pumpkin/src/net/java/play/chat_message.rs +++ b/crates/pumpkin/src/net/java/play/chat_message.rs @@ -266,7 +266,7 @@ impl JavaClient { @@ -1957,7 +1821,7 @@ index 589277311..ba28c7450 100644 let verifying_key = VerifyingKey::::new(key.clone()); verifying_key.verify(&signable, &key_signature).is_ok() diff --git a/crates/pumpkin/src/plugin/loader/mod.rs b/crates/pumpkin/src/plugin/loader/mod.rs -index fa7f8c5ab..61bbe63f1 100644 +index fa7f8c5a..61bbe63f 100644 --- a/crates/pumpkin/src/plugin/loader/mod.rs +++ b/crates/pumpkin/src/plugin/loader/mod.rs @@ -1,8 +1,12 @@ @@ -1983,7 +1847,7 @@ index fa7f8c5ab..61bbe63f1 100644 WasmInitializationError(#[from] PluginInitError), } diff --git a/crates/pumpkin/src/plugin/mod.rs b/crates/pumpkin/src/plugin/mod.rs -index b686898d0..33ed73b36 100644 +index b686898d..33ed73b3 100644 --- a/crates/pumpkin/src/plugin/mod.rs +++ b/crates/pumpkin/src/plugin/mod.rs @@ -1,6 +1,9 @@ @@ -2181,7 +2045,7 @@ index b686898d0..33ed73b36 100644 #[expect(clippy::result_unit_err)] diff --git a/crates/pumpkin/src/server/mod.rs b/crates/pumpkin/src/server/mod.rs -index 28690a867..5f58b8b58 100644 +index 28690a86..5f58b8b5 100644 --- a/crates/pumpkin/src/server/mod.rs +++ b/crates/pumpkin/src/server/mod.rs @@ -64,6 +64,7 @@ pub mod ticker; @@ -2235,7 +2099,7 @@ index 28690a867..5f58b8b58 100644 self.scheduled_functions.tick( self, diff --git a/crates/pumpkin/src/server/scheduler.rs b/crates/pumpkin/src/server/scheduler.rs -index e2056b48a..f6221d64a 100644 +index e2056b48..f6221d64 100644 --- a/crates/pumpkin/src/server/scheduler.rs +++ b/crates/pumpkin/src/server/scheduler.rs @@ -1,3 +1,4 @@ @@ -2300,7 +2164,7 @@ index e2056b48a..f6221d64a 100644 #[must_use] pub fn new() -> Self { diff --git a/crates/pumpkin/src/server/ticker.rs b/crates/pumpkin/src/server/ticker.rs -index ae45c2b05..1f35b272b 100644 +index ae45c2b0..1f35b272 100644 --- a/crates/pumpkin/src/server/ticker.rs +++ b/crates/pumpkin/src/server/ticker.rs @@ -16,9 +16,14 @@ use tracing::debug; @@ -2386,7 +2250,7 @@ index ae45c2b05..1f35b272b 100644 if STOP_INTERRUPT.is_cancelled() { break 'ticker; diff --git a/crates/pumpkin/src/world/portal/mod.rs b/crates/pumpkin/src/world/portal/mod.rs -index aeadba752..85bf10aff 100644 +index aeadba75..85bf10af 100644 --- a/crates/pumpkin/src/world/portal/mod.rs +++ b/crates/pumpkin/src/world/portal/mod.rs @@ -92,6 +92,7 @@ impl PortalType { @@ -2405,65 +2269,3 @@ index aeadba752..85bf10aff 100644 if let Ok(handle) = tokio::runtime::Handle::try_current() { tokio::task::block_in_place(|| { handle.block_on(async { -diff --git a/crates/pumpkin/src/net/bedrock/nethernet_stub.rs b/crates/pumpkin/src/net/bedrock/nethernet_stub.rs -new file mode 100644 -index 000000000..90f0989c4 ---- /dev/null -+++ b/crates/pumpkin/src/net/bedrock/nethernet_stub.rs -@@ -0,0 +1,56 @@ -+//! WebRTC-free stub of the NetherNet transport, compiled when the `nethernet` -+//! feature is off (e.g. the emscripten/embedded Java-only build). The types -+//! exist so the always-compiled Bedrock client code type-checks, but no Bedrock -+//! connection is ever created (the listener is never bound), so the method -+//! bodies are unreachable. -+use std::net::SocketAddr; -+use std::sync::Arc; -+ -+use bytes::Bytes; -+use pumpkin_util::p384::PublicKey; -+ -+pub type IncomingSession = (Arc, SocketAddr); -+ -+/// Stub listener: never bound when `nethernet` is off, so `accept` never yields. -+pub struct NetherNetListener { -+ _private: (), -+} -+ -+impl NetherNetListener { -+ pub async fn accept(&self) -> Option { -+ None -+ } -+ -+ pub const fn local_addr(&self) -> SocketAddr { -+ SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED), 0) -+ } -+} -+ -+/// Stub session. Never constructed when `nethernet` is off. -+pub struct NetherNetSession { -+ _private: (), -+} -+ -+impl NetherNetSession { -+ pub async fn recv(&self) -> Option { -+ None -+ } -+ -+ pub async fn send(&self, _data: Bytes) -> Result<(), String> { -+ Err("NetherNet transport is disabled in this build".to_string()) -+ } -+ -+ pub async fn send_unreliable(&self, _data: Bytes) -> Result<(), String> { -+ Err("NetherNet transport is disabled in this build".to_string()) -+ } -+ -+ pub const fn is_closed(&self) -> bool { -+ true -+ } -+ -+ pub async fn close(&self) {} -+ -+ pub const fn client_public_key(&self) -> Option<&PublicKey> { -+ None -+ } -+} diff --git a/patches/wasm-bindgen-emscripten-closures.patch b/patches/wasm-bindgen-emscripten-closures.patch deleted file mode 100644 index 88700c3..0000000 --- a/patches/wasm-bindgen-emscripten-closures.patch +++ /dev/null @@ -1,30 +0,0 @@ -Subject: [PATCH] Escape Emscripten closure finalizer postsets - -The closure finalizer is emitted inside an Emscripten JavaScript string. -Interpolating its destructor expression directly can leave quotes and -backslashes unescaped, producing invalid generated JavaScript. - -Build the complete postset first, then format it as an escaped string -literal. This preserves the finalizer expression and the fallback used -when FinalizationRegistry is unavailable. - -Base-commit: 4b69f3b3ba4212c857be6854f77fa5aec8b62871 - ---- -diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs -index 4486b980..06d4a28a 100644 ---- a/crates/cli-support/src/js/mod.rs -+++ b/crates/cli-support/src/js/mod.rs -@@ -4326,10 +4326,11 @@ if (require('worker_threads').isMainThread) {{ - }; - - if matches!(self.config.mode, OutputMode::Emscripten) { -+ let postset = format!("CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') ? {{ register: () => {{}}, unregister: () => {{}} }} : new FinalizationRegistry({prevent_stale});"); - format!( - "addToLibrary({{ - $CLOSURE_DTORS: {{}}, -- $CLOSURE_DTORS__postset: \"CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') ? {{ register: () => {{}}, unregister: () => {{}} }} : new FinalizationRegistry({prevent_stale});\" -+ $CLOSURE_DTORS__postset: {postset:?} - }});\n" - ) - } else { diff --git a/patches/wasm-streams-rlib.patch b/patches/wasm-streams-rlib.patch deleted file mode 100644 index 8afa042..0000000 --- a/patches/wasm-streams-rlib.patch +++ /dev/null @@ -1,25 +0,0 @@ -Subject: [PATCH] Build wasm-streams as an rlib for Emscripten - -The embedded application links wasm-streams into its final WebAssembly -module. Declaring both rlib and cdylib also makes Cargo attempt a standalone -library link, which is incompatible with this static Emscripten build. - -Build only the rlib so the application supplies the final link and host -bindings. The Rust stream adapter API is unchanged. - -Base-commit: 35665f7b1da830b5ac51b4c6c3ff13f5c1a09ccb - ---- -diff --git a/Cargo.toml b/Cargo.toml -index c57b1c6..52f6805 100644 ---- a/Cargo.toml -+++ b/Cargo.toml -@@ -16,7 +16,7 @@ exclude = [ - ] - - [lib] --crate-type = ["cdylib", "rlib"] -+crate-type = ["rlib"] - - [dependencies] - js-sys = "^0.3.85" diff --git a/patches/workers-rs-emscripten-toolchain.patch b/patches/workers-rs-emscripten-toolchain.patch deleted file mode 100644 index 425e670..0000000 --- a/patches/workers-rs-emscripten-toolchain.patch +++ /dev/null @@ -1,184 +0,0 @@ -Subject: [PATCH] Embed workers-rs with the hosted Emscripten runtime - -The embedding host creates each Emscripten module and owns its lifetime. -The SDK's standalone wasm-bindgen loader and recovery hook cannot -reinitialize those instances, so leave initialization and panic handling -to the host on this target. - -Expose an Emscripten-only adapter that drives promise exports on one hosted -Tokio runtime per module and converts injected sockets into SDK streams. -It uses the hosted-runtime Tokio fork selected by the application. Align -the wasm-bindgen, js-sys, and web-sys version family with the pinned -Emscripten-enabled wasm-bindgen CLI so their generated bindings agree. - -Flush outstanding socket writes before closing the WritableStream. This -satisfies AsyncWrite's shutdown contract and releases the writer lock -before close, including when a pending write completes asynchronously. - -Base-commit: 7db011ec97658a5d907f3e3102028ce86c044f19 - ---- -diff --git a/Cargo.toml b/Cargo.toml -index 4e08234..9888881 100644 ---- a/Cargo.toml -+++ b/Cargo.toml -@@ -28,7 +28,7 @@ chrono = { version = "0.4.41", default-features = false, features = [ - futures-channel = "0.3.31" - futures-util = { version = "0.3.31", default-features = false } - http = "1.3" --js-sys = { version = "0.3.104" } -+js-sys = { version = "0.3.103" } - serde = { version = "1.0.164", features = ["derive"] } - strum = { version = "0.28", features = ["derive"] } - serde_json = "1.0.140" -@@ -37,14 +37,14 @@ syn = "3.0.3" - trybuild = "1.0" - proc-macro2 = "1.0.60" - quote = "1.0.28" --wasm-bindgen = { version = "0.2.127" } --wasm-bindgen-cli-support = { version = "0.2.127" } --wasm-bindgen-futures = { version = "0.4.77" } --wasm-bindgen-macro-support = { version = "0.2.127" } --wasm-bindgen-shared = { version = "0.2.127" } --wasm-bindgen-test = { version = "0.3.77" } -+wasm-bindgen = { version = "0.2.126" } -+wasm-bindgen-cli-support = { version = "0.2.126" } -+wasm-bindgen-futures = { version = "0.4.76" } -+wasm-bindgen-macro-support = { version = "0.2.126" } -+wasm-bindgen-shared = { version = "0.2.126" } -+wasm-bindgen-test = { version = "0.3.76" } - wasm-streams = { version = "0.6.0" } --web-sys = { version = "0.3.104", features = [ -+web-sys = { version = "0.3.103", features = [ - "AbortController", - "AbortSignal", - "BinaryType", -@@ -106,11 +106,11 @@ opt-level = "z" - # These are local patches we use to test against local wasm bindgen - # We always align on the exact stable wasm bindgen version for releases - [patch.crates-io] --js-sys = { version = "0.3.104", path = './wasm-bindgen/crates/js-sys' } --wasm-bindgen = { version = "0.2.127", path = './wasm-bindgen' } --wasm-bindgen-cli-support = { version = "0.2.127", path = "./wasm-bindgen/crates/cli-support" } --wasm-bindgen-futures = { version = "0.4.77", path = './wasm-bindgen/crates/futures' } --wasm-bindgen-macro-support = { version = "0.2.127", path = "./wasm-bindgen/crates/macro-support" } --wasm-bindgen-shared = { version = "0.2.127", path = "./wasm-bindgen/crates/shared" } --wasm-bindgen-test = { version = "0.3.77", path = "./wasm-bindgen/crates/test" } --web-sys = { version = "0.3.104", path = './wasm-bindgen/crates/web-sys' } -+js-sys = { version = "0.3.103", path = './wasm-bindgen/crates/js-sys' } -+wasm-bindgen = { version = "0.2.126", path = './wasm-bindgen' } -+wasm-bindgen-cli-support = { version = "0.2.126", path = "./wasm-bindgen/crates/cli-support" } -+wasm-bindgen-futures = { version = "0.4.76", path = './wasm-bindgen/crates/futures' } -+wasm-bindgen-macro-support = { version = "0.2.126", path = "./wasm-bindgen/crates/macro-support" } -+wasm-bindgen-shared = { version = "0.2.126", path = "./wasm-bindgen/crates/shared" } -+wasm-bindgen-test = { version = "0.3.76", path = "./wasm-bindgen/crates/test" } -+web-sys = { version = "0.3.103", path = './wasm-bindgen/crates/web-sys' } -diff --git a/worker/Cargo.toml b/worker/Cargo.toml -index 573a8f2..d286f02 100644 ---- a/worker/Cargo.toml -+++ b/worker/Cargo.toml -@@ -45,6 +45,9 @@ web-sys.workspace = true - worker-macros.workspace = true - worker-sys.workspace = true - -+[target.'cfg(target_os = "emscripten")'.dependencies] -+tokio = { version = "1.28", default-features = false, features = ["rt", "time", "net"] } -+ - [features] - queue = ["worker-macros/queue", "worker-sys/queue"] - d1 = ["worker-sys/d1"] -diff --git a/worker/src/lib.rs b/worker/src/lib.rs -index bc2cdc9..72097fe 100644 ---- a/worker/src/lib.rs -+++ b/worker/src/lib.rs -@@ -223,6 +223,11 @@ mod container; - mod context; - mod cors; - pub mod crypto; -+#[cfg(target_os = "emscripten")] -+pub mod emscripten; -+// The embedding host owns Emscripten module instances and panic handling. -+// SDK reinitialization targets wasm-bindgen's standalone loader instead. -+#[cfg(not(target_os = "emscripten"))] - mod init; - // Require pub module for macro export - #[cfg(feature = "d1")] -diff --git a/worker/src/socket.rs b/worker/src/socket.rs -index bc46cd4..a32d2bd 100644 ---- a/worker/src/socket.rs -+++ b/worker/src/socket.rs -@@ -289,6 +289,12 @@ impl AsyncWrite for Socket { - } - - fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { -+ // An outstanding write owns the stream's writer lock. Finish it before -+ // closing the WritableStream, as required by AsyncWrite::poll_shutdown. -+ match self.as_mut().poll_flush(cx) { -+ Poll::Ready(Ok(())) => {} -+ pending_or_error => return pending_or_error, -+ } - fn handle_future(cx: &mut Context<'_>, mut fut: JsFuture) -> (Closing, Poll>) { - match fut.poll_unpin(cx) { - Poll::Pending => (Closing::Pending(fut), Poll::Pending), -diff --git a/worker/src/emscripten.rs b/worker/src/emscripten.rs -new file mode 100644 -index 0000000..e3516ed ---- /dev/null -+++ b/worker/src/emscripten.rs -@@ -0,0 +1,56 @@ -+//! Promise exports driven by the Emscripten hosted Tokio runtime. -+//! -+//! Each modularized Wasm instance owns its runtime. This adapter keeps Tokio -+//! timers and I/O on that executor when JavaScript invokes an async Rust export. -+//! It requires the Emscripten-enabled Tokio toolchain. -+ -+use std::future::Future; -+use wasm_bindgen::prelude::*; -+ -+thread_local! { -+ static RUNTIME: tokio::runtime::HostedRuntime = tokio::runtime::Builder::new_current_thread() -+ .enable_all() -+ .build_hosted_event_loop_runtime() -+ .expect("hosted runtime"); -+} -+ -+/// All exports in one wasm instance use the same runtime. Emscripten's module -+/// factory gives each world its own wasm memory, thread locals, and callbacks. -+pub fn future_to_promise(future: F) -> js_sys::Promise -+where -+ F: Future> + 'static, -+{ -+ // Tokio catches future panics and delivers JoinError to the completion callback. -+ let mut future = std::panic::AssertUnwindSafe(Some(future)); -+ js_sys::Promise::new(&mut move |resolve, reject| { -+ let future = future.take().expect("Promise executor runs once"); -+ RUNTIME.with(|runtime| { -+ runtime.schedule(future, move |result| { -+ let result = result -+ .unwrap_or_else(|error| Err(js_sys::Error::new(&error.to_string()).into())); -+ match result { -+ Ok(value) => { -+ let _ = resolve.call1(&JsValue::UNDEFINED, &value); -+ } -+ Err(error) => { -+ let _ = reject.call1(&JsValue::UNDEFINED, &error); -+ } -+ } -+ }); -+ runtime.drive(); -+ }); -+ }) -+} -+ -+pub fn js_error(error: impl std::fmt::Display) -> JsValue { -+ js_sys::Error::new(&error.to_string()).into() -+} -+ -+/// Convert the socket passed to an Emscripten export into the SDK's async stream. -+pub fn socket_from_value(value: JsValue) -> Result { -+ use crate::FromSocket; -+ crate::Socket::from_raw(value.unchecked_into()).map_err(|error| { -+ let error: Box = error.into(); -+ js_error(error) -+ }) -+} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 7fced56..6f67d1c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2026-07-20" +channel = "beta" profile = "minimal" targets = ["wasm32-unknown-emscripten"] diff --git a/scripts/build.sh b/scripts/build.sh index 52c7d3f..b55e92d 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -4,4 +4,4 @@ source "$(dirname "${BASH_SOURCE[0]}")/common.sh" require_node require_toolchain cd "$REPO" -cargo build --locked --release --bin pumpkin-do +worker-build --emscripten --tokio --release -- --locked diff --git a/scripts/common.sh b/scripts/common.sh index acc92a8..5ed03a8 100755 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -4,6 +4,11 @@ set -euo pipefail REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" WORK="$REPO/.work" NODE="${NODE:-node}" +# Setup provisions the Emscripten frontend and backend and the worker-build and +# wasm-bindgen CLIs under .work/. worker-build reads the toolchain from +# EMSCRIPTEN, EMSDK and WASM_BINDGEN_BIN. +EMSCRIPTEN="$WORK/emscripten" +BIN="$WORK/bin" require_node() { local major @@ -15,17 +20,23 @@ require_node() { } require_toolchain() { - if [ ! -f "$WORK/emscripten/.emscripten_cf" ] || - [ ! -x "$WORK/bin/wasm-bindgen" ] || + if [ ! -x "$EMSCRIPTEN/emcc" ] || + [ ! -x "$BIN/worker-build" ] || + [ ! -x "$BIN/wasm-bindgen" ] || [ ! -f "$WORK/pumpkin/crates/pumpkin/Cargo.toml" ] || + [ ! -f "$WORK/tokio/tokio/Cargo.toml" ] || [ ! -f "$WORK/workers-rs/worker/Cargo.toml" ]; then echo "error: run bash scripts/setup.sh first." >&2 exit 1 fi - export EM_CONFIG="$WORK/emscripten/.emscripten_cf" - export PATH="$WORK/emscripten:$WORK/bin:$PATH" - export CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_LINKER="$WORK/emscripten/emcc" + export EMSCRIPTEN + export EMSDK="${EMSDK:-$WORK/emsdk}" + export WASM_BINDGEN_BIN="$BIN/wasm-bindgen" + export PATH="$BIN:$PATH" export CARGO_TARGET_DIR="$REPO/target/workers" + # rustc's LLVM const emission recurses deeply on pumpkin-data's generated tables + # and overflows its default 8 MiB compile-thread stack on current toolchains. + export RUST_MIN_STACK="${RUST_MIN_STACK:-268435456}" } require_packages() { diff --git a/scripts/setup.sh b/scripts/setup.sh index a640927..18dfbe3 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Clone pinned sources, apply dependency patches, and build the host toolchain. +# Clone pinned sources, apply dependency patches, and provision the toolchain. set -euo pipefail source "$(dirname "${BASH_SOURCE[0]}")/common.sh" @@ -12,7 +12,7 @@ mkdir -p "$WORK" checkout() { # name url branch commit local name="$1" url="$2" branch="$3" commit="$4" dir="$WORK/$1" - if [ ! -d "$dir/.git" ]; then + if [ ! -d "$dir/.git" ] && [ ! -f "$dir/.git" ]; then if [ -e "$dir" ]; then echo "error: $dir exists but is not a Git checkout." >&2 exit 1 @@ -43,53 +43,57 @@ apply_patch() { } echo "==> Pinned dependencies" -checkout emscripten https://github.com/guybedford/emscripten cf 21166256c4c4d73d39b3685c8973d7cbe427ce8c -checkout wasm-bindgen https://github.com/guybedford/wasm-bindgen emscripten-non-identifier-names 4b69f3b3ba4212c857be6854f77fa5aec8b62871 -checkout tokio https://github.com/guybedford/tokio emscripten-layering 7c1d4977c510866775ed6164b58b2218a6a2955b -checkout libc https://github.com/guybedford/libc libc-0.2-emscripten 4091fe0b0dc5f9c1a27bed75be1ff02bb27e756d -checkout ring https://github.com/guybedford/ring emscripten 6671f7cfbb13f249b571ffa6326275a8596e0ca2 checkout pumpkin https://github.com/Pumpkin-MC/Pumpkin master b5b9b9d7010e793806a83c495af223c67e1d35ee -checkout workers-rs https://github.com/ThomasRubini/workers-rs connect-bindings 7db011ec97658a5d907f3e3102028ce86c044f19 -checkout wasm-streams https://github.com/MattiasBuelens/wasm-streams main 35665f7b1da830b5ac51b4c6c3ff13f5c1a09ccb -apply_patch wasm-bindgen wasm-bindgen-emscripten-closures.patch +checkout tokio https://github.com/guybedford/tokio emscripten-event-loop-host 067f92b60cbba5c9fea585806af4b07fd838427e +# cloudflare/workers-rs#1061; its wasm-bindgen submodule (wasm-bindgen/wasm-bindgen +# gbedford/emscripten-stack) supplies every wasm-bindgen crate. +checkout workers-rs https://github.com/cloudflare/workers-rs gbedford/worker-build-emscripten f4546f5379a933fbf3607bce205a8b372811d465 +WASM_BINDGEN_SUBMODULE=d48e82c4b3f7a182223fb1b1c7b99f59cf9b7c17 +if [ "$(git -C "$WORK/workers-rs/wasm-bindgen" rev-parse HEAD 2>/dev/null)" != "$WASM_BINDGEN_SUBMODULE" ]; then + git -C "$WORK/workers-rs" submodule update --init wasm-bindgen +fi apply_patch pumpkin pumpkin-emscripten.patch apply_patch pumpkin pumpkin-memory.patch -apply_patch workers-rs workers-rs-emscripten-toolchain.patch -apply_patch wasm-streams wasm-streams-rlib.patch if [ "${1:-}" = --sources-only ]; then exit 0; fi -echo "==> Pinned Rust toolchain" +echo "==> Rust toolchain" RUST_CHANNEL="$(python3 -c 'import tomllib,sys; print(tomllib.load(open(sys.argv[1],"rb"))["toolchain"]["channel"])' "$REPO/rust-toolchain.toml")" rustup toolchain install "$RUST_CHANNEL" --profile minimal --target wasm32-unknown-emscripten --no-self-update -echo "==> Emscripten frontend and Homebrew compiler backend" -EM_PREFIX="$(brew --prefix emscripten)" || { - echo "error: install the backend with 'brew install emscripten'." >&2 - exit 1 -} -# Resolve NODE before entering the checkout (it may be a relative path). -NODE_PATH="$("$NODE" -p 'process.execPath')" -export PATH="$(dirname "$NODE_PATH"):$PATH" -(cd "$WORK/emscripten" && npm ci --no-audit --no-fund && python3 bootstrap.py) -python3 - "$WORK/emscripten/.emscripten_cf" "$EM_PREFIX" "$NODE_PATH" <<'PY' -from pathlib import Path -import sys -config, prefix, node = sys.argv[1:] -Path(config).write_text( - f"LLVM_ROOT={prefix + '/libexec/llvm/bin'!r}\n" - f"BINARYEN_ROOT={prefix + '/libexec/binaryen'!r}\n" - f"NODE_JS={node!r}\n" -) -PY +echo "==> wasm-bindgen CLI" +WASM_BINDGEN_VERSION="$(python3 -c 'import tomllib,sys; print(tomllib.load(open(sys.argv[1],"rb"))["dependencies"]["wasm-bindgen"])' "$REPO/Cargo.toml")" +if [ "$("$BIN/wasm-bindgen" --version 2>/dev/null || true)" != "wasm-bindgen $WASM_BINDGEN_VERSION" ]; then + cargo "+$RUST_CHANNEL" install --force --locked wasm-bindgen-cli --version "$WASM_BINDGEN_VERSION" --root "$WORK" +fi + +echo "==> worker-build" +# Built from the workers-rs checkout (cloudflare/workers-rs#1061) for the host, +# overriding the wasm target .cargo/config.toml sets for the package path. +if [ "$(cat "$BIN/.worker-build-rev" 2>/dev/null)" != "$(git -C "$WORK/workers-rs" rev-parse HEAD)" ]; then + HOST="$(rustc "+$RUST_CHANNEL" -vV | sed -n 's/^host: //p')" + cargo "+$RUST_CHANNEL" install --force --locked --target "$HOST" --path "$WORK/workers-rs/worker-build" --root "$WORK" + git -C "$WORK/workers-rs" rev-parse HEAD > "$BIN/.worker-build-rev" +fi -echo "==> Patched wasm-bindgen CLI" -# Upstream does not commit this workspace lockfile. Preserve the proven CLI resolution. -cp "$REPO/toolchain/wasm-bindgen.Cargo.lock" "$WORK/wasm-bindgen/Cargo.lock" -# Override nested dependency toolchain files with the repository's dated nightly. -HOST_TARGET="$(rustc "+$RUST_CHANNEL" -vV | sed -n 's/^host: //p')" -(cd "$WORK/wasm-bindgen" && cargo "+$RUST_CHANNEL" build --locked --release -p wasm-bindgen-cli \ - --target "$HOST_TARGET" --target-dir "$WORK/wasm-bindgen/target") -mkdir -p "$WORK/bin" -cp "$WORK/wasm-bindgen/target/$HOST_TARGET/release/wasm-bindgen" "$WORK/bin/wasm-bindgen" +echo "==> Emscripten" +# Frontend: upstream main plus the pending epoll listener PR. The backend (LLVM, Binaryen, Node) is the emscripten-releases build +# the frontend's main is paired with, installed through emsdk; EMSDK selects an +# activated emsdk instead. worker-build reads both from EMSCRIPTEN and EMSDK +# (scripts/common.sh) and uses the frontend unpatched. +EMSDK_RELEASE=8324e94759a0292e342577007021b4b47106333b +checkout emscripten https://github.com/guybedford/emscripten cf-final 462303990a2f62e2c065dcc3ba0794cb7bb2e6c4 +NODE_PATH="$("$NODE" -p 'process.execPath')" +(cd "$EMSCRIPTEN" && PATH="$(dirname "$NODE_PATH"):$PATH" npm ci --no-audit --no-fund && python3 bootstrap.py) +if [ -z "${EMSDK:-}" ]; then + checkout emsdk https://github.com/emscripten-core/emsdk main 5eb0bde7585670252e8ba05e9d361627bffd08b5 + if [ "$(cat "$WORK/emsdk/upstream/.emsdk_version" 2>/dev/null)" != "releases-$EMSDK_RELEASE-64bit" ]; then + (cd "$WORK/emsdk" && ./emsdk install "$EMSDK_RELEASE" && ./emsdk activate "$EMSDK_RELEASE") + fi + EMSDK="$WORK/emsdk" +fi +if [ ! -x "$EMSDK/upstream/bin/clang" ] || [ ! -x "$EMSDK/upstream/bin/wasm-opt" ]; then + echo "error: no Emscripten backend at EMSDK=$EMSDK." >&2 + exit 1 +fi echo "Setup complete. Run: bash scripts/test.sh or bash scripts/serve.sh" diff --git a/scripts/test.sh b/scripts/test.sh index 6d67bd4..5ef4a42 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -7,7 +7,4 @@ require_toolchain "$NODE" "$REPO/scripts/check-ports.mjs" 25565 8787 bash "$REPO/scripts/build.sh" cd "$REPO" -cargo build --locked --release --features test-fixtures --bin filesystem-probe -"$NODE" node_modules/wrangler/bin/wrangler.js types worker/env.d.ts --strict-vars=false -"$NODE" node_modules/typescript/bin/tsc --noEmit exec "$NODE" tests/integration.mjs diff --git a/src/config.rs b/src/config.rs index 22360a8..e9106a7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,8 +13,9 @@ pub fn configuration() -> (BasicConfiguration, AdvancedConfiguration) { } let mut adv = AdvancedConfiguration::default(); - // Offline Java server with a bounded view distance. - adv.networking.java.enabled = false; // Connections are supplied by the Durable Object. + // Offline Java server with a bounded view distance. The listener binds the + // default 0.0.0.0:25565 in the Durable Object's port table; the host routes + // inbound sockets to it by port. adv.networking.java.online_mode = false; adv.networking.java.encryption = false; adv.networking.java.view_distance = NonZero::new(3).unwrap(); diff --git a/src/host.rs b/src/host.rs new file mode 100644 index 0000000..cbb7de3 --- /dev/null +++ b/src/host.rs @@ -0,0 +1,62 @@ +//! Platform calls, each returning the platform's own Promise or value. + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(module = "/src/js/mount.js")] +extern "C" { + /// Mounts the object's SQLite storage as the world's filesystem and routes + /// NODERAWFS through it; `ROOT` is the mount path. + #[wasm_bindgen(js_name = mountStorage)] + pub fn mount_storage(storage: &JsValue); + #[wasm_bindgen(js_name = ROOT, thread_local_v2)] + pub static MOUNT_ROOT: JsValue; +} + +#[wasm_bindgen(module = "cloudflare:node")] +extern "C" { + /// Routes an inbound socket to the `net.Server` listening on its local + /// port within the current Durable Object's port table; resolves when the + /// connection closes. + #[wasm_bindgen(js_name = handleAsNodeConnection, catch)] + pub fn handle_as_node_connection(socket: &JsValue) -> Result; +} + +pub fn property(target: &JsValue, name: &str) -> Result { + js_sys::Reflect::get(target, &name.into()) +} + +pub fn method(target: &JsValue, name: &str, args: &[&JsValue]) -> Result { + let function: js_sys::Function = property(target, name)?.unchecked_into(); + let array = js_sys::Array::new(); + for arg in args { + array.push(arg); + } + js_sys::Reflect::apply(&function, target, &array) +} + +/// `promise.then(on_fulfilled, on_rejected)` on any thenable; the callbacks +/// are one-shot closures already converted with `Closure::once_into_js`. +pub fn then( + promise: &JsValue, + on_fulfilled: Option<&JsValue>, + on_rejected: Option<&JsValue>, +) -> Result { + let then: js_sys::Function = property(promise, "then")?.unchecked_into(); + then.call2( + promise, + on_fulfilled.unwrap_or(&JsValue::UNDEFINED), + on_rejected.unwrap_or(&JsValue::UNDEFINED), + ) +} + +/// `stub.connect(authority, { allowHalfOpen: true })`. +pub fn stub_connect(stub: &JsValue, authority: &str) -> Result { + let options = js_sys::Object::new(); + js_sys::Reflect::set(&options, &"allowHalfOpen".into(), &JsValue::TRUE)?; + method(stub, "connect", &[&authority.into(), &options]) +} + +pub fn js_error(error: impl std::fmt::Display) -> JsValue { + js_sys::Error::new(&error.to_string()).into() +} + diff --git a/src/js/mount.js b/src/js/mount.js new file mode 100644 index 0000000..60f6c21 --- /dev/null +++ b/src/js/mount.js @@ -0,0 +1,13 @@ +// The world's filesystem: worker-fs-mount's node:fs implementation routes paths +// under a mount to the Durable Object's SQLite storage and everything else to +// workerd's node:fs. Emscripten's NODERAWFS is rebound to it (src/workerd.js). +import { LocalDOFilesystem } from 'durable-object-fs/local'; +import { mount } from 'worker-fs-mount'; +import * as fs from 'worker-fs-mount/fs-sync'; + +export const ROOT = '/data'; + +export function mountStorage(storage) { + mount(ROOT, new LocalDOFilesystem(storage)); + globalThis.__pumpkin_fs = fs; +} diff --git a/src/main.rs b/src/main.rs index 5bee898..9245c60 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,111 +1,85 @@ mod config; +mod host; mod memory; +mod world; -use pumpkin::{data::VanillaData, PumpkinServer}; -use std::{cell::RefCell, sync::atomic::Ordering, sync::Arc}; +use host::{method, property, then}; use wasm_bindgen::prelude::*; -use worker::emscripten::{future_to_promise, js_error, socket_from_value}; +use worker::{event, Context, Env, Request, Response}; fn main() {} -thread_local! { - static FAILURE: RefCell> = const { RefCell::new(None) }; - static SERVER: RefCell>> = const { RefCell::new(None) }; - static NEXT_CLIENT: RefCell = const { RefCell::new(0) }; -} - -fn server() -> Result, JsValue> { - if let Some(error) = FAILURE.with(|failure| failure.borrow().clone()) { - return Err(js_error(error)); - } - SERVER - .with(|server| server.borrow().clone()) - .ok_or_else(|| js_error("Server is not started")) -} - -#[wasm_bindgen] -pub fn pumpkin_start() -> js_sys::Promise { - future_to_promise(async { - if SERVER.with(|server| server.borrow().is_some()) { - return Ok(JsValue::UNDEFINED); - } - std::panic::set_hook(Box::new(|info| { - FAILURE.with(|failure| { - failure.borrow_mut().get_or_insert_with(|| info.to_string()); - }); - eprintln!("RUST PANIC: {info}"); - })); - rayon::ThreadPoolBuilder::new() - .num_threads(1) - .use_current_thread() - .build_global() - .map_err(js_error)?; - let (basic, advanced) = config::configuration(); - pumpkin::init_logger(&advanced); - let server = Arc::new(PumpkinServer::new(basic, advanced, VanillaData::load()).await); - server.init_plugins().await; - SERVER.with(|slot| *slot.borrow_mut() = Some(server)); - Ok(JsValue::UNDEFINED) - }) +/// The world's Durable Object stub. +fn stub(env: &Env) -> worker::Result { + let name = env.var("WORLD_NAME")?.to_string(); + Ok(env.durable_object("WORLD")?.get_by_name(&name)?.into_rpc()) } +/// Pipes an inbound TCP connection to the world's object until either side +/// closes. worker-build exports it as the entrypoint's `connect` handler. #[wasm_bindgen] -pub fn pumpkin_connect(raw: JsValue) -> js_sys::Promise { - future_to_promise(async move { - let server = server()?; - let socket = socket_from_value(raw)?; - let id = NEXT_CLIENT.with(|slot| { - let mut id = slot.borrow_mut(); - *id += 1; - *id +pub fn connect(socket: JsValue, env: Env, _ctx: JsValue) -> Result { + let target = host::stub_connect(&stub(&env)?, &world::authority())?; + // Read/write failures are observed by the pumps; a normal peer disconnect + // must not surface as an unhandled rejection of the lifetime promise. + let swallow = Closure::::new(|_| ()).into_js_value(); + for side in [&socket, &target] { + then(&property(side, "closed")?, None, Some(&swallow))?; + } + let abort = web_sys::AbortController::new()?; + let pipe = |from: &JsValue, to: &JsValue| -> Result { + let readable: web_sys::ReadableStream = property(from, "readable")?.unchecked_into(); + let writable: web_sys::WritableStream = property(to, "writable")?.unchecked_into(); + let options = web_sys::StreamPipeOptions::new(); + options.set_signal(&abort.signal()); + let abort = abort.clone(); + let on_error = Closure::once_into_js(move |error: JsValue| -> Result { + abort.abort(); + Err(error) }); - // The SDK's socket is the injected Tokio byte stream. Its adapter handles - // backpressure, EOF and JS stream errors; no emulated TCP listener is used. - server - .serve_connection(socket, ([127, 0, 0, 1], 0).into(), id) - .await; - Ok(JsValue::UNDEFINED) - }) + then(&readable.pipe_to_with_options(&writable, &options), None, Some(&on_error)) + }; + let pumps = js_sys::Array::of2(&pipe(&socket, &target)?, &pipe(&target, &socket)?); + let close_both = Closure::once_into_js(move |results: JsValue| -> Result { + for result in js_sys::Array::from(&results).iter() { + if property(&result, "status")? != "rejected" { + continue; + } + let reason = property(&result, "reason")?; + let text = reason + .as_string() + .unwrap_or_else(|| js_sys::Error::from(reason.clone()).message().into()) + .to_lowercase(); + let expected = ["closed", "closing", "abort", "cancel", "reset", "network connection lost"]; + if !expected.iter().any(|e| text.contains(e)) { + web_sys::console::error_2(&"TCP forwarding failed".into(), &reason); + } + } + let closes = js_sys::Array::new(); + for side in [&socket, &target] { + closes.push(&method(side, "close", &[])?); + } + Ok(js_sys::Promise::all_settled(&closes).into()) + }); + then(&js_sys::Promise::all_settled(&pumps).into(), Some(&close_both), None) } -#[wasm_bindgen] -pub fn pumpkin_status() -> Result { - let server = server()?; - let result = js_sys::Object::new(); - js_sys::Reflect::set( - &result, - &"players".into(), - &(server.server.get_all_players().len() as f64).into(), - )?; - js_sys::Reflect::set( - &result, - &"ticks".into(), - &(server.server.tick_count.load(Ordering::Relaxed) as f64).into(), - )?; - js_sys::Reflect::set( - &result, - &"wasm_memory_bytes".into(), - &((core::arch::wasm32::memory_size::<0>() * 65536) as f64).into(), - )?; - for (name, bytes) in memory::heap_usage() { - js_sys::Reflect::set(&result, &name.into(), &(bytes as f64).into())?; +#[event(fetch)] +async fn fetch(request: Request, env: Env, _ctx: Context) -> worker::Result { + match request.path().as_str() { + "/health" => Response::ok(r#"{"ready":true}"#).map(json), + "/" if request.method() == worker::Method::Get => { + let status = method(&stub(&env)?, "status", &[])?; + let status = wasm_bindgen_futures::JsFuture::from(js_sys::Promise::from(status)).await?; + let body = js_sys::JSON::stringify(&status)?; + Response::ok(String::from(body)).map(json) + } + _ => Response::error("Not found", 404), } - Ok(result.into()) } -#[wasm_bindgen] -pub fn pumpkin_shutdown() -> js_sys::Promise { - future_to_promise(async { - let server = server()?; - if !server.server.get_all_players().is_empty() { - return Err(js_error( - "Disconnect clients before shutting down the world", - )); - } - pumpkin::stop_server(); - server.server.save_all().await.map_err(js_error)?; - server.server.shutdown().await; - SERVER.with(|slot| slot.borrow_mut().take()); - Ok(JsValue::UNDEFINED) - }) +fn json(response: Response) -> Response { + let headers = worker::Headers::new(); + let _ = headers.set("content-type", "application/json"); + response.with_headers(headers) } diff --git a/src/workerd.js b/src/workerd.js new file mode 100644 index 0000000..dc86445 --- /dev/null +++ b/src/workerd.js @@ -0,0 +1,54 @@ +// Emscripten library adjustments for NODERAWFS on workerd's Node filesystem. +addToLibrary({ + // Node's deprecated process.binding is absent; fs.constants is the public + // form of the same table. + $NODEFS__postset: ` + NODEFS.isWindows = false; + NODEFS.flagsForNodeMap = Object.fromEntries([ + [{{{ cDefs.O_APPEND }}}, 'O_APPEND'], + [{{{ cDefs.O_CREAT }}}, 'O_CREAT'], + [{{{ cDefs.O_EXCL }}}, 'O_EXCL'], + [{{{ cDefs.O_NOCTTY }}}, 'O_NOCTTY'], + [{{{ cDefs.O_RDONLY }}}, 'O_RDONLY'], + [{{{ cDefs.O_RDWR }}}, 'O_RDWR'], + [{{{ cDefs.O_DSYNC }}}, 'O_SYNC'], + [{{{ cDefs.O_TRUNC }}}, 'O_TRUNC'], + [{{{ cDefs.O_WRONLY }}}, 'O_WRONLY'], + [{{{ cDefs.O_NOFOLLOW }}}, 'O_NOFOLLOW'], + ].map(([flag, name]) => [flag, fs.constants[name]])); + `, + + // The host enforces permissions; workerd reports directories as 0666, which + // would otherwise fail the VFS's execute check in getdents. Once the object + // mounts its storage (src/js/mount.js), NODERAWFS reads and writes through + // worker-fs-mount's fs, which routes the mount to SQLite and the rest to + // workerd's own fs. + $workerdFs__deps: ['$FS', '$PATH_FS'], + $workerdFs__postset: () => addAtPreRun('workerdFs()'), + $workerdFs: () => { + Object.defineProperty(FS, 'ignorePermissions', { get: () => true, set() {} }); + // The working directory is the object's, not the isolate process's, so + // relative paths (which NODERAWFS otherwise leaves to the host's cwd) are + // resolved here before they reach the fs implementation. + let cwd = '/'; + FS.cwd = () => cwd; + FS.chdir = (path) => { cwd = PATH_FS.resolve(path); }; + const resolve = (path) => (typeof path === 'string' && path[0] !== '/' ? PATH_FS.resolve(path) : path); + const twoPaths = new Set(['renameSync', 'copyFileSync', 'linkSync']); + const resolving = (impl) => new Proxy(impl, { + get(target, name) { + const value = target[name]; + if (typeof value !== 'function' || !String(name).endsWith('Sync')) return value; + return (first, second, ...rest) => + value.call(target, resolve(first), twoPaths.has(name) ? resolve(second) : second, ...rest); + }, + }); + const nodeFs = fs; + fs = resolving(nodeFs); + Object.defineProperty(globalThis, '__pumpkin_fs', { + configurable: true, + get: () => nodeFs, + set: (mounted) => { fs = resolving(mounted); }, + }); + }, +}); diff --git a/src/world.rs b/src/world.rs new file mode 100644 index 0000000..fbdaf80 --- /dev/null +++ b/src/world.rs @@ -0,0 +1,280 @@ +//! The Durable Object hosting one Pumpkin server. +//! +//! `connect` is a `#[wasm_bindgen(tokio)]` export: its future runs on the +//! thread's Tokio event loop, whose wait is the host event loop, so nothing +//! blocks or suspends and the export returns a Promise. The server lifetime is +//! one such future: the first `connect` after idle runs it on the world mounted +//! from the object's storage, and it completes after the final save is synced. +//! Later connections are routed to the running listener. + +use crate::{config, host, memory}; +use host::{js_error, method, property, then}; +use pumpkin::{data::VanillaData, server::Server, PumpkinServer}; +use std::{ + cell::{Cell, RefCell}, + rc::Rc, + sync::{atomic::Ordering, Arc}, +}; +use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::JsFuture; + +pub const PORT: u16 = 25565; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +enum Phase { + Idle, + Starting, + Running, + Stopping, + Failed, +} + +impl Phase { + fn name(self) -> &'static str { + match self { + Phase::Idle => "idle", + Phase::Starting => "starting", + Phase::Running => "running", + Phase::Stopping => "stopping", + Phase::Failed => "failed", + } + } +} + +thread_local! { + static PANIC: RefCell> = const { RefCell::new(None) }; + static SERVER: RefCell>> = const { RefCell::new(None) }; + /// Pumpkin's logger is process-wide and installed once. + static LOGGER: Cell = const { Cell::new(false) }; +} + +/// State shared between the exports and the server future. +struct Shared { + /// `ctx.storage` + storage: JsValue, + phase: Cell, + failure: RefCell>, + connections: Cell, + startup_ms: Cell>, + saved_at: RefCell>, + /// Signals the server root that the last connection has closed. + idle: Arc, + /// Resolves at the next phase change; connections arriving mid-transition + /// wait on it and retry. + transition: RefCell>, +} + +// Closures run under catch_unwind; the object is only ever touched from this +// thread, so a poisoned borrow cannot be observed. +impl std::panic::RefUnwindSafe for Shared {} + +#[wasm_bindgen] +pub struct MinecraftWorld { + shared: Rc, +} + +#[wasm_bindgen] +impl MinecraftWorld { + #[wasm_bindgen(constructor)] + pub fn new(state: JsValue, _env: JsValue) -> Result { + std::panic::set_hook(Box::new(|info| { + PANIC.with(|slot| { + slot.borrow_mut().get_or_insert_with(|| info.to_string()); + }); + eprintln!("RUST PANIC: {info}"); + })); + let storage = property(&state, "storage")?; + host::mount_storage(&storage); + Ok(MinecraftWorld { + shared: Rc::new(Shared { + storage, + phase: Cell::new(Phase::Idle), + failure: RefCell::new(None), + connections: Cell::new(0), + startup_ms: Cell::new(None), + saved_at: RefCell::new(None), + idle: Arc::new(tokio::sync::Notify::new()), + transition: RefCell::new(None), + }), + }) + } + + /// Serves one inbound connection; resolves when it closes. The first call + /// while idle also runs the server, resolving once the final save is synced. + #[wasm_bindgen(tokio)] + pub async fn connect(&self, socket: JsValue) -> Result { + let shared = self.shared.clone(); + shared.connect(socket).await + } + + pub fn status(&self) -> Result { + let shared = &self.shared; + let result = js_sys::Object::new(); + let set = |key: &str, value: JsValue| js_sys::Reflect::set(&result, &key.into(), &value); + set("phase", shared.phase.get().name().into())?; + set("connections", (shared.connections.get() as f64).into())?; + set( + "failure", + shared.failure.borrow().as_deref().map_or(JsValue::NULL, JsValue::from), + )?; + set("startup_ms", shared.startup_ms.get().map_or(JsValue::NULL, JsValue::from))?; + set( + "saved_at", + shared.saved_at.borrow().as_deref().map_or(JsValue::NULL, JsValue::from), + )?; + let server = SERVER.with(|slot| slot.borrow().clone()); + set( + "server", + match (shared.phase.get(), server) { + (Phase::Running, Some(server)) => server_status(&server)?, + _ => JsValue::NULL, + }, + )?; + Ok(result.into()) + } +} + +impl Shared { + async fn connect(self: Rc, socket: JsValue) -> Result { + loop { + match self.phase.get() { + Phase::Running => return self.route(socket), + Phase::Failed => { + return Err(js_error(self.failure.borrow().as_deref().unwrap_or("failed"))) + } + Phase::Starting | Phase::Stopping => { + JsFuture::from(self.transition()?).await?; + } + Phase::Idle => break, + } + } + self.set_phase(Phase::Starting); + let started = js_sys::Date::now(); + // A task of its own so a panic arrives as a `JoinError` and fails the + // object rather than escaping the export. + let outcome = match tokio::task::spawn_local(self.clone().run(socket, started)).await { + Ok(outcome) => outcome, + Err(panic) => Err(js_error(format!("server task failed: {panic}"))), + }; + if let Err(error) = &outcome { + self.fail(error); + } + outcome + } + + /// The promise of the next phase change. + fn transition(&self) -> Result { + let mut slot = self.transition.borrow_mut(); + if let Some((promise, _)) = slot.as_ref() { + return Ok(promise.clone()); + } + let resolvers = with_resolvers()?; + let promise: js_sys::Promise = property(&resolvers, "promise")?.unchecked_into(); + let resolve: js_sys::Function = property(&resolvers, "resolve")?.unchecked_into(); + Ok(slot.insert((promise, resolve)).0.clone()) + } + + fn set_phase(&self, phase: Phase) { + self.phase.set(phase); + if let Some((_, resolve)) = self.transition.borrow_mut().take() { + let _ = resolve.call0(&JsValue::UNDEFINED); + } + } + + fn fail(&self, error: &JsValue) { + if self.phase.get() != Phase::Failed { + self.set_phase(Phase::Failed); + let message = js_sys::Error::from(error.clone()).message(); + *self.failure.borrow_mut() = Some(String::from(message)); + } + } + + /// Routes a socket to Pumpkin's listener and tracks it until it closes. + fn route(self: &Rc, socket: JsValue) -> Result { + self.connections.set(self.connections.get() + 1); + let promise = host::handle_as_node_connection(&socket)?; + let shared = self.clone(); + let done = Closure::once_into_js(move |_: JsValue| { + shared.connections.set(shared.connections.get() - 1); + if shared.connections.get() == 0 { + shared.idle.notify_one(); + } + }); + then(&promise, Some(&done), Some(&done)) + } + + async fn run(self: Rc, first: JsValue, started: f64) -> Result { + let root = host::MOUNT_ROOT.with(|root| root.as_string()).unwrap_or_default(); + std::fs::create_dir_all(&root).map_err(js_error)?; + std::env::set_current_dir(&root).map_err(js_error)?; + pumpkin::reset_stop(); + let (basic, advanced) = config::configuration(); + if !LOGGER.replace(true) { + pumpkin::init_logger(&advanced); + } + let server = PumpkinServer::new(basic, advanced, VanillaData::load()).await; + server.init_plugins().await; + SERVER.with(|slot| *slot.borrow_mut() = Some(server.server.clone())); + self.set_phase(Phase::Running); + self.startup_ms.set(Some(js_sys::Date::now() - started)); + + // The first connection's promise belongs to the platform; the server + // root tracks it only through `connections`. + drop(self.route(first)?); + let idle = self.idle.clone(); + let shared = self.clone(); + tokio::task::spawn_local(async move { + loop { + idle.notified().await; + // A connection may have been routed since the notification. + if shared.connections.get() == 0 { + break; + } + } + // Stopping before the listener closes, so no connection is routed + // to a server that will not accept it. + shared.set_phase(Phase::Stopping); + pumpkin::stop_server(); + }); + // Returns after the final save once stopped. + server.start().await; + SERVER.with(|slot| slot.borrow_mut().take()); + if let Some(panic) = PANIC.with(|slot| slot.borrow_mut().take()) { + return Err(js_error(panic)); + } + // Writes issued through the mount become durable before the connection + // is reported closed. + let synced = method(&self.storage, "sync", &[])?; + let shared = self.clone(); + let done = Closure::once_into_js(move |_: JsValue| { + shared + .saved_at + .replace(Some(String::from(js_sys::Date::new_0().to_iso_string()))); + shared.set_phase(Phase::Idle); + }); + then(&synced, Some(&done), None) + } +} + +fn with_resolvers() -> Result { + method(&js_sys::Promise::resolve(&JsValue::UNDEFINED).constructor().into(), "withResolvers", &[]) +} + +fn server_status(server: &Server) -> Result { + let result = js_sys::Object::new(); + let set = |key: &str, value: f64| js_sys::Reflect::set(&result, &key.into(), &value.into()); + set("players", server.get_all_players().len() as f64)?; + set("ticks", server.tick_count.load(Ordering::Relaxed) as f64)?; + set( + "wasm_memory_bytes", + (core::arch::wasm32::memory_size::<0>() * 65536) as f64, + )?; + for (name, bytes) in memory::heap_usage() { + set(name, bytes as f64)?; + } + Ok(result.into()) +} + +pub fn authority() -> String { + format!("world:{PORT}") +} diff --git a/tests/fixtures/filesystem.rs b/tests/fixtures/filesystem.rs deleted file mode 100644 index cf6c210..0000000 --- a/tests/fixtures/filesystem.rs +++ /dev/null @@ -1,76 +0,0 @@ -use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use wasm_bindgen::prelude::*; -use worker::emscripten::{future_to_promise, js_error, socket_from_value}; - -fn main() {} - -#[wasm_bindgen] -pub fn echo(socket: JsValue) -> js_sys::Promise { - future_to_promise(async move { - let mut socket = socket_from_value(socket)?; - let mut bytes = [0u8; 16 * 1024]; - loop { - let count = socket.read(&mut bytes).await.map_err(js_error)?; - if count == 0 { - break; - } - socket.write_all(&bytes[..count]).await.map_err(js_error)?; - } - socket.shutdown().await.map_err(js_error)?; - Ok(JsValue::UNDEFINED) - }) -} - -#[wasm_bindgen] -pub fn tick_after(milliseconds: u32) -> js_sys::Promise { - future_to_promise(async move { - tokio::time::sleep(std::time::Duration::from_millis(milliseconds.into())).await; - Ok(JsValue::TRUE) - }) -} - -/// Exercise the filesystem syscall path before loading Pumpkin's world data. -#[wasm_bindgen] -pub fn filesystem_probe(value: String) -> Result { - use std::io::{Read, Seek, SeekFrom, Write}; - std::fs::create_dir_all("transport-probe").map_err(js_error)?; - let path = "transport-probe/state.bin"; - let previous = std::fs::read_to_string(path).unwrap_or_default(); - let mut file = std::fs::OpenOptions::new() - .create(true) - .truncate(true) - .read(true) - .write(true) - .open("transport-probe/pending.bin") - .map_err(js_error)?; - file.write_all(b"----").map_err(js_error)?; - file.seek(SeekFrom::Start(0)).map_err(js_error)?; - file.write_all(value.as_bytes()).map_err(js_error)?; - file.set_len(value.len() as u64).map_err(js_error)?; - file.sync_all().map_err(js_error)?; - file.seek(SeekFrom::Start(0)).map_err(js_error)?; - let mut check = String::new(); - file.read_to_string(&mut check).map_err(js_error)?; - if check != value { - return Err(js_error("Filesystem descriptor roundtrip failed")); - } - drop(file); - std::fs::rename("transport-probe/pending.bin", path).map_err(js_error)?; - let mut large = vec![0x5a; 3 * 1024 * 1024]; - std::fs::write("transport-probe/large.bin", &large).map_err(js_error)?; - let mut file = std::fs::OpenOptions::new() - .write(true) - .open("transport-probe/large.bin") - .map_err(js_error)?; - file.seek(SeekFrom::Start(65534)).map_err(js_error)?; - file.write_all(&[1, 2, 3, 4]).map_err(js_error)?; - large[65534..65538].copy_from_slice(&[1, 2, 3, 4]); - file.set_len(large.len() as u64 + 3).map_err(js_error)?; - large.extend_from_slice(&[0, 0, 0]); - file.sync_all().map_err(js_error)?; - drop(file); - if std::fs::read("transport-probe/large.bin").map_err(js_error)? != large { - return Err(js_error("Large file roundtrip failed")); - } - Ok(previous) -} diff --git a/tests/fixtures/startup-failure.mjs b/tests/fixtures/startup-failure.mjs deleted file mode 100644 index 10849da..0000000 --- a/tests/fixtures/startup-failure.mjs +++ /dev/null @@ -1,14 +0,0 @@ -import { MinecraftWorld } from '../../worker/world'; - -export class StartupFailure extends MinecraftWorld { - loadRuntime() { throw new Error('Expected startup failure'); } - - async verify() { - const attempt = this.getRuntime(); - const starting = this.status(); - try { await attempt; } catch {} - const failed = this.status(); - try { await this.getRuntime(); } catch {} - return { starting, failed, repeated: this.status() }; - } -} diff --git a/tests/fixtures/worker.mjs b/tests/fixtures/worker.mjs deleted file mode 100644 index f8300ac..0000000 --- a/tests/fixtures/worker.mjs +++ /dev/null @@ -1,56 +0,0 @@ -import { DurableObject, exports } from 'cloudflare:workers'; -export { StartupFailure } from './startup-failure.mjs'; -import createModule from '../../target/workers/wasm32-unknown-emscripten/release/filesystem-probe.js'; -import wasm from '../../target/workers/wasm32-unknown-emscripten/release/filesystem_probe.wasm'; -import { createWorldRuntime } from '../../worker/filesystem'; -import { forwardSocket } from '../../worker/socket'; - -export class FilesystemTest extends DurableObject { - runtime; - - getRuntime() { - return this.runtime ??= createWorldRuntime(this.ctx.storage, options => createModule({ - ...options, - instantiateWasm(imports, receive) { - const instance = new WebAssembly.Instance(wasm, imports); - receive(instance); - return instance.exports; - }, - })); - } - - async connect(socket) { - void socket.closed.catch(() => undefined); - const runtime = await this.getRuntime(); - await runtime.run(() => runtime.instance.echo(socket)); - } - - async status() { - const runtime = await this.getRuntime(); - return { timer: await runtime.run(() => runtime.instance.tick_after(20)) }; - } - - async filesystemProbe(value) { - const runtime = await this.getRuntime(); - return runtime.run(() => runtime.instance.filesystem_probe(value)); - } -} - -export default { - async connect(socket, env) { - const target = exports.FilesystemTest.getByName(env.WORLD_NAME).connect('world:25565', { allowHalfOpen: true }); - await forwardSocket(socket, target); - }, - async fetch(request, env) { - const url = new URL(request.url); - if (url.pathname === '/health') return Response.json({ ready: true }); - if (url.pathname === '/startup-failure') { - return Response.json(await exports.StartupFailure.getByName('failure').verify()); - } - const stub = exports.FilesystemTest.getByName(url.searchParams.get('world') ?? env.WORLD_NAME); - if (request.method === 'POST' && url.pathname === '/filesystem-probe') { - return Response.json({ previous: await stub.filesystemProbe(await request.text()) }); - } - return Response.json(await stub.status()); - }, -}; diff --git a/tests/fixtures/wrangler.jsonc b/tests/fixtures/wrangler.jsonc deleted file mode 100644 index 8ab097a..0000000 --- a/tests/fixtures/wrangler.jsonc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "../../node_modules/wrangler/config-schema.json", - "name": "minecraft-filesystem-tests", - "main": "worker.mjs", - "compatibility_date": "2026-09-05", - "compatibility_flags": ["nodejs_compat", "new_module_registry", "no_handle_cross_request_promise_resolution"], - "dev": { "ip": "127.0.0.1", "port": 8787 }, - "connect": [{ "protocol": "tcp", "address": "127.0.0.1", "port": 25565 }], - "exports": { - "FilesystemTest": { "type": "durable-object", "storage": "sqlite" }, - "StartupFailure": { "type": "durable-object", "storage": "sqlite" } - }, - "vars": { "WORLD_NAME": "integration" } -} diff --git a/tests/integration.mjs b/tests/integration.mjs index ddeceb7..0b29590 100644 --- a/tests/integration.mjs +++ b/tests/integration.mjs @@ -1,5 +1,4 @@ import assert from 'node:assert/strict'; -import net from 'node:net'; import { readFile } from 'node:fs/promises'; import { setTimeout as delay } from 'node:timers/promises'; import { status, play, waitFor } from './client.mjs'; @@ -30,55 +29,8 @@ function editableBlock(client) { throw new Error(`No stable block within reach of ${JSON.stringify(client.position)}`); } -async function echo() { - const socket = net.connect({ host: '127.0.0.1', port: 25565 }); - socket.setTimeout(15000, () => socket.destroy(new Error('Echo timed out'))); - const payload = Buffer.from(Array.from({ length: 65536 }, (_, index) => index & 255)); - const chunks = []; - let length = 0; - try { - await new Promise((resolve, reject) => { - socket.once('connect', resolve); - socket.once('error', reject); - }); - for (let offset = 0; offset < payload.length; offset += 997) socket.write(payload.subarray(offset, offset + 997)); - for await (const chunk of socket) { - chunks.push(chunk); - length += chunk.length; - if (length >= payload.length) break; - } - assert.ok(Buffer.concat(chunks).equals(payload), `TCP echo differed: received ${length} of ${payload.length} bytes`); - } finally { socket.destroy(); } -} - -async function marker(world, value) { - return (await request(`/filesystem-probe?world=${world}`, { method: 'POST', body: value })).previous; -} - -let worker = await startWorker(true); -try { - await Promise.all([echo(), echo()]); - assert.equal((await request('/')).timer, true); - const failure = await request('/startup-failure'); - assert.equal(failure.starting.phase, 'starting'); - assert.equal(failure.failed.phase, 'failed'); - assert.equal(failure.failed.failure, 'Expected startup failure'); - assert.deepEqual(failure.repeated, failure.failed); - assert.equal(await marker('isolation-a', 'alpha'), ''); - assert.equal(await marker('isolation-b', 'beta'), ''); - assert.equal(await marker('isolation-a', 'alpha-2'), 'alpha'); - worker.healthy(); -} finally { await worker.stop(); } -worker = await startWorker(true); -try { - assert.equal(await marker('isolation-a', 'alpha-3'), 'alpha-2'); - assert.equal(await marker('isolation-b', 'beta-2'), 'beta'); - worker.healthy(); -} finally { await worker.stop(); } -console.log('Filesystem, socket, and isolation tests passed'); - let savedChunk, savedPosition, changedBlock; -worker = await startWorker(); +let worker = await startWorker(); const clients = []; try { assert.equal((await status()).version.protocol, 776); @@ -107,7 +59,7 @@ try { assert.equal(b.block(changedBlock), 0, 'Second client lost the edit before checkpoint'); await a.close(); await b.close(); clients.length = 0; const saved = await checkpoint(worker); - console.log(`World checkpoint completed at ${saved.checkpointed_at}`); + console.log(`World saved at ${saved.saved_at}`); } finally { await Promise.all(clients.map(client => client.close())); await worker.stop(); diff --git a/tests/server.mjs b/tests/server.mjs index 37ac929..7fd5d8b 100644 --- a/tests/server.mjs +++ b/tests/server.mjs @@ -23,11 +23,11 @@ export async function testServer({ seed = '1789195700909824175' } = {}) { } let phase = 0; - async function startWorker(fixture = false) { - const log = path.join(probe, `${++phase}-${fixture ? "filesystem" : "minecraft"}.log`); + async function startWorker() { + const log = path.join(probe, `${++phase}-minecraft.log`); const child = spawn(process.execPath, [ path.join(repo, 'node_modules/wrangler/bin/wrangler.js'), 'dev', '--local', - '--config', path.join(repo, fixture ? 'tests/fixtures/wrangler.jsonc' : 'wrangler.jsonc'), + '--config', path.join(repo, 'wrangler.jsonc'), '--persist-to', persistence, '--var', 'WORLD_NAME:integration', // Known terrain with a stable editable spawn block; random water spawns // cannot exercise the integration test's shared block-edit assertions. @@ -95,7 +95,7 @@ export async function testServer({ seed = '1789195700909824175' } = {}) { throw error; } if (state.failure) throw new Error(state.failure); - return state.phase === 'idle' && state.checkpointed_at && state.connections === 0 && state; + return state.phase === 'idle' && state.saved_at && state.connections === 0 && state; }, 'world checkpoint'); } diff --git a/toolchain/wasm-bindgen.Cargo.lock b/toolchain/wasm-bindgen.Cargo.lock deleted file mode 100644 index e772975..0000000 --- a/toolchain/wasm-bindgen.Cargo.lock +++ /dev/null @@ -1,4510 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "add" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - -[[package]] -name = "anyhow" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" - -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits 0.2.19", -] - -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - -[[package]] -name = "askama" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b8246bcbf8eb97abef10c2d92166449680d41d55c0fc6978a91dec2e3619608" -dependencies = [ - "askama_macros", - "itoa", - "percent-encoding", - "serde", - "serde_json", -] - -[[package]] -name = "askama_derive" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9670bc84a28bb3da91821ef74226949ab63f1265aff7c751634f1dd0e6f97c" -dependencies = [ - "askama_parser", - "basic-toml", - "memchr", - "proc-macro2", - "quote", - "rustc-hash", - "serde", - "serde_derive", - "syn 2.0.119", -] - -[[package]] -name = "askama_macros" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0756b45480437dded0565dfc568af62ccce146fb6cfe902e808ba86e445f44f" -dependencies = [ - "askama_derive", -] - -[[package]] -name = "askama_parser" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0af3691ba3af77949c0b5a3925444b85cb58a0184cc7fec16c68ba2e7be868" -dependencies = [ - "rustc-hash", - "serde", - "serde_derive", - "unicode-ident", - "winnow 1.0.4", -] - -[[package]] -name = "assert_cmd" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aa3a22042e45de04255c7bf3626e239f450200fd0493c1e382263544b20aea6" -dependencies = [ - "anstyle", - "bstr", - "libc", - "predicates", - "predicates-core", - "predicates-tree", - "wait-timeout", -] - -[[package]] -name = "async-trait" -version = "0.1.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.2", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "aws-lc-rs" -version = "1.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.43.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", - "pkg-config", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - -[[package]] -name = "basic-toml" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" -dependencies = [ - "serde", -] - -[[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 = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" - -[[package]] -name = "brotli-decompressor" -version = "5.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bstr" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" -dependencies = [ - "memchr", - "regex-automata", - "serde_core", -] - -[[package]] -name = "buf_redux" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" -dependencies = [ - "memchr", - "safemem", -] - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytemuck" -version = "1.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" - -[[package]] -name = "canvas" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cc" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex 2.0.1", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" - -[[package]] -name = "chacha20" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" -dependencies = [ - "cfg-if", - "cpufeatures", - "rand_core 0.10.1", -] - -[[package]] -name = "char" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "chrono" -version = "0.4.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits 0.2.19", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "chunked_transfer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" - -[[package]] -name = "clap" -version = "4.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fb99565819980999fb7b4a1796046a5c949e6d4ff132cf5fadf5a641e20d776" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f2392eae7f16557a3d727ef3a12e57b2b2ca6f98566a5f4fb41ffe305df077" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "clap_lex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" - -[[package]] -name = "closures" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "console_log" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "console_log" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86919cef3e37b9356ccf54d4421208c17ecfda01beae61393e7ffd72916c0ef1" -dependencies = [ - "log", - "web-sys", -] - -[[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" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "deflate" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" -dependencies = [ - "adler32", - "byteorder", -] - -[[package]] -name = "defmt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" -dependencies = [ - "bitflags 1.3.2", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" -dependencies = [ - "defmt-parser", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "defmt-parser" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" -dependencies = [ - "thiserror 2.0.19", -] - -[[package]] -name = "der" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" -dependencies = [ - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - -[[package]] -name = "displaydoc" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "dom" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "either" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enum_primitive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" -dependencies = [ - "num-traits 0.1.43", -] - -[[package]] -name = "env_filter" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "explicit-resource-management" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[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.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" - -[[package]] -name = "fetch" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "filetime" -version = "0.2.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" -dependencies = [ - "cfg-if", - "libc", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" -dependencies = [ - "num-traits 0.2.19", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "futures" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-channel-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a" -dependencies = [ - "futures-core-preview", -] - -[[package]] -name = "futures-core" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" - -[[package]] -name = "futures-core-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a" - -[[package]] -name = "futures-executor" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "futures-sink" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" - -[[package]] -name = "futures-task" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" - -[[package]] -name = "futures-timer" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" - -[[package]] -name = "futures-util" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "rand_core 0.10.1", - "wasm-bindgen", -] - -[[package]] -name = "gg-alloc" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee5134f1abeb59e9ba515150ddc26bf2b2207f8bce38e1f5cf8d1596efa22ba" - -[[package]] -name = "gif" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e41945ba23db3bf51b24756d73d81acb4f28d85c3dccc32c6fae904438c25f" -dependencies = [ - "color_quant", - "lzw", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" -dependencies = [ - "fallible-iterator", - "indexmap", - "stable_deref_trait", -] - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "gloo" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15282ece24eaf4bd338d73ef580c6714c8615155c4190c781290ee3fa0fd372" -dependencies = [ - "gloo-console", - "gloo-dialogs", - "gloo-events", - "gloo-file", - "gloo-history", - "gloo-net", - "gloo-render", - "gloo-storage", - "gloo-timers", - "gloo-utils", - "gloo-worker", -] - -[[package]] -name = "gloo-console" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a17868f56b4a24f677b17c8cb69958385102fa879418052d60b50bc1727e261" -dependencies = [ - "gloo-utils", - "js-sys", - "serde", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-dialogs" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4748e10122b01435750ff530095b1217cf6546173459448b83913ebe7815df" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-events" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c26fb45f7c385ba980f5fa87ac677e363949e065a083722697ef1b2cc91e41" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-file" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97563d71863fb2824b2e974e754a81d19c4a7ec47b09ced8a0e6656b6d54bd1f" -dependencies = [ - "gloo-events", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-history" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "903f432be5ba34427eac5e16048ef65604a82061fe93789f2212afc73d8617d6" -dependencies = [ - "getrandom 0.2.17", - "gloo-events", - "gloo-utils", - "serde", - "serde-wasm-bindgen", - "serde_urlencoded", - "thiserror 1.0.69", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-net" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils", - "http 0.2.12", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror 1.0.69", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-render" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56008b6744713a8e8d98ac3dcb7d06543d5662358c9c805b4ce2167ad4649833" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-storage" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a" -dependencies = [ - "gloo-utils", - "js-sys", - "serde", - "serde_json", - "thiserror 1.0.69", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gloo-worker" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "085f262d7604911c8150162529cefab3782e91adb20202e8658f7275d2aefe5d" -dependencies = [ - "bincode", - "futures", - "gloo-utils", - "gloo-worker-macros", - "js-sys", - "pinned", - "serde", - "thiserror 1.0.69", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-worker-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956caa58d4857bc9941749d55e4bd3000032d8212762586fa5705632967140e7" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "guide-supported-types-examples" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "h2" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.4.2", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "foldhash", - "serde", - "serde_core", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hello_world" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" -dependencies = [ - "bytes", - "http 1.4.2", -] - -[[package]] -name = "http-body-util" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" -dependencies = [ - "bytes", - "futures-core", - "http 1.4.2", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "humantime" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" - -[[package]] -name = "hyper" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http 1.4.2", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" -dependencies = [ - "http 1.4.2", - "hyper", - "hyper-util", - "rustls", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-util", - "http 1.4.2", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" -dependencies = [ - "rayon", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "image" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95816db758249fe16f23a4e23f1a3a817fe11892dbfd1c5836f625324702158" -dependencies = [ - "byteorder", - "enum_primitive", - "gif", - "jpeg-decoder", - "num-iter", - "num-rational 0.1.43", - "num-traits 0.1.43", - "png", - "scoped_threadpool", -] - -[[package]] -name = "import_js" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "inflate" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e0062d2dc2f17d2f13750d95316ae8a2ff909af0fda957084f5defd87c43bb" - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jiff" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e184d09547b80eb7e20d141ba2fb1fbac843ca53f4cf1b31210adc4c1adc6e16" -dependencies = [ - "defmt", - "jiff-core", - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", -] - -[[package]] -name = "jiff-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" -dependencies = [ - "defmt", -] - -[[package]] -name = "jiff-static" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323da076b7a6faf914dc677cb05a4b907742ff7375c8322c9e7f5061e5e0e9de" -dependencies = [ - "jiff-core", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "jni" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" -dependencies = [ - "cfg-if", - "combine", - "jni-macros", - "jni-sys", - "log", - "simd_cesu8", - "thiserror 2.0.19", - "walkdir", - "windows-link", -] - -[[package]] -name = "jni-macros" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", - "simd_cesu8", - "syn 2.0.119", -] - -[[package]] -name = "jni-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote", - "syn 2.0.119", -] - -[[package]] -name = "jobserver" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" -dependencies = [ - "getrandom 0.4.3", - "libc", -] - -[[package]] -name = "jpeg-decoder" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" -dependencies = [ - "rayon", -] - -[[package]] -name = "js-sys" -version = "0.3.103" -dependencies = [ - "cfg-if", - "futures-channel", - "futures-core", - "futures-lite", - "futures-util", - "half", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test", - "web-sys", -] - -[[package]] -name = "json" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" - -[[package]] -name = "julia_set" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leb128" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83bff1d572d6b9aeef67ddfc8448e4a3737909cb28e81f97c791b9018703e52" - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "log" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "lzw" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" - -[[package]] -name = "matrixmultiply" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f607c237553f086e7043417a51df26b2eb899d3caff94e6a67592ff992fedc7" -dependencies = [ - "autocfg", - "rawpointer", -] - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minicov" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4869b6a491569605d66d3952bcdf03df789e5b536e5f0cf7758a7f08a55ae24d" -dependencies = [ - "cc", - "walkdir", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "multipart" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" -dependencies = [ - "buf_redux", - "httparse", - "log", - "mime", - "mime_guess", - "quick-error", - "rand 0.8.7", - "safemem", - "tempfile", - "twoway", -] - -[[package]] -name = "nalgebra" -version = "0.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d43ddcacf343185dfd6de2ee786d9e8b1c2301622afab66b6c73baf9882abfd" -dependencies = [ - "approx", - "matrixmultiply", - "num-complex", - "num-rational 0.4.2", - "num-traits 0.2.19", - "rand 0.8.7", - "rand_distr", - "simba", - "typenum", -] - -[[package]] -name = "native-tls" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "no-std" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "node_and_deno" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "nodejs-threads" -version = "0.1.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "nom" -version = "5.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" -dependencies = [ - "memchr", - "version_check", -] - -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-bigint" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" -dependencies = [ - "num-integer", - "num-traits 0.2.19", -] - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits 0.2.19", -] - -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits 0.2.19", -] - -[[package]] -name = "num-iter" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" -dependencies = [ - "num-integer", - "num-traits 0.2.19", -] - -[[package]] -name = "num-rational" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbfff0773e8a07fb033d726b9ff1327466709820788e5298afce4d752965ff1e" -dependencies = [ - "autocfg", - "num-integer", - "num-traits 0.2.19", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits 0.2.19", -] - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -dependencies = [ - "num-traits 0.2.19", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - -[[package]] -name = "openssl" -version = "0.10.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" -dependencies = [ - "bitflags 2.13.1", - "cfg-if", - "foreign-types", - "libc", - "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.119", -] - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "openssl-src" -version = "300.6.1+3.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pem-rfc7468" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "performance" -version = "0.0.0" -dependencies = [ - "humantime", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "pin-project" -version = "1.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pinned" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b" -dependencies = [ - "futures", - "rustversion", - "thiserror 1.0.69", -] - -[[package]] -name = "pkg-config" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" - -[[package]] -name = "png" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb773e9a557edb568ce9935cf783e3cdcabe06a9449d41b3e5506d88e582c82" -dependencies = [ - "bitflags 0.7.0", - "deflate", - "inflate", - "num-iter", -] - -[[package]] -name = "portable-atomic" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" - -[[package]] -name = "portable-atomic-util" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[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.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "predicates" -version = "3.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" -dependencies = [ - "anstyle", - "difflib", - "float-cmp", - "normalize-line-endings", - "predicates-core", - "regex", -] - -[[package]] -name = "predicates-core" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" - -[[package]] -name = "predicates-tree" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" -dependencies = [ - "predicates-core", - "termtree", -] - -[[package]] -name = "pretty_assertions" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" -dependencies = [ - "diff", - "yansi", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit 0.25.13+spec-1.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quinn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.19", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.4.3", - "lru-slab", - "rand 0.10.2", - "rand_pcg", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.19", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.61.2", -] - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" -dependencies = [ - "libc", - "rand_chacha", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" -dependencies = [ - "chacha20", - "getrandom 0.4.3", - "rand_core 0.10.1", -] - -[[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 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" - -[[package]] -name = "rand_distr" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" -dependencies = [ - "num-traits 0.2.19", - "rand 0.8.7", -] - -[[package]] -name = "rand_pcg" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" -dependencies = [ - "rand_core 0.10.1", -] - -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - -[[package]] -name = "rayon" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "raytrace-parallel" -version = "0.0.0" -dependencies = [ - "console_error_panic_hook", - "futures-channel-preview", - "js-sys", - "rayon", - "rayon-core", - "raytracer", - "serde-wasm-bindgen", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "raytracer" -version = "0.1.0" -source = "git+https://github.com/alexcrichton/raytracer?branch=update-deps#42faa13859f7d8d47fd18be785c108003a207786" -dependencies = [ - "image", - "serde", - "serde_derive", -] - -[[package]] -name = "regex" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "relative-path" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" - -[[package]] -name = "request-animation-frame" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "reqwest" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-core", - "h2", - "http 1.4.2", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "mime", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rouille" -version = "3.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921" -dependencies = [ - "base64 0.13.1", - "chrono", - "filetime", - "multipart", - "percent-encoding", - "rand 0.8.7", - "serde", - "serde_derive", - "serde_json", - "sha1_smol", - "threadpool", - "time", - "tiny_http", - "url", -] - -[[package]] -name = "rstest" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" -dependencies = [ - "futures-timer", - "futures-util", - "rstest_macros", -] - -[[package]] -name = "rstest_macros" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" -dependencies = [ - "cfg-if", - "glob", - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "regex", - "relative-path", - "rustc_version", - "syn 2.0.119", - "unicode-ident", -] - -[[package]] -name = "rust-duck-typed-interfaces" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "rust-webassembly-weather-reports" -version = "0.0.0" -dependencies = [ - "chrono", - "gloo", - "json", - "reqwest", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" - -[[package]] -name = "rustc-hash" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.13.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" -dependencies = [ - "aws-lc-rs", - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pki-types" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "safe_arch" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - -[[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 = "sample" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde-wasm-bindgen" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "serde_core" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.2", -] - -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_spanned" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" -dependencies = [ - "serde_core", -] - -[[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_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simba" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95" -dependencies = [ - "approx", - "num-complex", - "num-traits 0.2.19", - "paste", - "wide", -] - -[[package]] -name = "simd-adler32" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" - -[[package]] -name = "simd_cesu8" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" -dependencies = [ - "rustc_version", - "simdutf8", -] - -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" - -[[package]] -name = "socket2" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "sourcefile" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "654ee0ca20343b1b2d6cb222c3c136b6209e20324ef53f7bde19cdd1e766e986" - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "statrs" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3fe7c28c6512e766b0874335db33c94ad7b8f9054228ae1c2abd47ce7d335e" -dependencies = [ - "approx", - "nalgebra", - "num-traits 0.2.19", - "rand 0.8.7", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a207d6d6a2b7fc470b80443726053f18a2481b7e1eee970597051596567987a3" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synchronous-instantiation" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "target-triple" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a6bfce3d99adfa72d24750a61f782f3036a81e7f86d8841ee1326deaebd171" - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.3", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "termtree" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" -dependencies = [ - "thiserror-impl 2.0.19", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.2", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "time" -version = "0.3.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" -dependencies = [ - "deranged", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde_core", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" - -[[package]] -name = "tiny_http" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" -dependencies = [ - "ascii", - "chunked_transfer", - "httpdate", - "log", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" -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 = "todomvc" -version = "0.0.0" -dependencies = [ - "askama", - "console_error_panic_hook", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "tokio" -version = "1.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "1.1.3+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" -dependencies = [ - "indexmap", - "serde_core", - "serde_spanned", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 1.0.4", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" - -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "toml_datetime 0.6.11", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.25.13+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" -dependencies = [ - "indexmap", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "winnow 1.0.4", -] - -[[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" -dependencies = [ - "winnow 1.0.4", -] - -[[package]] -name = "toml_writer" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" -dependencies = [ - "bitflags 2.13.1", - "bytes", - "futures-util", - "http 1.4.2", - "http-body", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", - "url", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -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 = "trybuild" -version = "1.0.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06649c6f63d86604ba0c8950d5a1829fc9a17afd70fc6629f481d75b6a624c78" -dependencies = [ - "glob", - "serde", - "serde_derive", - "serde_json", - "target-triple", - "termcolor", - "toml", -] - -[[package]] -name = "twoway" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" -dependencies = [ - "memchr", -] - -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "typescript-tests" -version = "0.0.0" -dependencies = [ - "js-sys", - "serde", - "serde-wasm-bindgen", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "ureq" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" -dependencies = [ - "base64 0.22.1", - "brotli-decompressor", - "der", - "flate2", - "log", - "native-tls", - "percent-encoding", - "rustls", - "rustls-pki-types", - "ureq-proto", - "utf8-zero", - "webpki-root-certs", - "webpki-roots", -] - -[[package]] -name = "ureq-proto" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" -dependencies = [ - "base64 0.22.1", - "http 1.4.2", - "httparse", - "log", -] - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8-zero" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[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.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "walrus" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bfa49767bb3a9e1afb02aa95bbcbde8d82f2db4ca377afae94d688f14f62378" -dependencies = [ - "anyhow", - "gimli", - "id-arena", - "leb128", - "log", - "rayon", - "walrus-macro", - "wasm-encoder 0.245.1", - "wasmparser 0.245.1", -] - -[[package]] -name = "walrus-macro" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9b0525d7ea6e5f906aca581a172e5c91b4c595290dfa8ad4a2bc9ffef33b44" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[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.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasm-audio-worklet" -version = "0.0.0" -dependencies = [ - "console_error_panic_hook", - "console_log 1.1.0", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.126" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "paste", - "rustversion", - "serde", - "serde_derive", - "serde_json", - "wasm-bindgen-futures", - "wasm-bindgen-macro", - "wasm-bindgen-shared", - "wasm-bindgen-test", - "wasm-bindgen-test-crate-a", - "wasm-bindgen-test-crate-b", -] - -[[package]] -name = "wasm-bindgen-benchmark" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-cli" -version = "0.2.126" -dependencies = [ - "anyhow", - "assert_cmd", - "clap", - "env_logger", - "log", - "native-tls", - "predicates", - "pretty_assertions", - "regex", - "rouille", - "rstest", - "serde", - "serde_derive", - "serde_json", - "shlex 1.3.0", - "tempfile", - "ureq", - "walrus", - "wasm-bindgen-cli-support", - "wasm-bindgen-test-shared", - "wasmparser 0.245.1", - "wasmprinter 0.245.1", -] - -[[package]] -name = "wasm-bindgen-cli-support" -version = "0.2.126" -dependencies = [ - "anyhow", - "base64 0.22.1", - "leb128", - "log", - "rstest", - "rustc-demangle", - "serde", - "serde_json", - "walrus", - "wasm-bindgen-shared", - "wasmparser 0.245.1", - "wasmprinter 0.243.0", - "wast 243.0.0", - "wat", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.76" -dependencies = [ - "futures-channel", - "futures-lite", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-test", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.126" -dependencies = [ - "js-sys", - "quote", - "trybuild", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-macro-support", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.126" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.119", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-paint" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.126" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-bindgen-test" -version = "0.3.76" -dependencies = [ - "async-trait", - "cast", - "gg-alloc", - "js-sys", - "libm", - "minicov", - "nu-ansi-term", - "num-traits 0.2.19", - "oorandom", - "serde", - "serde_json", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", - "wasm-bindgen-test-shared", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-test-crate-a" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-test-crate-b" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.3.76" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", - "tokio", - "trybuild", - "wasm-bindgen-test", -] - -[[package]] -name = "wasm-bindgen-test-shared" -version = "0.2.126" - -[[package]] -name = "wasm-bindgen-webidl" -version = "0.0.0" -dependencies = [ - "anyhow", - "clap", - "env_logger", - "heck", - "lazy_static", - "log", - "once_cell", - "proc-macro2", - "quote", - "sourcefile", - "syn 2.0.119", - "weedle", -] - -[[package]] -name = "wasm-encoder" -version = "0.243.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55db9c896d70bd9fa535ce83cd4e1f2ec3726b0edd2142079f594fc3be1cb35" -dependencies = [ - "leb128fmt", - "wasmparser 0.243.0", -] - -[[package]] -name = "wasm-encoder" -version = "0.245.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9dca005e69bf015e45577e415b9af8c67e8ee3c0e38b5b0add5aa92581ed5c" -dependencies = [ - "leb128fmt", - "wasmparser 0.245.1", -] - -[[package]] -name = "wasm-encoder" -version = "0.254.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09480d646178e5fdd12bb06e812d0af9a3a191dbc9cd697fdc86687beade7393" -dependencies = [ - "leb128fmt", - "wasmparser 0.254.0", -] - -[[package]] -name = "wasm-in-wasm" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", -] - -[[package]] -name = "wasm-in-wasm-imports" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", -] - -[[package]] -name = "wasm-in-web-worker" -version = "0.0.0" -dependencies = [ - "console_error_panic_hook", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasmparser" -version = "0.243.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d8db401b0528ec316dfbe579e6ab4152d61739cfe076706d2009127970159d" -dependencies = [ - "bitflags 2.13.1", - "indexmap", - "semver", -] - -[[package]] -name = "wasmparser" -version = "0.245.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f08c9adee0428b7bddf3890fc27e015ac4b761cc608c822667102b8bfd6995e" -dependencies = [ - "bitflags 2.13.1", - "hashbrown 0.16.1", - "indexmap", - "semver", - "serde", -] - -[[package]] -name = "wasmparser" -version = "0.254.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5769a29f799fbab136aaf65b4fe5384cd7d93fe6fc9ba0dcb6c8382a1f16e27" -dependencies = [ - "bitflags 2.13.1", - "indexmap", - "semver", -] - -[[package]] -name = "wasmprinter" -version = "0.243.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2b6035559e146114c29a909a3232928ee488d6507a1504d8934e8607b36d7b" -dependencies = [ - "anyhow", - "termcolor", - "wasmparser 0.243.0", -] - -[[package]] -name = "wasmprinter" -version = "0.245.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41517a3716fbb8ccf46daa9c1325f760fcbff5168e75c7392288e410b91ac8" -dependencies = [ - "anyhow", - "termcolor", - "wasmparser 0.245.1", -] - -[[package]] -name = "wast" -version = "243.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df21d01c2d91e46cb7a221d79e58a2d210ea02020d57c092e79255cc2999ca7f" -dependencies = [ - "bumpalo", - "leb128fmt", - "memchr", - "unicode-width", - "wasm-encoder 0.243.0", -] - -[[package]] -name = "wast" -version = "254.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ed4dfc8f6b9fc38b231065e2cdfbf7359af5ab945990abf09658dcc63c3e32" -dependencies = [ - "bumpalo", - "leb128fmt", - "memchr", - "unicode-width", - "wasm-encoder 0.254.0", -] - -[[package]] -name = "wat" -version = "1.254.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7127f7f9b8f127c879991cecd35f494e4628bae1b0874c681414d8d8831e952c" -dependencies = [ - "wast 254.0.0", -] - -[[package]] -name = "wcodspeed" -version = "0.1.0" -dependencies = [ - "anyhow", - "serde", - "serde_json", - "statrs", -] - -[[package]] -name = "web-sys" -version = "0.3.103" -dependencies = [ - "futures", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webaudio" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "webgl" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "webidl-tests" -version = "0.0.0" -dependencies = [ - "env_logger", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test", - "wasm-bindgen-webidl", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webrtc_datachannel" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "websockets" -version = "0.0.0" -dependencies = [ - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "webxr" -version = "0.0.0" -dependencies = [ - "console_error_panic_hook", - "futures", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "weedle" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5352ce9558ad6aa2c4eca1b18329177f5f77ba71f54eac09709ef47701137eb" -dependencies = [ - "nom", -] - -[[package]] -name = "wide" -version = "0.7.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" -dependencies = [ - "bytemuck", - "safe_arch", -] - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" -dependencies = [ - "memchr", -] - -[[package]] -name = "without-a-bundler" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "without-a-bundler-no-modules" -version = "0.0.0" -dependencies = [ - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "zerofrom" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index afefe46..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "Bundler", - "lib": ["ES2022"], - "strict": true, - "noEmit": true, - "skipLibCheck": true - }, - "include": ["worker/**/*.ts"] -} diff --git a/worker/filesystem.ts b/worker/filesystem.ts deleted file mode 100644 index 79dcf6e..0000000 --- a/worker/filesystem.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { AsyncLocalStorage } from 'node:async_hooks'; -import { LocalDOFilesystem } from 'durable-object-fs/local'; -import { mount, withMounts } from 'worker-fs-mount'; -import * as nodeFs from 'worker-fs-mount/fs-sync'; - -/** Each wasm instance keeps one mount context across requests and Tokio callbacks. */ -export function createWorldRuntime( - storage: DurableObjectStorage, - create: (options: { nodeFs: typeof nodeFs; cwd: string }) => Promise, -) { - return withMounts(async () => { - mount('/data', new LocalDOFilesystem(storage)); - const run = AsyncLocalStorage.snapshot(); - return { instance: await create({ nodeFs, cwd: '/data' }), run }; - }); -} diff --git a/worker/fs-library.js b/worker/fs-library.js deleted file mode 100644 index fd7153d..0000000 --- a/worker/fs-library.js +++ /dev/null @@ -1,114 +0,0 @@ -// Emscripten's Node loader uses createRequire at runtime, so Wrangler's build-time -// aliases cannot replace its fs object. Supply the mounted node:fs implementation -// to each factory, with a cwd that does not change the isolate's process.cwd. -addToLibrary({ - // Workers exposes public fs.constants, but not Node's internal process.binding. - $NODEFS__postset: ` - NODEFS.isWindows = false; - NODEFS.flagsForNodeMap = Object.fromEntries([ - [{{{ cDefs.O_APPEND }}}, 'O_APPEND'], - [{{{ cDefs.O_CREAT }}}, 'O_CREAT'], - [{{{ cDefs.O_EXCL }}}, 'O_EXCL'], - [{{{ cDefs.O_NOCTTY }}}, 'O_NOCTTY'], - [{{{ cDefs.O_RDONLY }}}, 'O_RDONLY'], - [{{{ cDefs.O_RDWR }}}, 'O_RDWR'], - [{{{ cDefs.O_DSYNC }}}, 'O_SYNC'], - [{{{ cDefs.O_TRUNC }}}, 'O_TRUNC'], - [{{{ cDefs.O_WRONLY }}}, 'O_WRONLY'], - [{{{ cDefs.O_NOFOLLOW }}}, 'O_NOFOLLOW'], - ].map(([flag, name]) => [flag, fs.constants[name]])); - `, - - $workerFilesystem__deps: ['$FS', '$PATH_FS', '$TTY'], - $workerFilesystem__postset: () => addAtPreRun('workerFilesystem()'), - $workerFilesystem: () => { - // biome-ignore lint/complexity/useLiteralKeys: preserve the external Emscripten property name - fs = Module['nodeFs']; - // biome-ignore lint/complexity/useLiteralKeys: preserve the external Emscripten property name - let workerCwd = Module['cwd']; - FS.cwd = () => workerCwd; - // Keep standard output on Emscripten's console callbacks, including printErr - // so the host can record Rust panics. Native Worker fds are not stdio handles. - const consoles = [ - null, - { output: [], ops: TTY.default_tty_ops }, - { output: [], ops: TTY.default_tty1_ops }, - ]; - // NODERAWFS's stdio streams have no nodes; expose character-device metadata - // without asking the native Workers filesystem about those descriptor numbers. - const fstat = FS.fstat; - FS.fstat = (fd) => { - const stream = FS.getStreamChecked(fd); - if (stream.nfd === 0 || stream.nfd === 1 || stream.nfd === 2) { - return { - dev: 0, - ino: stream.nfd, - mode: 0o020666, - nlink: 1, - uid: 0, - gid: 0, - rdev: 0, - size: 0, - blksize: 4096, - blocks: 0, - atime: new Date(0), - mtime: new Date(0), - ctime: new Date(0), - }; - } - return fstat(fd); - }; - const write = FS.write; - FS.write = (stream, buffer, offset, length, position) => { - const tty = consoles[stream.nfd]; - if (!tty) return write(stream, buffer, offset, length, position); - for (let i = offset; i < offset + length; i++) tty.ops.put_char(tty, buffer[i]); - return length; - }; - // Non-*at syscalls can pass relative paths directly to NODERAWFS. Resolve - // those against this wasm instance, not the shared Node process directory. - for (const name of [ - 'lookupPath', - 'stat', - 'chmod', - 'chown', - 'truncate', - 'utime', - 'open', - 'mkdir', - 'rmdir', - 'readdir', - 'unlink', - 'readlink', - ]) { - const operation = FS[name]; - FS[name] = (path, ...args) => operation(PATH_FS.resolve(path), ...args); - } - const rename = FS.rename; - FS.rename = (from, to) => rename(PATH_FS.resolve(from), PATH_FS.resolve(to)); - const symlink = FS.symlink; - FS.symlink = (target, path) => symlink(target, PATH_FS.resolve(path)); - FS.chdir = (path) => { - path = PATH_FS.resolve(path); - if (!FS.isDir(FS.stat(path).mode)) throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); - workerCwd = path; - }; - }, - - // SQLite operations are synchronous. Checkpoint completion separately awaits - // storage.sync(); ordinary Rust File::sync_all must not suspend a sync export. - fd_sync__deps: ['$FS', '$workerFilesystem'], - fd_sync__async: false, - fd_sync: (fd) => { - try { - const stream = FS.getStreamChecked(fd); - if (typeof stream.nfd === 'number') fs.fsyncSync(stream.nfd); - else stream.stream_ops?.fsync?.(stream); - return 0; - } catch (error) { - if (typeof error.errno === 'number') return error.errno; - if (error.code) return ERRNO_CODES[error.code]; - throw error; - } - }, -}); diff --git a/worker/index.ts b/worker/index.ts deleted file mode 100644 index 142919f..0000000 --- a/worker/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { exports } from 'cloudflare:workers'; -import { forwardSocket } from './socket'; -export { MinecraftWorld } from './world'; - -export default { - async connect(socket: Socket, env: Env): Promise { - const target = exports.MinecraftWorld.getByName(env.WORLD_NAME).connect('world:25565', { allowHalfOpen: true }); - await forwardSocket(socket, target); - }, - async fetch(request: Request, env: Env): Promise { - const url = new URL(request.url); - if (url.pathname === '/health') return Response.json({ ready: true }); - const stub = exports.MinecraftWorld.getByName(env.WORLD_NAME); - if (request.method === 'GET' && url.pathname === '/') return Response.json(await stub.status()); - return new Response('Not found', { status: 404 }); - }, -} satisfies ExportedHandler; diff --git a/worker/modules.d.ts b/worker/modules.d.ts deleted file mode 100644 index 552bea7..0000000 --- a/worker/modules.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -declare module '*.wasm' { - const module: WebAssembly.Module; - export default module; -} - -declare module '*pumpkin-do.js' { - interface Options { - cwd: string; - nodeFs: typeof import('worker-fs-mount/fs-sync'); - instantiateWasm(imports: WebAssembly.Imports, receive: (instance: WebAssembly.Instance) => void): WebAssembly.Exports; - printErr(message: string): void; - } - interface Pumpkin { - pumpkin_start(): Promise; - pumpkin_connect(socket: Socket): Promise; - pumpkin_status(): { players: number; ticks: number; wasm_memory_bytes: number }; - pumpkin_shutdown(): Promise; - } - export default function create(options: Options): Promise; -} diff --git a/worker/pumpkin.ts b/worker/pumpkin.ts deleted file mode 100644 index c8b86fd..0000000 --- a/worker/pumpkin.ts +++ /dev/null @@ -1,22 +0,0 @@ -import createPumpkin from '../target/workers/wasm32-unknown-emscripten/release/pumpkin-do.js'; -import pumpkinWasm from '../target/workers/wasm32-unknown-emscripten/release/pumpkin_do.wasm'; -import { createWorldRuntime } from './filesystem'; - -/** Bind the server API to its instance's filesystem and async context once. */ -export async function startPumpkin(storage: DurableObjectStorage) { - const { instance, run } = await createWorldRuntime(storage, options => createPumpkin({ - ...options, - instantiateWasm(imports, receive) { - const instance = new WebAssembly.Instance(pumpkinWasm, imports); - receive(instance); - return instance.exports; - }, - printErr: console.error, - })); - await run(() => instance.pumpkin_start()); - return { - connect: (socket: Socket) => run(() => instance.pumpkin_connect(socket)), - stop: () => run(() => instance.pumpkin_shutdown()), - status: () => run(() => instance.pumpkin_status()), - }; -} diff --git a/worker/socket.ts b/worker/socket.ts deleted file mode 100644 index 47f9d3b..0000000 --- a/worker/socket.ts +++ /dev/null @@ -1,25 +0,0 @@ -export async function forwardSocket(socket: Socket, target: Socket): Promise { - // Read/write failures are observed by the pumps below. Also consume the - // lifetime promises so a normal peer disconnect is not an unhandled rejection. - void socket.closed.catch(() => undefined); - void target.closed.catch(() => undefined); - const abort = new AbortController(); - const pipe = (source: ReadableStream, destination: WritableStream) => - source.pipeTo(destination, { signal: abort.signal }).catch(error => { - abort.abort(); - throw error; - }); - try { - const results = await Promise.allSettled([ - pipe(socket.readable, target.writable), - pipe(target.readable, socket.writable), - ]); - for (const result of results) { - if (result.status === 'rejected' && !/closed|closing|abort|cancel|reset|network connection lost/i.test(String(result.reason))) { - console.error('TCP forwarding failed', result.reason); - } - } - } finally { - await Promise.allSettled([socket.close(), target.close()]); - } -} diff --git a/worker/world.ts b/worker/world.ts deleted file mode 100644 index f457cfa..0000000 --- a/worker/world.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { DurableObject } from 'cloudflare:workers'; -import { startPumpkin } from './pumpkin'; - -type Runtime = Awaited>; -type State = - | { phase: 'idle' } - | { phase: 'starting'; pending: Promise } - | { phase: 'running'; runtime: Runtime } - | { phase: 'stopping'; pending: Promise } - | { phase: 'failed'; error: string }; - -export class MinecraftWorld extends DurableObject { - private state: State = { phase: 'idle' }; - private connections = 0; - private startupMilliseconds?: number; - private checkpointedAt?: string; - - protected loadRuntime(): Promise { - return startPumpkin(this.ctx.storage); - } - - private fail(error: unknown): void { - if (this.state.phase !== 'failed') { - this.state = { phase: 'failed', error: error instanceof Error ? error.message : String(error) }; - } - } - - protected async getRuntime(): Promise { - const state = this.state; - switch (state.phase) { - case 'running': return state.runtime; - case 'starting': return state.pending; - case 'failed': throw new Error(state.error); - case 'stopping': - await state.pending; - return this.getRuntime(); - case 'idle': { - const started = Date.now(); - const pending = Promise.resolve().then(() => this.loadRuntime()).then(runtime => { - this.startupMilliseconds = Date.now() - started; - this.state = { phase: 'running', runtime }; - return runtime; - }).catch(error => { this.fail(error); throw error; }); - this.state = { phase: 'starting', pending }; - return pending; - } - } - } - - async connect(socket: Socket): Promise { - void socket.closed.catch(() => undefined); - if (this.state.phase === 'stopping') await this.state.pending; - this.connections++; - try { - await (await this.getRuntime()).connect(socket); - } finally { - this.connections--; - await socket.close().catch(() => undefined); - if (this.connections === 0) await this.checkpoint(); - } - } - - private async checkpoint(): Promise { - const state = this.state; - if (state.phase === 'stopping') return state.pending; - if (state.phase !== 'running') return; - const pending = (async () => { - await state.runtime.stop(); - await this.ctx.storage.sync(); - this.checkpointedAt = new Date().toISOString(); - this.state = { phase: 'idle' }; - })().catch(error => { this.fail(error); throw error; }); - this.state = { phase: 'stopping', pending }; - return pending; - } - - status() { - let server: ReturnType | null = null; - if (this.state.phase === 'running') { - try { server = this.state.runtime.status(); } - catch (error) { this.fail(error); } - } - return { - phase: this.state.phase, - connections: this.connections, - failure: this.state.phase === 'failed' ? this.state.error : null, - startup_ms: this.startupMilliseconds ?? null, - checkpointed_at: this.checkpointedAt ?? null, - server, - }; - } -} diff --git a/wrangler.jsonc b/wrangler.jsonc index 7fb22ab..aef4207 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -1,12 +1,14 @@ { "$schema": "node_modules/wrangler/config-schema.json", "name": "rust-workers-minecraft", - "main": "worker/index.ts", + "main": "build/index.js", + "build": { "command": "worker-build --emscripten --tokio --release -- --locked" }, "compatibility_date": "2026-09-05", "compatibility_flags": ["nodejs_compat", "new_module_registry", "no_handle_cross_request_promise_resolution"], "dev": { "ip": "127.0.0.1", "port": 8787 }, "connect": [{ "protocol": "tcp", "address": "127.0.0.1", "port": 25565 }], - "exports": { "MinecraftWorld": { "type": "durable-object", "storage": "sqlite" } }, + "durable_objects": { "bindings": [{ "name": "WORLD", "class_name": "MinecraftWorld" }] }, + "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MinecraftWorld"] }], "vars": { "WORLD_NAME": "world" }, "observability": { "enabled": true, "head_sampling_rate": 1 } }