diff --git a/CHANGELOG.md b/CHANGELOG.md index 36d204d..b6497f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,31 @@ All notable changes to WASM-OJ are recorded here. Releases follow [Semantic Versioning](https://semver.org/) and the contract/package versioning policy in [the versioning policy](docs/versioning.md). +## Unreleased + +- Upgraded `@wasmer/sdk` from 0.10.0 to 0.19.0. Clang, rustc, TypeScript, and Python + runtime-file preparation now run to exit in per-build SDK sandboxes, which removes the + 0.10.0 race that could leave a finished process waiting forever for output EOF and stall + Python preparation or TypeScript compilation under load. Compiled artifacts are unchanged. +- The server compiler child and its rustc stage serve sequential builds and keep their loaded + toolchain packages, so the SDK's package load is paid once per child instead of once per build. + Cancellation, timeout, or failure discards the child; an idle child exits after 30 s, and either + child exits when its parent goes away. The rustc stage is replaced after the browser's stage + budget (two builds). +- `@wasm-oj/browser` ships the SDK's browser runtime files unbundled under + `assets/wasmer-sdk-/` and no longer installs `@wasmer/sdk`. Hosts that copy the + package's `dist/assets/` must copy that directory recursively. The SDK's WISP networking + module is not shipped, and its host-function trampoline is built without `eval`, so + execution still works under a strict Content Security Policy. +- Python execution reads the interpreter module directly from the pinned WEBC package and + verifies its digest. +- Raw Clang output names `/workspace/...` instead of `/project/...` source paths. Parsed + diagnostics and emitted artifacts are unchanged. +- The runtime identity changes with the SDK, so artifacts cached by earlier releases are + rebuilt. +- `@wasmer/sdk` 0.19.0 is distributed under Wasmer's Modified MIT License, which adds an + attribution condition for large commercial deployments; see `THIRD_PARTY_NOTICES.md`. + ## 0.2.3 - 2026-10-05 - Download pinned browser toolchain assets before the compiler timeout starts, so slow diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index bd70d1b..86dbc20 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -49,21 +49,28 @@ canonical Go module `h1:` hash over module ZIP entries. ### Wasmer JavaScript SDK -- Component: `@wasmer/sdk` 0.10.0 -- npm integrity: `sha512-YQ+s5tGag6P/I8kp9BTH+XhjoS9UFvWiZJvnWEEovClHffhYToKhprWr4UJG7wLP7c/2HQpGkF7ZrjoUvKjdmA==` -- Source revision: `wasmerio/wasmer-js@93b8b738ebd3ee57e118da0f0eb795b97d5b999e` +- Component: `@wasmer/sdk` 0.19.0 +- npm integrity: `sha512-4gdWiIlne8ti3dQl1yD6jrLLNKrQXmZXA6iy6ta/6sgYpixPLwymeGqhMzjAIHqhmwrD+SjPFpZ5ugd9SytDrQ==` +- Source revision: `wasmerio/wasmer-sdk@7e69332b7f65dbc5584d64bb79f547eaf4302b69` - Locked Rust graph: `Cargo.lock` SHA-256 - `d352926f3f05e3d4308c4e261711d07db568e5c2b4387067180f920da074791f` -- Source: -- License: MIT -- License material: `licenses/wasmer-sdk-MIT.txt`, + `34156a76319127aa4152e8b25bd92a5657f7ddc0669ba3e26209cc57053f56a1` +- Source: +- License: Wasmer Modified MIT License. It adds one condition to MIT: a commercial product or + service with more than 1 million monthly active users or more than 1 million US dollars in + monthly revenue must prominently display "Wasmer" in its user interface. +- Bundled JavaScript dependency: `acorn` 8.18.0 (MIT), shipped in the SDK's wasm-bindgen snippets +- License material: `licenses/wasmer-sdk-LICENSE.txt`, `licenses/wasmer-sdk-acorn-MIT.txt`, `licenses/wasmer-sdk-dependencies.html`, and `licenses/wasmer-sdk-dependencies.json` -WASM-OJ consumes the official npm artifact without patching the installed -package. The generated dependency report covers every package in the pinned -normal Cargo dependency graph selected for `wasm32-unknown-unknown`; its compact -inventory binds all 332 package identities and the exact HTML report digest. +WASM-OJ ships the official npm runtime files unbundled. Its only modification replaces the +wasm-bindgen glue's `new Function` construction of Wasmer's fixed host-function trampoline with +the equivalent closure so the SDK runs under a strict Content Security Policy. The browser build +omits the SDK's WISP networking module, the only path to its AGPL-3.0 `@mercuryworkshop/wisp-js` +dependency, which WASM-OJ therefore does not distribute. The generated dependency report covers +every package in the pinned normal Cargo dependency graph of the SDK's `wasmer-sdk-js` crate +selected for `wasm32-unknown-unknown`; its compact inventory binds all 316 package identities and +the exact HTML report digest. ### QuickJS guest Node standard I/O libraries diff --git a/docs/architecture.md b/docs/architecture.md index 2c60251..f819ba7 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -51,9 +51,9 @@ flowchart LR Browser requests cross dedicated module-Worker boundaries. C/C++ retains bounded immutable Clang and content-addressed graph state; Rust and Go use serialized nested stages with bounded generation -lifetime. Python and JavaScript package source files directly. Server builds use a fresh isolated child. Cancellation, -restart, timeout, cache clearing, disposal, family switch, and stage-budget exhaustion establish a -complete browser Worker-generation boundary. +lifetime. Python and JavaScript package source files directly. Server builds run one at a time in a +reusable isolated child. Cancellation, restart, timeout, cache clearing, disposal, family switch, +and stage-budget exhaustion establish a complete browser Worker-generation boundary. The runner is one Rust codebase compiled to browser Wasm and native executables. Both forms admit the same artifacts, deterministic inputs, resource limits, denied capabilities, filesystem model, @@ -79,20 +79,22 @@ arguments for `wasm32-unknown-wasip1`. Both public `wasip1` and `wasix` profiles compiler ABI; `wasix` changes artifact/cache/runtime-profile identity, not emitted bytes for identical input. The runner validates actual module imports. -Browser compilation directly invokes packaged atoms. Successful clean translation units enter a -content-addressed dependency graph. The browser persists a digest-verified graph in IndexedDB; -warning-producing units are rebuilt because diagnostics cannot be reconstructed faithfully. +Browser compilation directly invokes packaged atoms in a per-build SDK sandbox whose project root is +`/workspace`; macro and debug prefix maps keep the pinned `/project` paths in emitted objects. +Successful clean translation units enter a content-addressed dependency graph. The browser persists +a digest-verified graph in IndexedDB; warning-producing units are rebuilt because diagnostics cannot +be reconstructed faithfully. `wasm-oj.pch.hpp` selects a separately pinned debug/release libc++ PCH only when its content equals `WASM_OJ_LIBCXX_PCH_HEADER` exactly. ### Rust The Rust package contains rustc 1.91.1-dev, its matching `wasm32-wasip1-threads` standard library, -and pinned wasm-ld resources. A browser stage keeps verified immutable package/command handles warm -while creating fresh project directories and command instances for each build. rustc emits the -crate object plus allocator bitcode; a fresh linker command produces the standalone Wasm artifact. -The generation is recycled before its output-ready stage budget is exceeded. Server compilation -uses the same content and arguments in a one-shot child. +and pinned wasm-ld resources. A browser stage keeps its SDK client and verified immutable package +warm while creating a fresh sandbox for each build. rustc emits the crate object plus allocator +bitcode; a fresh linker process produces the standalone Wasm artifact. The generation is recycled +before its stage budget is exceeded. Server compilation uses the same content and arguments in a +child that keeps the loaded package for sequential builds and is replaced under the same budget. The resulting shared-memory module remains a `wasip1` artifact. The host supplies imported memory but denies guest thread spawning. diff --git a/docs/integration-guide.md b/docs/integration-guide.md index 33d1f6a..4f8bfeb 100644 --- a/docs/integration-guide.md +++ b/docs/integration-guide.md @@ -102,6 +102,10 @@ The page must be cross-origin isolated: COOP `same-origin`, COEP `require-corp`, `same-origin`, and `worker-src 'self' blob:` are required. Cross-origin asset servers must emit compatible CORS/CORP headers. +`@wasm-oj/browser` resolves its Workers and the `assets/wasmer-sdk-/` runtime directory +relative to its own `dist/assets/` files. Hosts that copy those assets instead of bundling them must +copy the directory recursively and keep its layout. + ### Prefetching a toolchain `BrowserCompiler` downloads a build's pinned toolchain assets before its build boundary (60 s for diff --git a/docs/library-contract.md b/docs/library-contract.md index 6531588..bc33459 100644 --- a/docs/library-contract.md +++ b/docs/library-contract.md @@ -125,9 +125,15 @@ nested stages with bounded lifetime. Python and JavaScript package source files crossing a stage budget, cancellation, timeout, restart, cache clearing, disposal, or infrastructure failure establishes a complete Worker-generation boundary. -Wasmer secondary Workers are host implementation details. They use the SDK's supported `workerUrl` -protocol and do not grant guest thread-spawn capability. The host page must be cross-origin -isolated. +Compiler and runtime-preparation processes use the official `@wasmer/sdk`. `@wasm-oj/browser` ships +its browser runtime files unbundled under `assets/wasmer-sdk-/`, without the SDK's WISP +networking module, and Workers import the SDK from there. SDK thread Workers are host implementation +details: they start from the same same-origin blob bootstrap as other module Workers and do not +grant guest thread-spawn capability. The host page must be cross-origin isolated. + +SDK processes run to exit; WASM-OJ never calls the SDK's process kill or client shutdown, which +terminate threads at arbitrary points. Cancellation and timeouts discard the whole Worker generation +or server child instead. ## Server execution boundary @@ -136,10 +142,12 @@ isolated. Initialization verifies executable regular files and every descriptor asset before constructing the engine. -Each server build uses a fresh isolated compiler child. Each execution uses the native Rust runtime -process and a bounded request/response transport. Server startup never provisions binaries, -downloads assets, invokes a host compiler for user source, follows a symlink-owned toolchain path, -or falls back to another directory. +Server builds run one at a time in an isolated compiler child that keeps its loaded toolchain +packages and creates a fresh SDK sandbox for each build. Cancellation, timeout, or failure discards +the child; it exits after a short idle period or when its parent goes away. Each execution uses +the native Rust runtime process and a bounded request/response transport. Server startup never +provisions binaries, downloads assets, invokes a host compiler for user source, follows a +symlink-owned toolchain path, or falls back to another directory. The default writable cache root is `.wasm-oj` below the current working directory when the caller does not provide `cacheDirectory`; a filesystem root is never accepted. diff --git a/licenses/components.json b/licenses/components.json index ad1a61e..7a4d5dd 100644 --- a/licenses/components.json +++ b/licenses/components.json @@ -773,40 +773,44 @@ { "id": "wasmer-sdk", "name": "Wasmer JavaScript SDK and locked Rust dependency closure", - "version": "0.10.0", + "version": "0.19.0", "source": { - "url": "https://github.com/wasmerio/wasmer-js", - "revision": "93b8b738ebd3ee57e118da0f0eb795b97d5b999e" + "url": "https://github.com/wasmerio/wasmer-sdk", + "revision": "7e69332b7f65dbc5584d64bb79f547eaf4302b69" }, "distributions": [ { "kind": "file", "path": "licenses/wasmer-sdk-dependencies.html", - "sha256": "ec4b0175b4d1affb0f97996cb6661ec6fa1c5959dbce637d89b39cf3559b079a" + "sha256": "93d2898132e31cd7634c1470a587a7b0f54e887bcb0a968742260f2f985f643d" }, { "kind": "file", "path": "licenses/wasmer-sdk-dependencies.json", - "sha256": "4fb0d9e02e29f98b5c0ec047fa041cce870d98b9fdfecf2c02436a19842d0977" + "sha256": "39cac76585998208f43bb66076840c6fddc4e91eb7ef4cc2d9b1f90287b461f3" }, { "kind": "npm", - "package": "@wasmer/sdk@0.10.0", - "integrity": "sha512-YQ+s5tGag6P/I8kp9BTH+XhjoS9UFvWiZJvnWEEovClHffhYToKhprWr4UJG7wLP7c/2HQpGkF7ZrjoUvKjdmA==" + "package": "@wasmer/sdk@0.19.0", + "integrity": "sha512-4gdWiIlne8ti3dQl1yD6jrLLNKrQXmZXA6iy6ta/6sgYpixPLwymeGqhMzjAIHqhmwrD+SjPFpZ5ugd9SytDrQ==" } ], "licenseFiles": [ { - "path": "licenses/wasmer-sdk-MIT.txt", - "sha256": "76dc7d305458d07478bc62669fe53dbfd3b94b95c5e00fbb45af1f492cbd7284" + "path": "licenses/wasmer-sdk-LICENSE.txt", + "sha256": "6350e730ed61a3ec83b5843634aa0a476505ec9b96792600606ced736835b45c" + }, + { + "path": "licenses/wasmer-sdk-acorn-MIT.txt", + "sha256": "76a876cf886ff9be2a8b5e2e86514fed06223c8c9f0c1e9ee9606e93841e00b7" }, { "path": "licenses/wasmer-sdk-dependencies.html", - "sha256": "ec4b0175b4d1affb0f97996cb6661ec6fa1c5959dbce637d89b39cf3559b079a" + "sha256": "93d2898132e31cd7634c1470a587a7b0f54e887bcb0a968742260f2f985f643d" }, { "path": "licenses/wasmer-sdk-dependencies.json", - "sha256": "4fb0d9e02e29f98b5c0ec047fa041cce870d98b9fdfecf2c02436a19842d0977" + "sha256": "39cac76585998208f43bb66076840c6fddc4e91eb7ef4cc2d9b1f90287b461f3" } ] } diff --git a/licenses/wasmer-sdk-LICENSE.txt b/licenses/wasmer-sdk-LICENSE.txt new file mode 100644 index 0000000..cdcdf1a --- /dev/null +++ b/licenses/wasmer-sdk-LICENSE.txt @@ -0,0 +1,27 @@ +Modified MIT License + +Copyright (c) 2026 Wasmer, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Our only modification part is that, if the Software (or any derivative works +thereof) is used for any of your commercial products or services that have +more than 1 million monthly active users, or more than 1 million US dollars +(or equivalent in other currencies) in monthly revenue, you shall prominently +display "Wasmer" on the user interface of such product or service. diff --git a/licenses/wasmer-sdk-MIT.txt b/licenses/wasmer-sdk-acorn-MIT.txt similarity index 86% rename from licenses/wasmer-sdk-MIT.txt rename to licenses/wasmer-sdk-acorn-MIT.txt index 62bb543..9d71cc6 100644 --- a/licenses/wasmer-sdk-MIT.txt +++ b/licenses/wasmer-sdk-acorn-MIT.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019-present Wasmer, Inc. and its affiliates. +Copyright (C) 2012-2022 by various contributors (see AUTHORS) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/licenses/wasmer-sdk-dependencies.html b/licenses/wasmer-sdk-dependencies.html index 58d3cf6..0ff2bf4 100644 --- a/licenses/wasmer-sdk-dependencies.html +++ b/licenses/wasmer-sdk-dependencies.html @@ -15,7 +15,7 @@

Wasmer JavaScript SDK dependency licenses

Generated from the locked normal dependency graph for the exact source - revision used by @wasmer/sdk 0.10.0, targeting + revision used by @wasmer/sdk 0.19.0, targeting wasm32-unknown-unknown.

@@ -23,22 +23,26 @@

BSD Zero Clause License (0BSD)

Used by:

-
Copyright (C) 2016 whitequark@whitequark.org
+    
BSD Zero Clause License
 
-Permission to use, copy, modify, and/or distribute this software for
-any purpose with or without fee is hereby granted.
+Copyright (c) 2018 Stephane Raux
 
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
-AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.
 
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
 
@@ -68,17 +72,22 @@

BSD Zero Clause License (0BSD)

Used by:

-
Copyright (C) YEAR by AUTHOR EMAIL
+    
Copyright (C) smoltcp contributors
+
+Permission to use, copy, modify, and/or distribute this software for
+any purpose with or without fee is hereby granted.
 
-Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
@@ -86,7 +95,7 @@

Apache License 2.0 (Apache-2.0)

Used by:

@@ -325,7 +334,7 @@ 

Apache License 2.0 (Apache-2.0)

ciborium 0.2.2
  • - self_cell 1.0.3 + self_cell 1.3.0
  •                                  Apache License
    @@ -536,7 +545,7 @@ 

    Apache License 2.0 (Apache-2.0)

    Used by:

                                     Apache License
    @@ -740,6 +749,7 @@ 

    Apache License 2.0 (Apache-2.0)

    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +
    @@ -747,574 +757,112 @@

    Apache License 2.0 (Apache-2.0)

    Used by:

    -
                                     Apache License
    -                           Version 2.0, January 2004
    -                        http://www.apache.org/licenses/
    +    
    Apache License
    +Version 2.0, January 2004
    +http://www.apache.org/licenses/
     
    -   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
     
    -   1. Definitions.
    +1. Definitions.
     
    -      "License" shall mean the terms and conditions for use, reproduction,
    -      and distribution as defined by Sections 1 through 9 of this document.
    +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
     
    -      "Licensor" shall mean the copyright owner or entity authorized by
    -      the copyright owner that is granting the License.
    +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
     
    -      "Legal Entity" shall mean the union of the acting entity and all
    -      other entities that control, are controlled by, or are under common
    -      control with that entity. For the purposes of this definition,
    -      "control" means (i) the power, direct or indirect, to cause the
    -      direction or management of such entity, whether by contract or
    -      otherwise, or (ii) ownership of fifty percent (50%) or more of the
    -      outstanding shares, or (iii) beneficial ownership of such entity.
    +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
     
    -      "You" (or "Your") shall mean an individual or Legal Entity
    -      exercising permissions granted by this License.
    +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
     
    -      "Source" form shall mean the preferred form for making modifications,
    -      including but not limited to software source code, documentation
    -      source, and configuration files.
    +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
     
    -      "Object" form shall mean any form resulting from mechanical
    -      transformation or translation of a Source form, including but
    -      not limited to compiled object code, generated documentation,
    -      and conversions to other media types.
    +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
     
    -      "Work" shall mean the work of authorship, whether in Source or
    -      Object form, made available under the License, as indicated by a
    -      copyright notice that is included in or attached to the work
    -      (an example is provided in the Appendix below).
    +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
     
    -      "Derivative Works" shall mean any work, whether in Source or Object
    -      form, that is based on (or derived from) the Work and for which the
    -      editorial revisions, annotations, elaborations, or other modifications
    -      represent, as a whole, an original work of authorship. For the purposes
    -      of this License, Derivative Works shall not include works that remain
    -      separable from, or merely link (or bind by name) to the interfaces of,
    -      the Work and Derivative Works thereof.
    +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
     
    -      "Contribution" shall mean any work of authorship, including
    -      the original version of the Work and any modifications or additions
    -      to that Work or Derivative Works thereof, that is intentionally
    -      submitted to Licensor for inclusion in the Work by the copyright owner
    -      or by an individual or Legal Entity authorized to submit on behalf of
    -      the copyright owner. For the purposes of this definition, "submitted"
    -      means any form of electronic, verbal, or written communication sent
    -      to the Licensor or its representatives, including but not limited to
    -      communication on electronic mailing lists, source code control systems,
    -      and issue tracking systems that are managed by, or on behalf of, the
    -      Licensor for the purpose of discussing and improving the Work, but
    -      excluding communication that is conspicuously marked or otherwise
    -      designated in writing by the copyright owner as "Not a Contribution."
    +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
     
    -      "Contributor" shall mean Licensor and any individual or Legal Entity
    -      on behalf of whom a Contribution has been received by Licensor and
    -      subsequently incorporated within the Work.
    +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
     
    -   2. Grant of Copyright License. Subject to the terms and conditions of
    -      this License, each Contributor hereby grants to You a perpetual,
    -      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -      copyright license to reproduce, prepare Derivative Works of,
    -      publicly display, publicly perform, sublicense, and distribute the
    -      Work and such Derivative Works in Source or Object form.
    +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
     
    -   3. Grant of Patent License. Subject to the terms and conditions of
    -      this License, each Contributor hereby grants to You a perpetual,
    -      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -      (except as stated in this section) patent license to make, have made,
    -      use, offer to sell, sell, import, and otherwise transfer the Work,
    -      where such license applies only to those patent claims licensable
    -      by such Contributor that are necessarily infringed by their
    -      Contribution(s) alone or by combination of their Contribution(s)
    -      with the Work to which such Contribution(s) was submitted. If You
    -      institute patent litigation against any entity (including a
    -      cross-claim or counterclaim in a lawsuit) alleging that the Work
    -      or a Contribution incorporated within the Work constitutes direct
    -      or contributory patent infringement, then any patent licenses
    -      granted to You under this License for that Work shall terminate
    -      as of the date such litigation is filed.
    +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
     
    -   4. Redistribution. You may reproduce and distribute copies of the
    -      Work or Derivative Works thereof in any medium, with or without
    -      modifications, and in Source or Object form, provided that You
    -      meet the following conditions:
    +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
     
    -      (a) You must give any other recipients of the Work or
    -          Derivative Works a copy of this License; and
    +     (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
     
    -      (b) You must cause any modified files to carry prominent notices
    -          stating that You changed the files; and
    +     (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
     
    -      (c) You must retain, in the Source form of any Derivative Works
    -          that You distribute, all copyright, patent, trademark, and
    -          attribution notices from the Source form of the Work,
    -          excluding those notices that do not pertain to any part of
    -          the Derivative Works; and
    +     (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
     
    -      (d) If the Work includes a "NOTICE" text file as part of its
    -          distribution, then any Derivative Works that You distribute must
    -          include a readable copy of the attribution notices contained
    -          within such NOTICE file, excluding those notices that do not
    -          pertain to any part of the Derivative Works, in at least one
    -          of the following places: within a NOTICE text file distributed
    -          as part of the Derivative Works; within the Source form or
    -          documentation, if provided along with the Derivative Works; or,
    -          within a display generated by the Derivative Works, if and
    -          wherever such third-party notices normally appear. The contents
    -          of the NOTICE file are for informational purposes only and
    -          do not modify the License. You may add Your own attribution
    -          notices within Derivative Works that You distribute, alongside
    -          or as an addendum to the NOTICE text from the Work, provided
    -          that such additional attribution notices cannot be construed
    -          as modifying the License.
    +     (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
     
    -      You may add Your own copyright statement to Your modifications and
    -      may provide additional or different license terms and conditions
    -      for use, reproduction, or distribution of Your modifications, or
    -      for any such Derivative Works as a whole, provided Your use,
    -      reproduction, and distribution of the Work otherwise complies with
    -      the conditions stated in this License.
    +     You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
     
    -   5. Submission of Contributions. Unless You explicitly state otherwise,
    -      any Contribution intentionally submitted for inclusion in the Work
    -      by You to the Licensor shall be under the terms and conditions of
    -      this License, without any additional terms or conditions.
    -      Notwithstanding the above, nothing herein shall supersede or modify
    -      the terms of any separate license agreement you may have executed
    -      with Licensor regarding such Contributions.
    +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
     
    -   6. Trademarks. This License does not grant permission to use the trade
    -      names, trademarks, service marks, or product names of the Licensor,
    -      except as required for reasonable and customary use in describing the
    -      origin of the Work and reproducing the content of the NOTICE file.
    +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
     
    -   7. Disclaimer of Warranty. Unless required by applicable law or
    -      agreed to in writing, Licensor provides the Work (and each
    -      Contributor provides its Contributions) on an "AS IS" BASIS,
    -      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    -      implied, including, without limitation, any warranties or conditions
    -      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    -      PARTICULAR PURPOSE. You are solely responsible for determining the
    -      appropriateness of using or redistributing the Work and assume any
    -      risks associated with Your exercise of permissions under this License.
    +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
     
    -   8. Limitation of Liability. In no event and under no legal theory,
    -      whether in tort (including negligence), contract, or otherwise,
    -      unless required by applicable law (such as deliberate and grossly
    -      negligent acts) or agreed to in writing, shall any Contributor be
    -      liable to You for damages, including any direct, indirect, special,
    -      incidental, or consequential damages of any character arising as a
    -      result of this License or out of the use or inability to use the
    -      Work (including but not limited to damages for loss of goodwill,
    -      work stoppage, computer failure or malfunction, or any and all
    -      other commercial damages or losses), even if such Contributor
    -      has been advised of the possibility of such damages.
    +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
     
    -   9. Accepting Warranty or Additional Liability. While redistributing
    -      the Work or Derivative Works thereof, You may choose to offer,
    -      and charge a fee for, acceptance of support, warranty, indemnity,
    -      or other liability obligations and/or rights consistent with this
    -      License. However, in accepting such obligations, You may act only
    -      on Your own behalf and on Your sole responsibility, not on behalf
    -      of any other Contributor, and only if You agree to indemnify,
    -      defend, and hold each Contributor harmless for any liability
    -      incurred by, or claims asserted against, such Contributor by reason
    -      of your accepting any such warranty or additional liability.
    +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
     
    -   END OF TERMS AND CONDITIONS
    +END OF TERMS AND CONDITIONS
     
    -   APPENDIX: How to apply the Apache License to your work.
    +APPENDIX: How to apply the Apache License to your work.
     
    -      To apply the Apache License to your work, attach the following
    -      boilerplate notice, with the fields enclosed by brackets "{}"
    -      replaced with your own identifying information. (Don't include
    -      the brackets!)  The text should be enclosed in the appropriate
    -      comment syntax for the file format. We also recommend that a
    -      file or class name and description of purpose be included on the
    -      same "printed page" as the copyright notice for easier
    -      identification within third-party archives.
    +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!)  The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
     
    -   Copyright {yyyy} {name of copyright owner}
    +Copyright [yyyy] [name of copyright owner]
     
    -   Licensed under the Apache License, Version 2.0 (the "License");
    -   you may not use this file except in compliance with the License.
    -   You may obtain a copy of the License at
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
     
    -       http://www.apache.org/licenses/LICENSE-2.0
    -
    -   Unless required by applicable law or agreed to in writing, software
    -   distributed under the License is distributed on an "AS IS" BASIS,
    -   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    -   See the License for the specific language governing permissions and
    -   limitations under the License.
    -
    -
    -
    -
    -

    Apache License 2.0 (Apache-2.0)

    -

    Used by:

    - -
                                  Apache License
    -                        Version 2.0, January 2004
    -                     http://www.apache.org/licenses/
    -
    -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    -
    -1. Definitions.
    -
    -   "License" shall mean the terms and conditions for use, reproduction,
    -   and distribution as defined by Sections 1 through 9 of this document.
    -
    -   "Licensor" shall mean the copyright owner or entity authorized by
    -   the copyright owner that is granting the License.
    -
    -   "Legal Entity" shall mean the union of the acting entity and all
    -   other entities that control, are controlled by, or are under common
    -   control with that entity. For the purposes of this definition,
    -   "control" means (i) the power, direct or indirect, to cause the
    -   direction or management of such entity, whether by contract or
    -   otherwise, or (ii) ownership of fifty percent (50%) or more of the
    -   outstanding shares, or (iii) beneficial ownership of such entity.
    -
    -   "You" (or "Your") shall mean an individual or Legal Entity
    -   exercising permissions granted by this License.
    -
    -   "Source" form shall mean the preferred form for making modifications,
    -   including but not limited to software source code, documentation
    -   source, and configuration files.
    -
    -   "Object" form shall mean any form resulting from mechanical
    -   transformation or translation of a Source form, including but
    -   not limited to compiled object code, generated documentation,
    -   and conversions to other media types.
    -
    -   "Work" shall mean the work of authorship, whether in Source or
    -   Object form, made available under the License, as indicated by a
    -   copyright notice that is included in or attached to the work
    -   (an example is provided in the Appendix below).
    -
    -   "Derivative Works" shall mean any work, whether in Source or Object
    -   form, that is based on (or derived from) the Work and for which the
    -   editorial revisions, annotations, elaborations, or other modifications
    -   represent, as a whole, an original work of authorship. For the purposes
    -   of this License, Derivative Works shall not include works that remain
    -   separable from, or merely link (or bind by name) to the interfaces of,
    -   the Work and Derivative Works thereof.
    -
    -   "Contribution" shall mean any work of authorship, including
    -   the original version of the Work and any modifications or additions
    -   to that Work or Derivative Works thereof, that is intentionally
    -   submitted to Licensor for inclusion in the Work by the copyright owner
    -   or by an individual or Legal Entity authorized to submit on behalf of
    -   the copyright owner. For the purposes of this definition, "submitted"
    -   means any form of electronic, verbal, or written communication sent
    -   to the Licensor or its representatives, including but not limited to
    -   communication on electronic mailing lists, source code control systems,
    -   and issue tracking systems that are managed by, or on behalf of, the
    -   Licensor for the purpose of discussing and improving the Work, but
    -   excluding communication that is conspicuously marked or otherwise
    -   designated in writing by the copyright owner as "Not a Contribution."
    -
    -   "Contributor" shall mean Licensor and any individual or Legal Entity
    -   on behalf of whom a Contribution has been received by Licensor and
    -   subsequently incorporated within the Work.
    -
    -2. Grant of Copyright License. Subject to the terms and conditions of
    -   this License, each Contributor hereby grants to You a perpetual,
    -   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -   copyright license to reproduce, prepare Derivative Works of,
    -   publicly display, publicly perform, sublicense, and distribute the
    -   Work and such Derivative Works in Source or Object form.
    -
    -3. Grant of Patent License. Subject to the terms and conditions of
    -   this License, each Contributor hereby grants to You a perpetual,
    -   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
    -   (except as stated in this section) patent license to make, have made,
    -   use, offer to sell, sell, import, and otherwise transfer the Work,
    -   where such license applies only to those patent claims licensable
    -   by such Contributor that are necessarily infringed by their
    -   Contribution(s) alone or by combination of their Contribution(s)
    -   with the Work to which such Contribution(s) was submitted. If You
    -   institute patent litigation against any entity (including a
    -   cross-claim or counterclaim in a lawsuit) alleging that the Work
    -   or a Contribution incorporated within the Work constitutes direct
    -   or contributory patent infringement, then any patent licenses
    -   granted to You under this License for that Work shall terminate
    -   as of the date such litigation is filed.
    -
    -4. Redistribution. You may reproduce and distribute copies of the
    -   Work or Derivative Works thereof in any medium, with or without
    -   modifications, and in Source or Object form, provided that You
    -   meet the following conditions:
    -
    -   (a) You must give any other recipients of the Work or
    -       Derivative Works a copy of this License; and
    -
    -   (b) You must cause any modified files to carry prominent notices
    -       stating that You changed the files; and
    -
    -   (c) You must retain, in the Source form of any Derivative Works
    -       that You distribute, all copyright, patent, trademark, and
    -       attribution notices from the Source form of the Work,
    -       excluding those notices that do not pertain to any part of
    -       the Derivative Works; and
    -
    -   (d) If the Work includes a "NOTICE" text file as part of its
    -       distribution, then any Derivative Works that You distribute must
    -       include a readable copy of the attribution notices contained
    -       within such NOTICE file, excluding those notices that do not
    -       pertain to any part of the Derivative Works, in at least one
    -       of the following places: within a NOTICE text file distributed
    -       as part of the Derivative Works; within the Source form or
    -       documentation, if provided along with the Derivative Works; or,
    -       within a display generated by the Derivative Works, if and
    -       wherever such third-party notices normally appear. The contents
    -       of the NOTICE file are for informational purposes only and
    -       do not modify the License. You may add Your own attribution
    -       notices within Derivative Works that You distribute, alongside
    -       or as an addendum to the NOTICE text from the Work, provided
    -       that such additional attribution notices cannot be construed
    -       as modifying the License.
    -
    -   You may add Your own copyright statement to Your modifications and
    -   may provide additional or different license terms and conditions
    -   for use, reproduction, or distribution of Your modifications, or
    -   for any such Derivative Works as a whole, provided Your use,
    -   reproduction, and distribution of the Work otherwise complies with
    -   the conditions stated in this License.
    -
    -5. Submission of Contributions. Unless You explicitly state otherwise,
    -   any Contribution intentionally submitted for inclusion in the Work
    -   by You to the Licensor shall be under the terms and conditions of
    -   this License, without any additional terms or conditions.
    -   Notwithstanding the above, nothing herein shall supersede or modify
    -   the terms of any separate license agreement you may have executed
    -   with Licensor regarding such Contributions.
    -
    -6. Trademarks. This License does not grant permission to use the trade
    -   names, trademarks, service marks, or product names of the Licensor,
    -   except as required for reasonable and customary use in describing the
    -   origin of the Work and reproducing the content of the NOTICE file.
    -
    -7. Disclaimer of Warranty. Unless required by applicable law or
    -   agreed to in writing, Licensor provides the Work (and each
    -   Contributor provides its Contributions) on an "AS IS" BASIS,
    -   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    -   implied, including, without limitation, any warranties or conditions
    -   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
    -   PARTICULAR PURPOSE. You are solely responsible for determining the
    -   appropriateness of using or redistributing the Work and assume any
    -   risks associated with Your exercise of permissions under this License.
    -
    -8. Limitation of Liability. In no event and under no legal theory,
    -   whether in tort (including negligence), contract, or otherwise,
    -   unless required by applicable law (such as deliberate and grossly
    -   negligent acts) or agreed to in writing, shall any Contributor be
    -   liable to You for damages, including any direct, indirect, special,
    -   incidental, or consequential damages of any character arising as a
    -   result of this License or out of the use or inability to use the
    -   Work (including but not limited to damages for loss of goodwill,
    -   work stoppage, computer failure or malfunction, or any and all
    -   other commercial damages or losses), even if such Contributor
    -   has been advised of the possibility of such damages.
    -
    -9. Accepting Warranty or Additional Liability. While redistributing
    -   the Work or Derivative Works thereof, You may choose to offer,
    -   and charge a fee for, acceptance of support, warranty, indemnity,
    -   or other liability obligations and/or rights consistent with this
    -   License. However, in accepting such obligations, You may act only
    -   on Your own behalf and on Your sole responsibility, not on behalf
    -   of any other Contributor, and only if You agree to indemnify,
    -   defend, and hold each Contributor harmless for any liability
    -   incurred by, or claims asserted against, such Contributor by reason
    -   of your accepting any such warranty or additional liability.
    -
    -END OF TERMS AND CONDITIONS
    -
    -APPENDIX: How to apply the Apache License to your work.
    -
    -   To apply the Apache License to your work, attach the following
    -   boilerplate notice, with the fields enclosed by brackets "[]"
    -   replaced with your own identifying information. (Don't include
    -   the brackets!)  The text should be enclosed in the appropriate
    -   comment syntax for the file format. We also recommend that a
    -   file or class name and description of purpose be included on the
    -   same "printed page" as the copyright notice for easier
    -   identification within third-party archives.
    -
    -Copyright [yyyy] [name of copyright owner]
    -
    -Licensed under the Apache License, Version 2.0 (the "License");
    -you may not use this file except in compliance with the License.
    -You may obtain a copy of the License at
    -
    -	http://www.apache.org/licenses/LICENSE-2.0
    -
    -Unless required by applicable law or agreed to in writing, software
    -distributed under the License is distributed on an "AS IS" BASIS,
    -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    -See the License for the specific language governing permissions and
    -limitations under the License.
    -
    -
    -
    -

    Apache License 2.0 (Apache-2.0)

    -

    Used by:

    - -
    Apache License
    -Version 2.0, January 2004
    -http://www.apache.org/licenses/
    -
    -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
    -
    -1. Definitions.
    -
    -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
    -
    -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
    -
    -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
    -
    -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
    -
    -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
    -
    -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
    -
    -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
    -
    -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
    -
    -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
    -
    -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
    -
    -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
    -
    -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
    -
    -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
    -
    -     (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
    -
    -     (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
    -
    -     (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
    -
    -     (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
    -
    -     You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
    -
    -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
    -
    -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
    -
    -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
    -
    -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
    -
    -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
    -
    -END OF TERMS AND CONDITIONS
    -
    -APPENDIX: How to apply the Apache License to your work.
    -
    -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!)  The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
    -
    -Copyright [yyyy] [name of copyright owner]
    -
    -Licensed under the Apache License, Version 2.0 (the "License");
    -you may not use this file except in compliance with the License.
    -You may obtain a copy of the License at
    -
    -http://www.apache.org/licenses/LICENSE-2.0
    +http://www.apache.org/licenses/LICENSE-2.0
     
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
    -
    -
    -
    -

    BSD 2-Clause "Simplified" License (BSD-2-Clause)

    -

    Used by:

    - -
    Copyright (c) 2015 David Roundy <roundyd@physics.oregonstate.edu>
    -All rights reserved.
    -
    -Redistribution and use in source and binary forms, with or without
    -modification, are permitted provided that the following conditions are
    -met:
    -
    -1. Redistributions of source code must retain the above copyright
    -   notice, this list of conditions and the following disclaimer.
    -
    -2. Redistributions in binary form must reproduce the above copyright
    -   notice, this list of conditions and the following disclaimer in the
    -   documentation and/or other materials provided with the
    -   distribution.
    -
    -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     
    @@ -1322,7 +870,7 @@

    BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)Used by:

    Copyright (c) 2019, Sébastien Crozet
    @@ -1390,7 +938,7 @@ 

    Boost Software License 1.0 (BSL-1.0)

    Used by:

    Boost Software License - Version 1.0 - August 17th, 2003
    @@ -1416,199 +964,6 @@ 

    Boost Software License 1.0 (BSL-1.0)

    FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -
    -

    -
    -

    Creative Commons Zero v1.0 Universal (CC0-1.0)

    -

    Used by:

    - -
    Creative Commons Legal Code
    -
    -CC0 1.0 Universal
    -
    -    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
    -    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
    -    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
    -    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
    -    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
    -    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
    -    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
    -    HEREUNDER.
    -
    -Statement of Purpose
    -
    -The laws of most jurisdictions throughout the world automatically confer
    -exclusive Copyright and Related Rights (defined below) upon the creator
    -and subsequent owner(s) (each and all, an "owner") of an original work of
    -authorship and/or a database (each, a "Work").
    -
    -Certain owners wish to permanently relinquish those rights to a Work for
    -the purpose of contributing to a commons of creative, cultural and
    -scientific works ("Commons") that the public can reliably and without fear
    -of later claims of infringement build upon, modify, incorporate in other
    -works, reuse and redistribute as freely as possible in any form whatsoever
    -and for any purposes, including without limitation commercial purposes.
    -These owners may contribute to the Commons to promote the ideal of a free
    -culture and the further production of creative, cultural and scientific
    -works, or to gain reputation or greater distribution for their Work in
    -part through the use and efforts of others.
    -
    -For these and/or other purposes and motivations, and without any
    -expectation of additional consideration or compensation, the person
    -associating CC0 with a Work (the "Affirmer"), to the extent that he or she
    -is an owner of Copyright and Related Rights in the Work, voluntarily
    -elects to apply CC0 to the Work and publicly distribute the Work under its
    -terms, with knowledge of his or her Copyright and Related Rights in the
    -Work and the meaning and intended legal effect of CC0 on those rights.
    -
    -1. Copyright and Related Rights. A Work made available under CC0 may be
    -protected by copyright and related or neighboring rights ("Copyright and
    -Related Rights"). Copyright and Related Rights include, but are not
    -limited to, the following:
    -
    -  i. the right to reproduce, adapt, distribute, perform, display,
    -     communicate, and translate a Work;
    - ii. moral rights retained by the original author(s) and/or performer(s);
    -iii. publicity and privacy rights pertaining to a person's image or
    -     likeness depicted in a Work;
    - iv. rights protecting against unfair competition in regards to a Work,
    -     subject to the limitations in paragraph 4(a), below;
    -  v. rights protecting the extraction, dissemination, use and reuse of data
    -     in a Work;
    - vi. database rights (such as those arising under Directive 96/9/EC of the
    -     European Parliament and of the Council of 11 March 1996 on the legal
    -     protection of databases, and under any national implementation
    -     thereof, including any amended or successor version of such
    -     directive); and
    -vii. other similar, equivalent or corresponding rights throughout the
    -     world based on applicable law or treaty, and any national
    -     implementations thereof.
    -
    -2. Waiver. To the greatest extent permitted by, but not in contravention
    -of, applicable law, Affirmer hereby overtly, fully, permanently,
    -irrevocably and unconditionally waives, abandons, and surrenders all of
    -Affirmer's Copyright and Related Rights and associated claims and causes
    -of action, whether now known or unknown (including existing as well as
    -future claims and causes of action), in the Work (i) in all territories
    -worldwide, (ii) for the maximum duration provided by applicable law or
    -treaty (including future time extensions), (iii) in any current or future
    -medium and for any number of copies, and (iv) for any purpose whatsoever,
    -including without limitation commercial, advertising or promotional
    -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
    -member of the public at large and to the detriment of Affirmer's heirs and
    -successors, fully intending that such Waiver shall not be subject to
    -revocation, rescission, cancellation, termination, or any other legal or
    -equitable action to disrupt the quiet enjoyment of the Work by the public
    -as contemplated by Affirmer's express Statement of Purpose.
    -
    -3. Public License Fallback. Should any part of the Waiver for any reason
    -be judged legally invalid or ineffective under applicable law, then the
    -Waiver shall be preserved to the maximum extent permitted taking into
    -account Affirmer's express Statement of Purpose. In addition, to the
    -extent the Waiver is so judged Affirmer hereby grants to each affected
    -person a royalty-free, non transferable, non sublicensable, non exclusive,
    -irrevocable and unconditional license to exercise Affirmer's Copyright and
    -Related Rights in the Work (i) in all territories worldwide, (ii) for the
    -maximum duration provided by applicable law or treaty (including future
    -time extensions), (iii) in any current or future medium and for any number
    -of copies, and (iv) for any purpose whatsoever, including without
    -limitation commercial, advertising or promotional purposes (the
    -"License"). The License shall be deemed effective as of the date CC0 was
    -applied by Affirmer to the Work. Should any part of the License for any
    -reason be judged legally invalid or ineffective under applicable law, such
    -partial invalidity or ineffectiveness shall not invalidate the remainder
    -of the License, and in such case Affirmer hereby affirms that he or she
    -will not (i) exercise any of his or her remaining Copyright and Related
    -Rights in the Work or (ii) assert any associated claims and causes of
    -action with respect to the Work, in either case contrary to Affirmer's
    -express Statement of Purpose.
    -
    -4. Limitations and Disclaimers.
    -
    - a. No trademark or patent rights held by Affirmer are waived, abandoned,
    -    surrendered, licensed or otherwise affected by this document.
    - b. Affirmer offers the Work as-is and makes no representations or
    -    warranties of any kind concerning the Work, express, implied,
    -    statutory or otherwise, including without limitation warranties of
    -    title, merchantability, fitness for a particular purpose, non
    -    infringement, or the absence of latent or other defects, accuracy, or
    -    the present or absence of errors, whether or not discoverable, all to
    -    the greatest extent permissible under applicable law.
    - c. Affirmer disclaims responsibility for clearing rights of other persons
    -    that may apply to the Work or any use thereof, including without
    -    limitation any person's Copyright and Related Rights in the Work.
    -    Further, Affirmer disclaims responsibility for obtaining any necessary
    -    consents, permissions or other rights required for any use of the
    -    Work.
    - d. Affirmer understands and acknowledges that Creative Commons is not a
    -    party to this document and has no duty or obligation with respect to
    -    this CC0 or use of the Work.
    -
    -
    -
    -

    ISC License (ISC)

    -

    Used by:

    - -
    ISC License
    -
    -Copyright (c) 2020, Hunter WB <hunterwb.com>
    -
    -Permission to use, copy, modify, and/or distribute this software for any
    -purpose with or without fee is hereby granted, provided that the above
    -copyright notice and this permission notice appear in all copies.
    -
    -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    Copyright (c) 2006-2009 Graydon Hoare
    -Copyright (c) 2009-2013 Mozilla Foundation
    -Copyright (c) 2016 Artyom Pavlov
    -
    -Permission is hereby granted, free of charge, to any
    -person obtaining a copy of this software and associated
    -documentation files (the "Software"), to deal in the
    -Software without restriction, including without
    -limitation the rights to use, copy, modify, merge,
    -publish, distribute, sublicense, and/or sell copies of
    -the Software, and to permit persons to whom the Software
    -is furnished to do so, subject to the following
    -conditions:
    -
    -The above copyright notice and this permission notice
    -shall be included in all copies or substantial portions
    -of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    -DEALINGS IN THE SOFTWARE.
     
    @@ -1616,10 +971,10 @@

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2010 The Rust Project Developers
    +    
    Copyright (C) 2022 Thom Chiovoloni <thom@shift.click>
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -1681,7 +1036,7 @@ 

    MIT License (MIT)

    Used by:

    Copyright (c) 2013-2016 The rust-url developers
    @@ -1716,16 +1071,16 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2013-2022 The rust-url developers
    +    
    Copyright (c) 2013-2025 The rust-url developers
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -1757,52 +1112,37 @@ 

    MIT License (MIT)

    Used by:

    Copyright (c) 2014 Alex Crichton
    @@ -1840,25 +1180,19 @@ 

    MIT License (MIT)

    bitflags 1.3.2
  • - bitflags 2.9.0 -
  • -
  • - log 0.4.20 -
  • -
  • - num-traits 0.2.17 + bitflags 2.13.1
  • - regex-automata 0.4.3 + log 0.4.34
  • - regex-syntax 0.6.29 + num-traits 0.2.19
  • - regex-syntax 0.8.2 + regex-automata 0.4.18
  • - regex 1.10.2 + regex-syntax 0.8.11
  • Copyright (c) 2014 The Rust Project Developers
    @@ -1886,36 +1220,6 @@ 

    MIT License (MIT)

    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    Copyright (c) 2014-2017 Sean McArthur
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in
    -all copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -THE SOFTWARE.
    -
     
    @@ -1953,10 +1257,10 @@

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2014-2020 The Rust Project Developers
    +    
    Copyright (c) 2014-2026 Alex Crichton
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -1988,34 +1292,29 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2014-2025 Alex Crichton
    +    
    Copyright (c) 2014-2026 Sean McArthur
     
    -Permission is hereby granted, free of charge, to any
    -person obtaining a copy of this software and associated
    -documentation files (the "Software"), to deal in the
    -Software without restriction, including without
    -limitation the rights to use, copy, modify, merge,
    -publish, distribute, sublicense, and/or sell copies of
    -the Software, and to permit persons to whom the Software
    -is furnished to do so, subject to the following
    -conditions:
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
     
    -The above copyright notice and this permission notice
    -shall be included in all copies or substantial portions
    -of the Software.
    +The above copyright notice and this permission notice shall be included in
    +all copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    +THE SOFTWARE.
     
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    -DEALINGS IN THE SOFTWARE.
     
    @@ -2023,7 +1322,13 @@

    MIT License (MIT)

    Used by:

    Copyright (c) 2015
    @@ -2058,7 +1363,7 @@ 

    MIT License (MIT)

    Used by:

    Copyright (c) 2015 Steven Allen
    @@ -2093,7 +1398,10 @@ 

    MIT License (MIT)

    Used by:

    Copyright (c) 2015 The Gimli Developers
    @@ -2128,66 +1436,34 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2015 The LALRPOP Project Developers
    -
    -Permission is hereby granted, free of charge, to any
    -person obtaining a copy of this software and associated
    -documentation files (the "Software"), to deal in the
    -Software without restriction, including without
    -limitation the rights to use, copy, modify, merge,
    -publish, distribute, sublicense, and/or sell copies of
    -the Software, and to permit persons to whom the Software
    -is furnished to do so, subject to the following
    -conditions:
    -
    -The above copyright notice and this permission notice
    -shall be included in all copies or substantial portions
    -of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    -DEALINGS IN THE SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    -
    Copyright (c) 2015 The Rust Project Developers
    @@ -2215,6 +1491,35 @@ 

    MIT License (MIT)

    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    + +
    Copyright (c) 2015 The rust-openssl-verify Developers
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
     
    @@ -2256,6 +1561,9 @@

    MIT License (MIT)

    MIT License (MIT)

    Used by:

      +
    • + httparse 1.10.1 +
    • num_cpus 1.17.0
    • @@ -2287,31 +1595,31 @@

      MIT License (MIT)

      Used by:

      Copyright (c) 2016 Alex Crichton
      @@ -2347,10 +1655,13 @@ 

      MIT License (MIT)

      Used by:

      Copyright (c) 2016 Amanieu d'Antras
      @@ -2385,10 +1696,10 @@ 

      MIT License (MIT)

      Used by:

      -
      Copyright (c) 2016 Anthony Ramine
      +    
      Copyright (c) 2016 Joe Wilm
       
       Permission is hereby granted, free of charge, to any
       person obtaining a copy of this software and associated
      @@ -2449,29 +1760,34 @@ 

      MIT License (MIT)

      Used by:

      -
      Copyright (c) 2016 Sean McArthur
      -
      -Permission is hereby granted, free of charge, to any person obtaining a copy
      -of this software and associated documentation files (the "Software"), to deal
      -in the Software without restriction, including without limitation the rights
      -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      -copies of the Software, and to permit persons to whom the Software is
      -furnished to do so, subject to the following conditions:
      +    
      Copyright (c) 2016 Project Developers
       
      -The above copyright notice and this permission notice shall be included in
      -all copies or substantial portions of the Software.
      +Permission is hereby granted, free of charge, to any
      +person obtaining a copy of this software and associated
      +documentation files (the "Software"), to deal in the
      +Software without restriction, including without
      +limitation the rights to use, copy, modify, merge,
      +publish, distribute, sublicense, and/or sell copies of
      +the Software, and to permit persons to whom the Software
      +is furnished to do so, subject to the following
      +conditions:
       
      -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      -THE SOFTWARE.
      +The above copyright notice and this permission notice
      +shall be included in all copies or substantial portions
      +of the Software.
       
      +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
      +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
      +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
      +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
      +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
      +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
      +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      +DEALINGS IN THE SOFTWARE.
       
    @@ -2479,13 +1795,10 @@

    MIT License (MIT)

    Used by:

    Copyright (c) 2016 The Rust Project Developers
    @@ -2520,7 +1833,7 @@ 

    MIT License (MIT)

    Used by:

    Copyright (c) 2016--2017
    @@ -2555,7 +1868,7 @@ 

    MIT License (MIT)

    Used by:

    Copyright (c) 2016--2023
    @@ -2590,7 +1903,10 @@ 

    MIT License (MIT)

    Used by:

    Copyright (c) 2016-2018 The gimli Developers
    @@ -2660,10 +1976,13 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2017 Artyom Pavlov
    +    
    Copyright (c) 2016-2026 The RustCrypto Project Developers
    +Copyright (c) 2016 Artyom Pavlov
    +Copyright (c) 2009-2013 Mozilla Foundation
    +Copyright (c) 2006-2009 Graydon Hoare
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -2695,12 +2014,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2017 Carl Lerche
    -Copyright (c) 2018 Bastian Köcher
    -Copyright (c) 2019-2020 Artem Vorotnikov
    +    
    Copyright (c) 2017 Contributors
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -2732,10 +2049,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2017 Contributors
    +    
    Copyright (c) 2017 Jorge Aparicio
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -2767,10 +2084,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2017 Jorge Aparicio
    +    
    Copyright (c) 2017 Robert Grosse
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -2794,18 +2111,17 @@ 

    MIT License (MIT)

    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -
    +DEALINGS IN THE SOFTWARE.

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2017 Robert Grosse
    +    
    Copyright (c) 2017 http-rs authors
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -2829,17 +2145,21 @@ 

    MIT License (MIT)

    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE.
    +DEALINGS IN THE SOFTWARE. +

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2017 http-rs authors
    +    
    Copyright (c) 2017-2025 Alissa Rao <aura@aura.moe>
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -2864,6 +2184,7 @@ 

    MIT License (MIT)

    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +
    @@ -2871,13 +2192,11 @@

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2017-2023 Alissa Rao <lymiahugs@gmail.com>
    +    
    Copyright (c) 2017-2025 RustCrypto Developers
    +Copyright (c) 2017 Artyom Pavlov
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -2902,7 +2221,6 @@ 

    MIT License (MIT)

    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -
    @@ -2910,7 +2228,7 @@

    MIT License (MIT)

    Used by:

    Copyright (c) 2018 Carl Lerche
    @@ -2945,27 +2263,34 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2018 Dan Reeves
    +    
    Copyright (c) 2018 Jorge Aparicio
     
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    +Permission is hereby granted, free of charge, to any
    +person obtaining a copy of this software and associated
    +documentation files (the "Software"), to deal in the
    +Software without restriction, including without
    +limitation the rights to use, copy, modify, merge,
    +publish, distribute, sublicense, and/or sell copies of
    +the Software, and to permit persons to whom the Software
    +is furnished to do so, subject to the following
    +conditions:
     
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    +The above copyright notice and this permission notice
    +shall be included in all copies or substantial portions
    +of the Software.
     
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    +DEALINGS IN THE SOFTWARE.
     
    @@ -2973,10 +2298,10 @@

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2018 Jorge Aparicio
    +    
    Copyright (c) 2018 Nick Fitzgerald
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3008,36 +2333,34 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2018 Maciej Hirsz <maciej.hirsz@gmail.com>
    -
    -The MIT License (MIT)
    +    
    Copyright (c) 2018 The Servo Project Developers
     
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    +Permission is hereby granted, free of charge, to any
    +person obtaining a copy of this software and associated
    +documentation files (the "Software"), to deal in the
    +Software without restriction, including without
    +limitation the rights to use, copy, modify, merge,
    +publish, distribute, sublicense, and/or sell copies of
    +the Software, and to permit persons to whom the Software
    +is furnished to do so, subject to the following
    +conditions:
     
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    +The above copyright notice and this permission notice
    +shall be included in all copies or substantial portions
    +of the Software.
     
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    +DEALINGS IN THE SOFTWARE.
     
    @@ -3045,10 +2368,10 @@

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2018 Nick Fitzgerald
    +    
    Copyright (c) 2018 The pin-utils authors
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3080,10 +2403,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2018 The Servo Project Developers
    +    
    Copyright (c) 2018-2025 The RustCrypto Project Developers
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3115,10 +2438,11 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2018 The pin-utils authors
    +    
    Copyright (c) 2018-2025 The rust-random Project Developers
    +Copyright (c) 2014 The Rust Project Developers
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3150,10 +2474,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2018 Tom Kaitchuck
    +    
    Copyright (c) 2018-2026 The Rand Project Developers
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3185,10 +2509,11 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2018-2019 The RustCrypto Project Developers
    +    
    Copyright (c) 2018-2026 The rust-random Project Developers
    +Copyright (c) 2014 The Rust Project Developers
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3220,11 +2545,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2018-2024 The rust-random Project Developers
    -Copyright (c) 2014 The Rust Project Developers
    +    
    Copyright (c) 2019 Carl Lerche
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3256,10 +2580,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2019 Carl Lerche
    +    
    Copyright (c) 2019 Nick Fitzgerald
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3291,68 +2615,16 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2019 Eliza Weisman
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in
    -all copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -THE SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    Copyright (c) 2019 Eliza Weisman
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    Copyright (c) 2019 Nick Fitzgerald
    +    
    Copyright (c) 2019 Tokio Contributors
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3384,10 +2656,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2019 The CryptoCorrosion Contributors
    +    
    Copyright (c) 2019-2026 The RustCrypto Project Developers
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3419,22 +2691,67 @@ 

    MIT License (MIT)

    Used by:

    +
    Copyright (c) 2020 Lucien Greathouse
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    + +
    Copyright (c) 2020 Olivier Goffart <ogoffart@sixtyfps.io>
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
    +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    + -
    Copyright (c) 2019 Tokio Contributors
    +    
    Copyright (c) 2020 Project Developers
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3466,10 +2783,14 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2019 Tower Contributors
    +    
    Copyright (c) 2020 Yevhenii Reizner
    +Copyright (c) 2015 Dan Burkert
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3501,10 +2822,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2020 Ludwig Stecher and contributors
    +    
    Copyright (c) 2020-2026 The RustCrypto Project Developers
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3536,70 +2857,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2020 Maciej Hirsz <hello@maciej.codes>
    -
    -The MIT License (MIT)
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    Copyright (c) 2020 Olivier Goffart <ogoffart@sixtyfps.io>
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    Copyright (c) 2020 Project Developers
    +    
    Copyright (c) 2021-2026 RustCrypto Developers
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3631,11 +2892,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2020 Yevhenii Reizner
    -Copyright (c) 2015 Dan Burkert
    +    
    Copyright (c) 2022 The Wasmer Engineering Team <engineering@wasmer.io>
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3667,10 +2927,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2021 RustCrypto Developers
    +    
    Copyright (c) 2022-2026 The RustCrypto Project Developers
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3702,13 +2962,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2022 Jacob Pratt et al.
    +    
    Copyright (c) 2023 Jacob Pratt et al.
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -3734,10 +2991,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2022 The Wasmer Engineering Team <engineering@wasmer.io>
    +    
    Copyright (c) 2023 Michael-F-Bryan <michaelfbryan@gmail.com>
     
     Permission is hereby granted, free of charge, to any
     person obtaining a copy of this software and associated
    @@ -3769,10 +3026,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2023 Jacob Pratt
    +    
    Copyright (c) 2024 Jacob Pratt et al.
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -3798,10 +3055,55 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2023 Jacob Pratt et al.
    +    
    Copyright (c) Individual contributors
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -3827,72 +3129,28 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2023 Michael-F-Bryan <michaelfbryan@gmail.com>
    +    
    Copyright (c) Jacob Pratt
     
    -Permission is hereby granted, free of charge, to any
    -person obtaining a copy of this software and associated
    -documentation files (the "Software"), to deal in the
    -Software without restriction, including without
    -limitation the rights to use, copy, modify, merge,
    -publish, distribute, sublicense, and/or sell copies of
    -the Software, and to permit persons to whom the Software
    -is furnished to do so, subject to the following
    -conditions:
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
     
    -The above copyright notice and this permission notice
    -shall be included in all copies or substantial portions
    -of the Software.
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
     
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    -DEALINGS IN THE SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    Copyright (c) 2023 Tokio Contributors
    -
    -Permission is hereby granted, free of charge, to any
    -person obtaining a copy of this software and associated
    -documentation files (the "Software"), to deal in the
    -Software without restriction, including without
    -limitation the rights to use, copy, modify, merge,
    -publish, distribute, sublicense, and/or sell copies of
    -the Software, and to permit persons to whom the Software
    -is furnished to do so, subject to the following
    -conditions:
    -
    -The above copyright notice and this permission notice
    -shall be included in all copies or substantial portions
    -of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    -DEALINGS IN THE SOFTWARE.
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
     
    @@ -3900,10 +3158,13 @@

    MIT License (MIT)

    Used by:

    -
    Copyright (c) 2024 Jacob Pratt et al.
    +    
    Copyright (c) Jacob Pratt et al.
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -3929,40 +3190,34 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright (c) Individual contributors
    +    
    Copyright (c) The Rust Project Developers
     
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    +Permission is hereby granted, free of charge, to any
    +person obtaining a copy of this software and associated
    +documentation files (the "Software"), to deal in the
    +Software without restriction, including without
    +limitation the rights to use, copy, modify, merge,
    +publish, distribute, sublicense, and/or sell copies of
    +the Software, and to permit persons to whom the Software
    +is furnished to do so, subject to the following
    +conditions:
     
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    +The above copyright notice and this permission notice
    +shall be included in all copies or substantial portions
    +of the Software.
     
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    +DEALINGS IN THE SOFTWARE.
     
    @@ -3970,7 +3225,7 @@

    MIT License (MIT)

    Used by:

    Copyright (c) The rust-url developers
    @@ -4005,7 +3260,7 @@ 

    MIT License (MIT)

    Used by:

    Copyright (c) Ulrik Sverdrup "bluss" 2015-2023
    @@ -4040,7 +3295,7 @@ 

    MIT License (MIT)

    Used by:

    Copyright 2016 Nika Layzell
    @@ -4057,7 +3312,7 @@ 

    MIT License (MIT)

    Used by:

    Copyright 2017 Juniper Networks, Inc.
    @@ -4074,13 +3329,7 @@ 

    MIT License (MIT)

    Used by:

    Copyright 2018 Developers of the Rand project
    @@ -4116,16 +3365,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright 2020 David Koloski
    +    
    Copyright 2019 Jeffrey Parsons
     
     Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
     
    @@ -4139,28 +3382,10 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright 2021 David Koloski
    +    
    Copyright 2020 David Koloski
     
     Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
     
    @@ -4174,10 +3399,22 @@ 

    MIT License (MIT)

    Used by:

    -
    Copyright 2023 David Koloski
    +    
    Copyright 2021 David Koloski
     
     Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
     
    @@ -4191,7 +3428,10 @@ 

    MIT License (MIT)

    Used by:

    Copyright 2023 David Koloski
    @@ -4208,7 +3448,10 @@ 

    MIT License (MIT)

    Used by:

    Copyright 2023 The Fuchsia Authors
    @@ -4244,10 +3487,10 @@ 

    MIT License (MIT)

    Used by:

    Copyright 2024 David Koloski
    @@ -4263,15 +3506,9 @@ 

    MIT License (MIT)

    MIT License (MIT)

    Used by:

    Copyright Mozilla Foundation
     
    @@ -4305,12 +3542,12 @@ 

    MIT License (MIT)

    Used by:

    MIT License
     
    -Copyright (c) 2017 Frommi
    +Copyright (c) 2017 Denis Kurilenko
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -4336,12 +3573,27 @@ 

    MIT License (MIT)

    Used by:

    MIT License
     
    -Copyright (c) 2017 Nikolai Vazquez
    +Copyright (c) 2017 Ted Driggs
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -4367,27 +3619,43 @@ 

    MIT License (MIT)

    Used by:

    +
    MIT License
    +
    +Copyright (c) 2017 Yilin Chen
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
    +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    +
    MIT License
     
    -Copyright (c) 2017 Ted Driggs
    +Copyright (c) 2018 Sam Rijs, Alex Crichton and contributors
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -4413,12 +3681,12 @@ 

    MIT License (MIT)

    Used by:

    MIT License
     
    -Copyright (c) 2017 Yilin Chen
    +Copyright (c) 2018 The typed-arena developers
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -4444,12 +3712,12 @@ 

    MIT License (MIT)

    Used by:

    MIT License
     
    -Copyright (c) 2018 Sam Rijs, Alex Crichton and contributors
    +Copyright (c) 2019 Acrimon
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -4475,12 +3743,12 @@ 

    MIT License (MIT)

    Used by:

    MIT License
     
    -Copyright (c) 2019 Acrimon
    +Copyright (c) 2019 Eric Seppanen
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -4506,10 +3774,10 @@ 

    MIT License (MIT)

    Used by:

    MIT License
    @@ -4540,7 +3808,7 @@ 

    MIT License (MIT)

    Used by:

    MIT License
    @@ -4571,7 +3839,7 @@ 

    MIT License (MIT)

    Used by:

    MIT License
    @@ -4668,12 +3936,12 @@ 

    MIT License (MIT)

    Used by:

    MIT License
     
    -Copyright (c) 2023 dAxpeDDa
    +Copyright (c) 2021 Casper Beyer
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -4699,18 +3967,12 @@ 

    MIT License (MIT)

    Used by:

    MIT License
     
    -Copyright (c) 2023 magiclen.org (Ron Li)
    +Copyright (c) 2023 dAxpeDDa
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -4736,76 +3998,76 @@ 

    MIT License (MIT)

    Used by:

    +
    MIT License
    +
    +Copyright (c) 2025 rutrum
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    +
    MIT License
    @@ -4833,26 +4095,13 @@ 

    MIT License (MIT)

    Used by:

    -
    MIT License
    -
    -Copyright (c) <year> <copyright holders>
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    -
    MIT License
    @@ -4883,15 +4132,12 @@ 

    MIT License (MIT)

    Used by:

    MIT License
     
    -Copyright 2013-2014 RAD Game Tools and Valve Software
    -Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
    -Copyright (c) 2017 Frommi
    -Copyright (c) 2017-2024 oyvindln
    +Copyright (c) [2021] [Marvin Countryman]
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -4917,9 +4163,46 @@ 

    MIT License (MIT)

    Used by:

    +
  • + miniz_oxide 0.9.1 +
  • + +
    MIT License
    +
    +Copyright 2013-2014 RAD Game Tools and Valve Software
    +Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
    +Copyright (c) 2017 Frommi
    +Copyright (c) 2017-2024 oyvindln
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
    +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    +
    MIT License
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
    @@ -4944,6 +4227,9 @@ 

    MIT License (MIT)

    MIT License (MIT)

    Used by:

      +
    • + half 2.7.1 +
    • ident_case 1.0.1
    • @@ -5005,12 +4291,12 @@

      MIT License (MIT)

      Used by:

      MIT License
       
      -Copyright (c) 2019 Daniel Augusto Rizzi Salvadori
      +Copyright (c) 2023 Bincode
       
       Permission is hereby granted, free of charge, to any person obtaining a copy
       of this software and associated documentation files (the "Software"), to deal
      @@ -5028,101 +4314,170 @@ 

      MIT License (MIT)

      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.
      +SOFTWARE. +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    + +
    MIT License
    +
    +Copyright (c) 2023 Sebastian Aaltonen, Patrick Walton
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
    +

    MIT License (MIT)

    Used by:

    +
    Modified MIT License
    +
    +Copyright (c) 2026 Wasmer, Inc.
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the “Software”), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
    +
    +Our only modification part is that, if the Software (or any derivative works
    +thereof) is used for any of your commercial products or services that have
    +more than 1 million monthly active users, or more than 1 million US dollars
    +(or equivalent in other currencies) in monthly revenue, you shall prominently
    +display "Wasmer" on the user interface of such product or service.
    +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    +
    Permission is hereby granted, free of charge, to any
    @@ -5182,10 +4546,7 @@ 

    MIT License (MIT)

    Used by:

    Permission is hereby granted, free of charge, to any person obtaining
    @@ -5213,7 +4574,7 @@ 

    MIT License (MIT)

    Used by:

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
    @@ -5259,13 +4620,12 @@ 

    MIT License (MIT)

    Used by:

    The MIT License (MIT)
     
    -Copyright (c) 2014 Benjamin Sago
    -Copyright (c) 2021-2022 The Nushell Project Developers
    +Copyright (c) 2014 Paho Lurie-Gregg
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -5291,12 +4651,15 @@ 

    MIT License (MIT)

    Used by:

    The MIT License (MIT)
     
    -Copyright (c) 2014 Paho Lurie-Gregg
    +Copyright (c) 2014 Ty Overby
     
     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
    @@ -5315,6 +4678,45 @@ 

    MIT License (MIT)

    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    + +
    The MIT License (MIT)
    +
    +Copyright (c) 2014-2022 Steven Fackler, Yuki Okushi
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy of
    +this software and associated documentation files (the "Software"), to deal in
    +the Software without restriction, including without limitation the rights to
    +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    +the Software, and to permit persons to whom the Software is furnished to do so,
    +subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     
    @@ -5353,25 +4755,22 @@

    MIT License (MIT)

    Used by:

    The MIT License (MIT)
    @@ -5402,7 +4801,7 @@ 

    MIT License (MIT)

    Used by:

    The MIT License (MIT)
    @@ -5435,7 +4834,7 @@ 

    MIT License (MIT)

    Used by:

    The MIT License (MIT)
    @@ -5466,13 +4865,10 @@ 

    MIT License (MIT)

    Used by:

    The MIT License (MIT)
    @@ -5534,13 +4930,13 @@ 

    MIT License (MIT)

    Used by:

    The MIT License (MIT)
    @@ -5602,39 +4998,7 @@ 

    MIT License (MIT)

    Used by:

    -
    The MIT License (MIT)
    -
    -Copyright (c) 2017 Armin Ronacher <armin.ronacher@active-4.com>
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    -
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    -
    The MIT License (MIT)
    @@ -5665,711 +5029,207 @@ 

    MIT License (MIT)

    Used by:

    -
    The MIT License (MIT)
    -
    -Copyright (c) 2019 The Crossbeam Project Developers
    -
    -Permission is hereby granted, free of charge, to any
    -person obtaining a copy of this software and associated
    -documentation files (the "Software"), to deal in the
    -Software without restriction, including without
    -limitation the rights to use, copy, modify, merge,
    -publish, distribute, sublicense, and/or sell copies of
    -the Software, and to permit persons to whom the Software
    -is furnished to do so, subject to the following
    -conditions:
    -
    -The above copyright notice and this permission notice
    -shall be included in all copies or substantial portions
    -of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    -DEALINGS IN THE SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    The MIT License (MIT)
    -
    -Copyright (c) 2020 James Dyson <avitex@wfxlabs.com>
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    The MIT License (MIT)
    -
    -Copyright (c) 2020 Yoshua Wuyts
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    The MIT License (MIT)
    -
    -Copyright (c) HeroicKatora 2020
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    -
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    The MIT License (MIT)
    -Copyright (c) 2016-2020 Sergio Benitez
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy of
    -this software and associated documentation files (the "Software"), to deal in
    -the Software without restriction, including without limitation the rights to
    -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    -the Software, and to permit persons to whom the Software is furnished to do so,
    -subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    The MIT License (MIT)
    -Copyright (c) 2017 Sergio Benitez
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy of
    -this software and associated documentation files (the "Software"), to deal in
    -the Software without restriction, including without limitation the rights to
    -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    -the Software, and to permit persons to whom the Software is furnished to do so,
    -subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    The MIT License (MIT)
    -
    -Copyright (c) 2014 Ty Overby
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    The MIT License (MIT)
    -
    -Copyright (c) 2015 Bartłomiej Kamiński
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    The MIT License (MIT)
    -
    -Copyright (c) 2016 J/A
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in all
    -copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -SOFTWARE.
    -
    -
    -
    -

    MIT License (MIT)

    -

    Used by:

    - -
    © 2016 Bertram Truong
    -© 2021 Kornel Lesiński
    -
    -Permission is hereby granted, free of charge, to any person obtaining a copy
    -of this software and associated documentation files (the "Software"), to deal
    -in the Software without restriction, including without limitation the rights
    -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -copies of the Software, and to permit persons to whom the Software is
    -furnished to do so, subject to the following conditions:
    -
    -The above copyright notice and this permission notice shall be included in
    -all copies or substantial portions of the Software.
    -
    -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    -THE SOFTWARE.
    -
    -
    -
    -

    Mozilla Public License 2.0 (MPL-2.0)

    -

    Used by:

    - -
    Mozilla Public License Version 2.0
    -==================================
    -
    -1. Definitions
    ---------------
    -
    -1.1. "Contributor"
    -    means each individual or legal entity that creates, contributes to
    -    the creation of, or owns Covered Software.
    -
    -1.2. "Contributor Version"
    -    means the combination of the Contributions of others (if any) used
    -    by a Contributor and that particular Contributor's Contribution.
    -
    -1.3. "Contribution"
    -    means Covered Software of a particular Contributor.
    -
    -1.4. "Covered Software"
    -    means Source Code Form to which the initial Contributor has attached
    -    the notice in Exhibit A, the Executable Form of such Source Code
    -    Form, and Modifications of such Source Code Form, in each case
    -    including portions thereof.
    -
    -1.5. "Incompatible With Secondary Licenses"
    -    means
    -
    -    (a) that the initial Contributor has attached the notice described
    -        in Exhibit B to the Covered Software; or
    -
    -    (b) that the Covered Software was made available under the terms of
    -        version 1.1 or earlier of the License, but not also under the
    -        terms of a Secondary License.
    -
    -1.6. "Executable Form"
    -    means any form of the work other than Source Code Form.
    -
    -1.7. "Larger Work"
    -    means a work that combines Covered Software with other material, in 
    -    a separate file or files, that is not Covered Software.
    -
    -1.8. "License"
    -    means this document.
    -
    -1.9. "Licensable"
    -    means having the right to grant, to the maximum extent possible,
    -    whether at the time of the initial grant or subsequently, any and
    -    all of the rights conveyed by this License.
    -
    -1.10. "Modifications"
    -    means any of the following:
    -
    -    (a) any file in Source Code Form that results from an addition to,
    -        deletion from, or modification of the contents of Covered
    -        Software; or
    -
    -    (b) any new file in Source Code Form that contains any Covered
    -        Software.
    -
    -1.11. "Patent Claims" of a Contributor
    -    means any patent claim(s), including without limitation, method,
    -    process, and apparatus claims, in any patent Licensable by such
    -    Contributor that would be infringed, but for the grant of the
    -    License, by the making, using, selling, offering for sale, having
    -    made, import, or transfer of either its Contributions or its
    -    Contributor Version.
    -
    -1.12. "Secondary License"
    -    means either the GNU General Public License, Version 2.0, the GNU
    -    Lesser General Public License, Version 2.1, the GNU Affero General
    -    Public License, Version 3.0, or any later versions of those
    -    licenses.
    -
    -1.13. "Source Code Form"
    -    means the form of the work preferred for making modifications.
    -
    -1.14. "You" (or "Your")
    -    means an individual or a legal entity exercising rights under this
    -    License. For legal entities, "You" includes any entity that
    -    controls, is controlled by, or is under common control with You. For
    -    purposes of this definition, "control" means (a) the power, direct
    -    or indirect, to cause the direction or management of such entity,
    -    whether by contract or otherwise, or (b) ownership of more than
    -    fifty percent (50%) of the outstanding shares or beneficial
    -    ownership of such entity.
    -
    -2. License Grants and Conditions
    ---------------------------------
    -
    -2.1. Grants
    -
    -Each Contributor hereby grants You a world-wide, royalty-free,
    -non-exclusive license:
    -
    -(a) under intellectual property rights (other than patent or trademark)
    -    Licensable by such Contributor to use, reproduce, make available,
    -    modify, display, perform, distribute, and otherwise exploit its
    -    Contributions, either on an unmodified basis, with Modifications, or
    -    as part of a Larger Work; and
    -
    -(b) under Patent Claims of such Contributor to make, use, sell, offer
    -    for sale, have made, import, and otherwise transfer either its
    -    Contributions or its Contributor Version.
    -
    -2.2. Effective Date
    -
    -The licenses granted in Section 2.1 with respect to any Contribution
    -become effective for each Contribution on the date the Contributor first
    -distributes such Contribution.
    -
    -2.3. Limitations on Grant Scope
    -
    -The licenses granted in this Section 2 are the only rights granted under
    -this License. No additional rights or licenses will be implied from the
    -distribution or licensing of Covered Software under this License.
    -Notwithstanding Section 2.1(b) above, no patent license is granted by a
    -Contributor:
    -
    -(a) for any code that a Contributor has removed from Covered Software;
    -    or
    -
    -(b) for infringements caused by: (i) Your and any other third party's
    -    modifications of Covered Software, or (ii) the combination of its
    -    Contributions with other software (except as part of its Contributor
    -    Version); or
    -
    -(c) under Patent Claims infringed by Covered Software in the absence of
    -    its Contributions.
    -
    -This License does not grant any rights in the trademarks, service marks,
    -or logos of any Contributor (except as may be necessary to comply with
    -the notice requirements in Section 3.4).
    -
    -2.4. Subsequent Licenses
    -
    -No Contributor makes additional grants as a result of Your choice to
    -distribute the Covered Software under a subsequent version of this
    -License (see Section 10.2) or under the terms of a Secondary License (if
    -permitted under the terms of Section 3.3).
    -
    -2.5. Representation
    -
    -Each Contributor represents that the Contributor believes its
    -Contributions are its original creation(s) or it has sufficient rights
    -to grant the rights to its Contributions conveyed by this License.
    -
    -2.6. Fair Use
    -
    -This License is not intended to limit any rights You have under
    -applicable copyright doctrines of fair use, fair dealing, or other
    -equivalents.
    -
    -2.7. Conditions
    -
    -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
    -in Section 2.1.
    -
    -3. Responsibilities
    --------------------
    -
    -3.1. Distribution of Source Form
    -
    -All distribution of Covered Software in Source Code Form, including any
    -Modifications that You create or to which You contribute, must be under
    -the terms of this License. You must inform recipients that the Source
    -Code Form of the Covered Software is governed by the terms of this
    -License, and how they can obtain a copy of this License. You may not
    -attempt to alter or restrict the recipients' rights in the Source Code
    -Form.
    -
    -3.2. Distribution of Executable Form
    -
    -If You distribute Covered Software in Executable Form then:
    -
    -(a) such Covered Software must also be made available in Source Code
    -    Form, as described in Section 3.1, and You must inform recipients of
    -    the Executable Form how they can obtain a copy of such Source Code
    -    Form by reasonable means in a timely manner, at a charge no more
    -    than the cost of distribution to the recipient; and
    -
    -(b) You may distribute such Executable Form under the terms of this
    -    License, or sublicense it under different terms, provided that the
    -    license for the Executable Form does not attempt to limit or alter
    -    the recipients' rights in the Source Code Form under this License.
    -
    -3.3. Distribution of a Larger Work
    -
    -You may create and distribute a Larger Work under terms of Your choice,
    -provided that You also comply with the requirements of this License for
    -the Covered Software. If the Larger Work is a combination of Covered
    -Software with a work governed by one or more Secondary Licenses, and the
    -Covered Software is not Incompatible With Secondary Licenses, this
    -License permits You to additionally distribute such Covered Software
    -under the terms of such Secondary License(s), so that the recipient of
    -the Larger Work may, at their option, further distribute the Covered
    -Software under the terms of either this License or such Secondary
    -License(s).
    -
    -3.4. Notices
    -
    -You may not remove or alter the substance of any license notices
    -(including copyright notices, patent notices, disclaimers of warranty,
    -or limitations of liability) contained within the Source Code Form of
    -the Covered Software, except that You may alter any license notices to
    -the extent required to remedy known factual inaccuracies.
    -
    -3.5. Application of Additional Terms
    -
    -You may choose to offer, and to charge a fee for, warranty, support,
    -indemnity or liability obligations to one or more recipients of Covered
    -Software. However, You may do so only on Your own behalf, and not on
    -behalf of any Contributor. You must make it absolutely clear that any
    -such warranty, support, indemnity, or liability obligation is offered by
    -You alone, and You hereby agree to indemnify every Contributor for any
    -liability incurred by such Contributor as a result of warranty, support,
    -indemnity or liability terms You offer. You may include additional
    -disclaimers of warranty and limitations of liability specific to any
    -jurisdiction.
    -
    -4. Inability to Comply Due to Statute or Regulation
    ----------------------------------------------------
    -
    -If it is impossible for You to comply with any of the terms of this
    -License with respect to some or all of the Covered Software due to
    -statute, judicial order, or regulation then You must: (a) comply with
    -the terms of this License to the maximum extent possible; and (b)
    -describe the limitations and the code they affect. Such description must
    -be placed in a text file included with all distributions of the Covered
    -Software under this License. Except to the extent prohibited by statute
    -or regulation, such description must be sufficiently detailed for a
    -recipient of ordinary skill to be able to understand it.
    -
    -5. Termination
    ---------------
    -
    -5.1. The rights granted under this License will terminate automatically
    -if You fail to comply with any of its terms. However, if You become
    -compliant, then the rights granted under this License from a particular
    -Contributor are reinstated (a) provisionally, unless and until such
    -Contributor explicitly and finally terminates Your grants, and (b) on an
    -ongoing basis, if such Contributor fails to notify You of the
    -non-compliance by some reasonable means prior to 60 days after You have
    -come back into compliance. Moreover, Your grants from a particular
    -Contributor are reinstated on an ongoing basis if such Contributor
    -notifies You of the non-compliance by some reasonable means, this is the
    -first time You have received notice of non-compliance with this License
    -from such Contributor, and You become compliant prior to 30 days after
    -Your receipt of the notice.
    -
    -5.2. If You initiate litigation against any entity by asserting a patent
    -infringement claim (excluding declaratory judgment actions,
    -counter-claims, and cross-claims) alleging that a Contributor Version
    -directly or indirectly infringes any patent, then the rights granted to
    -You by any and all Contributors for the Covered Software under Section
    -2.1 of this License shall terminate.
    -
    -5.3. In the event of termination under Sections 5.1 or 5.2 above, all
    -end user license agreements (excluding distributors and resellers) which
    -have been validly granted by You or Your distributors under this License
    -prior to termination shall survive termination.
    -
    -************************************************************************
    -*                                                                      *
    -*  6. Disclaimer of Warranty                                           *
    -*  -------------------------                                           *
    -*                                                                      *
    -*  Covered Software is provided under this License on an "as is"       *
    -*  basis, without warranty of any kind, either expressed, implied, or  *
    -*  statutory, including, without limitation, warranties that the       *
    -*  Covered Software is free of defects, merchantable, fit for a        *
    -*  particular purpose or non-infringing. The entire risk as to the     *
    -*  quality and performance of the Covered Software is with You.        *
    -*  Should any Covered Software prove defective in any respect, You     *
    -*  (not any Contributor) assume the cost of any necessary servicing,   *
    -*  repair, or correction. This disclaimer of warranty constitutes an   *
    -*  essential part of this License. No use of any Covered Software is   *
    -*  authorized under this License except under this disclaimer.         *
    -*                                                                      *
    -************************************************************************
    -
    -************************************************************************
    -*                                                                      *
    -*  7. Limitation of Liability                                          *
    -*  --------------------------                                          *
    -*                                                                      *
    -*  Under no circumstances and under no legal theory, whether tort      *
    -*  (including negligence), contract, or otherwise, shall any           *
    -*  Contributor, or anyone who distributes Covered Software as          *
    -*  permitted above, be liable to You for any direct, indirect,         *
    -*  special, incidental, or consequential damages of any character      *
    -*  including, without limitation, damages for lost profits, loss of    *
    -*  goodwill, work stoppage, computer failure or malfunction, or any    *
    -*  and all other commercial damages or losses, even if such party      *
    -*  shall have been informed of the possibility of such damages. This   *
    -*  limitation of liability shall not apply to liability for death or   *
    -*  personal injury resulting from such party's negligence to the       *
    -*  extent applicable law prohibits such limitation. Some               *
    -*  jurisdictions do not allow the exclusion or limitation of           *
    -*  incidental or consequential damages, so this exclusion and          *
    -*  limitation may not apply to You.                                    *
    -*                                                                      *
    -************************************************************************
    +    
    The MIT License (MIT)
     
    -8. Litigation
    --------------
    +Copyright (c) 2019 The Crossbeam Project Developers
     
    -Any litigation relating to this License may be brought only in the
    -courts of a jurisdiction where the defendant maintains its principal
    -place of business and such litigation shall be governed by laws of that
    -jurisdiction, without reference to its conflict-of-law provisions.
    -Nothing in this Section shall prevent a party's ability to bring
    -cross-claims or counter-claims.
    +Permission is hereby granted, free of charge, to any
    +person obtaining a copy of this software and associated
    +documentation files (the "Software"), to deal in the
    +Software without restriction, including without
    +limitation the rights to use, copy, modify, merge,
    +publish, distribute, sublicense, and/or sell copies of
    +the Software, and to permit persons to whom the Software
    +is furnished to do so, subject to the following
    +conditions:
     
    -9. Miscellaneous
    -----------------
    +The above copyright notice and this permission notice
    +shall be included in all copies or substantial portions
    +of the Software.
     
    -This License represents the complete agreement concerning the subject
    -matter hereof. If any provision of this License is held to be
    -unenforceable, such provision shall be reformed only to the extent
    -necessary to make it enforceable. Any law or regulation which provides
    -that the language of a contract shall be construed against the drafter
    -shall not be used to construe this License against a Contributor.
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
    +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
    +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
    +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    +DEALINGS IN THE SOFTWARE.
    +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    + +
    The MIT License (MIT)
     
    -10. Versions of the License
    ----------------------------
    +Copyright (c) 2020 Pascal Seitz
     
    -10.1. New Versions
    +Permission is hereby granted, free of charge, to any person obtaining a copy of
    +this software and associated documentation files (the "Software"), to deal in
    +the Software without restriction, including without limitation the rights to
    +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    +the Software, and to permit persons to whom the Software is furnished to do so,
    +subject to the following conditions:
     
    -Mozilla Foundation is the license steward. Except as provided in Section
    -10.3, no one other than the license steward has the right to modify or
    -publish new versions of this License. Each version will be given a
    -distinguishing version number.
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
     
    -10.2. Effect of New Versions
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    + +
    The MIT License (MIT)
     
    -You may distribute the Covered Software under the terms of the version
    -of the License under which You originally received the Covered Software,
    -or under the terms of any subsequent version published by the license
    -steward.
    +Copyright (c) 2025 Alice Maz, Marshall Pierce
     
    -10.3. Modified Versions
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
     
    -If you create software not governed by this License, and you want to
    -create a new license for such software, you may create and use a
    -modified version of this License if you rename the license and remove
    -any references to the name of the license steward (except to note that
    -such modified license differs from this License).
    +The above copyright notice and this permission notice shall be included in
    +all copies or substantial portions of the Software.
     
    -10.4. Distributing Source Code Form that is Incompatible With Secondary
    -Licenses
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    +THE SOFTWARE.
    +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    + +
    The MIT License (MIT)
    + 
    +Copyright (c) 2017-2026 Fabrice Bellard
    +Copyright (c) 2017-2024 Charlie Gordon
    +Copyright (c) 2023-2026 Ben Noordhuis
    +Copyright (c) 2023-2026 Saúl Ibarra Corretgé
     
    -If You choose to distribute Source Code Form that is Incompatible With
    -Secondary Licenses under the terms of this version of the License, the
    -notice described in Exhibit B of this License must be attached.
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
     
    -Exhibit A - Source Code Form License Notice
    --------------------------------------------
    +The above copyright notice and this permission notice shall be included in
    +all copies or substantial portions of the Software.
     
    -  This Source Code Form is subject to the terms of the Mozilla Public
    -  License, v. 2.0. If a copy of the MPL was not distributed with this
    -  file, You can obtain one at https://mozilla.org/MPL/2.0/.
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    +THE SOFTWARE.
    +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    + +
    The MIT License (MIT)
     
    -If it is not possible or desirable to put the notice in a particular
    -file, then You may include the notice in a location (such as a LICENSE
    -file in a relevant directory) where a recipient would be likely to look
    -for such a notice.
    +Copyright (c) 2021 Victor Koenders
     
    -You may add additional accurate notices of copyright ownership.
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
     
    -Exhibit B - "Incompatible With Secondary Licenses" Notice
    ----------------------------------------------------------
    +The above copyright notice and this permission notice shall be included in all
    +copies or substantial portions of the Software.
     
    -  This Source Code Form is "Incompatible With Secondary Licenses", as
    -  defined by the Mozilla Public License, v. 2.0.
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +SOFTWARE.
    +
    +
    +
    +

    MIT License (MIT)

    +

    Used by:

    + +
    © 2016 Bertram Truong
    +© 2021 Kornel Lesiński
    +
    +Permission is hereby granted, free of charge, to any person obtaining a copy
    +of this software and associated documentation files (the "Software"), to deal
    +in the Software without restriction, including without limitation the rights
    +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +copies of the Software, and to permit persons to whom the Software is
    +furnished to do so, subject to the following conditions:
    +
    +The above copyright notice and this permission notice shall be included in
    +all copies or substantial portions of the Software.
    +
    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    +THE SOFTWARE.
     
    @@ -6760,61 +5620,107 @@

    Unicode License v3 (Unicode-3.0)

    Used by:

    +
    UNICODE LICENSE V3
    +
    +COPYRIGHT AND PERMISSION NOTICE
    +
    +Copyright © 1991-2023 Unicode, Inc.
    +
    +NOTICE TO USER: Carefully read the following legal agreement. BY
    +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
    +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
    +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
    +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
    +
    +Permission is hereby granted, free of charge, to any person obtaining a
    +copy of data files and any associated documentation (the "Data Files") or
    +software and any associated documentation (the "Software") to deal in the
    +Data Files or Software without restriction, including without limitation
    +the rights to use, copy, modify, merge, publish, distribute, and/or sell
    +copies of the Data Files or Software, and to permit persons to whom the
    +Data Files or Software are furnished to do so, provided that either (a)
    +this copyright and permission notice appear with all copies of the Data
    +Files or Software, or (b) this copyright and permission notice appear in
    +associated Documentation.
    +
    +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
    +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
    +THIRD PARTY RIGHTS.
    +
    +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
    +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
    +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
    +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
    +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
    +FILES OR SOFTWARE.
    +
    +Except as contained in this notice, the name of a copyright holder shall
    +not be used in advertising or otherwise to promote the sale, use or other
    +dealings in these Data Files or Software without prior written
    +authorization of the copyright holder.
    +
    +
    +
    +

    Unicode License v3 (Unicode-3.0)

    +

    Used by:

    +
    UNICODE LICENSE V3
    @@ -6866,36 +5772,35 @@ 

    Unicode License v3 (Unicode-3.0)

    -

    Unicode License Agreement - Data Files and Software (2016) (Unicode-DFS-2016)

    +

    zlib License (Zlib)

    Used by:

    -
    UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
    -
    -Unicode Data Files include all data files under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and http://www.unicode.org/utility/trac/browser/.
    -
    -Unicode Data Files do not include PDF online code charts under the directory http://www.unicode.org/Public/.
    -
    -Software includes any source code published in the Unicode Standard or under the directories http://www.unicode.org/Public/, http://www.unicode.org/reports/, http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and http://www.unicode.org/utility/trac/browser/.
    +    
    Copyright (c) 2024 Orson Peters
     
    -NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
    +This software is provided 'as-is', without any express or implied warranty. In
    +no event will the authors be held liable for any damages arising from the use of
    +this software.
     
    -COPYRIGHT AND PERMISSION NOTICE
    -
    -Copyright © 1991-2016 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html.
    +Permission is granted to anyone to use this software for any purpose, including
    +commercial applications, and to alter it and redistribute it freely, subject to
    +the following restrictions:
     
    -Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that either
    +1. The origin of this software must not be misrepresented; you must not claim
    +    that you wrote the original software. If you use this software in a product,
    +    an acknowledgment in the product documentation would be appreciated but is
    +    not required.
     
    -     (a) this copyright and permission notice appear with all copies of the Data Files or Software, or
    -     (b) this copyright and permission notice appear in associated Documentation.
    +2. Altered source versions must be plainly marked as such, and must not be
    +    misrepresented as being the original software.
     
    -THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
    -
    -Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder.
    -
    +3. This notice may not be removed or altered from any source distribution.
    diff --git a/licenses/wasmer-sdk-dependencies.json b/licenses/wasmer-sdk-dependencies.json index c415671..c96c935 100644 --- a/licenses/wasmer-sdk-dependencies.json +++ b/licenses/wasmer-sdk-dependencies.json @@ -5,9 +5,9 @@ "version": "0.9.1" }, "graph": { - "package": "@wasmer/sdk@0.10.0", - "sourceRevision": "93b8b738ebd3ee57e118da0f0eb795b97d5b999e", - "cargoLockSha256": "d352926f3f05e3d4308c4e261711d07db568e5c2b4387067180f920da074791f", + "package": "@wasmer/sdk@0.19.0", + "sourceRevision": "7e69332b7f65dbc5584d64bb79f547eaf4302b69", + "cargoLockSha256": "34156a76319127aa4152e8b25bd92a5657f7ddc0669ba3e26209cc57053f56a1", "target": "wasm32-unknown-unknown", "defaultFeatures": true, "features": [], @@ -17,12 +17,12 @@ }, "report": { "path": "licenses/wasmer-sdk-dependencies.html", - "sha256": "ec4b0175b4d1affb0f97996cb6661ec6fa1c5959dbce637d89b39cf3559b079a" + "sha256": "93d2898132e31cd7634c1470a587a7b0f54e887bcb0a968742260f2f985f643d" }, "packages": [ { "name": "addr2line", - "version": "0.21.0", + "version": "0.25.1", "repository": "https://github.com/gimli-rs/addr2line", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ @@ -30,17 +30,17 @@ ] }, { - "name": "adler", - "version": "1.0.2", - "repository": "https://github.com/jonas-schievink/adler.git", - "declaredLicense": "0BSD OR MIT OR Apache-2.0", + "name": "addr2line", + "version": "0.27.1", + "repository": "https://github.com/gimli-rs/addr2line", + "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ "MIT" ] }, { "name": "adler2", - "version": "2.0.0", + "version": "2.0.1", "repository": "https://github.com/oyvindln/adler2", "declaredLicense": "0BSD OR MIT OR Apache-2.0", "selectedLicenses": [ @@ -48,62 +48,62 @@ ] }, { - "name": "ahash", - "version": "0.8.11", - "repository": "https://github.com/tkaitchuck/ahash", - "declaredLicense": "MIT OR Apache-2.0", + "name": "aho-corasick", + "version": "1.1.5", + "repository": "https://github.com/BurntSushi/aho-corasick", + "declaredLicense": "Unlicense OR MIT", "selectedLicenses": [ "MIT" ] }, { - "name": "aho-corasick", - "version": "1.1.2", - "repository": "https://github.com/BurntSushi/aho-corasick", - "declaredLicense": "Unlicense OR MIT", + "name": "anstream", + "version": "1.0.0", + "repository": "https://github.com/rust-cli/anstyle.git", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "aliasable", - "version": "0.1.3", - "repository": "https://github.com/avitex/rust-aliasable", - "declaredLicense": "MIT", + "name": "anstyle", + "version": "1.0.14", + "repository": "https://github.com/rust-cli/anstyle.git", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "any_ascii", - "version": "0.1.7", - "repository": "https://github.com/anyascii/anyascii", - "declaredLicense": "ISC", + "name": "anstyle-parse", + "version": "1.0.0", + "repository": "https://github.com/rust-cli/anstyle.git", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ - "ISC" + "MIT" ] }, { - "name": "anyhow", - "version": "1.0.97", - "repository": "https://github.com/dtolnay/anyhow", + "name": "anstyle-query", + "version": "1.1.5", + "repository": "https://github.com/rust-cli/anstyle.git", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "arrayref", - "version": "0.3.7", - "repository": "https://github.com/droundy/arrayref", - "declaredLicense": "BSD-2-Clause", + "name": "anyhow", + "version": "1.0.104", + "repository": "https://github.com/dtolnay/anyhow", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ - "BSD-2-Clause" + "MIT" ] }, { "name": "arrayvec", - "version": "0.7.4", + "version": "0.7.8", "repository": "https://github.com/bluss/arrayvec", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -112,7 +112,7 @@ }, { "name": "async-trait", - "version": "0.1.88", + "version": "0.1.92", "repository": "https://github.com/dtolnay/async-trait", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -121,7 +121,7 @@ }, { "name": "backtrace", - "version": "0.3.69", + "version": "0.3.76", "repository": "https://github.com/rust-lang/backtrace-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -138,9 +138,9 @@ ] }, { - "name": "beef", - "version": "0.5.2", - "repository": "https://github.com/maciejhirsz/beef", + "name": "base64", + "version": "0.23.1", + "repository": "https://github.com/marshallpierce/rust-base64", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -148,8 +148,17 @@ }, { "name": "bincode", - "version": "1.3.3", - "repository": "https://github.com/servo/bincode", + "version": "2.0.1", + "repository": "https://github.com/bincode-org/bincode", + "declaredLicense": "MIT", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "bincode_derive", + "version": "2.0.1", + "repository": "https://github.com/bincode-org/bincode", "declaredLicense": "MIT", "selectedLicenses": [ "MIT" @@ -166,7 +175,7 @@ }, { "name": "bitflags", - "version": "2.9.0", + "version": "2.13.1", "repository": "https://github.com/bitflags/bitflags", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -175,16 +184,16 @@ }, { "name": "blake3", - "version": "1.5.1", + "version": "1.8.7", "repository": "https://github.com/BLAKE3-team/BLAKE3", - "declaredLicense": "CC0-1.0 OR Apache-2.0", + "declaredLicense": "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception", "selectedLicenses": [ "Apache-2.0" ] }, { "name": "block-buffer", - "version": "0.10.4", + "version": "0.12.1", "repository": "https://github.com/RustCrypto/utils", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -193,7 +202,7 @@ }, { "name": "bstr", - "version": "1.9.1", + "version": "1.13.1", "repository": "https://github.com/BurntSushi/bstr", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -202,7 +211,7 @@ }, { "name": "bumpalo", - "version": "3.14.0", + "version": "3.20.3", "repository": "https://github.com/fitzgen/bumpalo", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -220,16 +229,7 @@ }, { "name": "bytecheck", - "version": "0.6.11", - "repository": "https://github.com/djkoloski/bytecheck", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "bytecheck", - "version": "0.8.1", + "version": "0.8.3", "repository": "https://github.com/rkyv/bytecheck", "declaredLicense": "MIT", "selectedLicenses": [ @@ -238,16 +238,7 @@ }, { "name": "bytecheck_derive", - "version": "0.6.11", - "repository": "https://github.com/djkoloski/bytecheck", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "bytecheck_derive", - "version": "0.8.1", + "version": "0.8.3", "repository": "https://github.com/rkyv/bytecheck", "declaredLicense": "MIT", "selectedLicenses": [ @@ -265,7 +256,7 @@ }, { "name": "bytes", - "version": "1.10.1", + "version": "1.12.1", "repository": "https://github.com/tokio-rs/bytes", "declaredLicense": "MIT", "selectedLicenses": [ @@ -274,8 +265,8 @@ }, { "name": "bytesize", - "version": "1.3.0", - "repository": "https://github.com/hyunsik/bytesize/", + "version": "2.7.0", + "repository": "https://github.com/bytesize-rs/bytesize", "declaredLicense": "Apache-2.0", "selectedLicenses": [ "Apache-2.0" @@ -283,8 +274,17 @@ }, { "name": "cfg-if", - "version": "1.0.0", - "repository": "https://github.com/alexcrichton/cfg-if", + "version": "1.0.4", + "repository": "https://github.com/rust-lang/cfg-if", + "declaredLicense": "MIT OR Apache-2.0", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "chacha20", + "version": "0.10.2", + "repository": "https://github.com/RustCrypto/stream-ciphers", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -292,7 +292,7 @@ }, { "name": "chrono", - "version": "0.4.40", + "version": "0.4.45", "repository": "https://github.com/chronotope/chrono", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -327,10 +327,46 @@ ] }, { - "name": "console", - "version": "0.15.11", - "repository": "https://github.com/console-rs/console", - "declaredLicense": "MIT", + "name": "clap", + "version": "4.6.6", + "repository": "https://github.com/clap-rs/clap", + "declaredLicense": "MIT OR Apache-2.0", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "clap_builder", + "version": "4.6.6", + "repository": "https://github.com/clap-rs/clap", + "declaredLicense": "MIT OR Apache-2.0", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "clap_derive", + "version": "4.6.4", + "repository": "https://github.com/clap-rs/clap", + "declaredLicense": "MIT OR Apache-2.0", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "clap_lex", + "version": "1.1.0", + "repository": "https://github.com/clap-rs/clap", + "declaredLicense": "MIT OR Apache-2.0", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "colorchoice", + "version": "1.0.5", + "repository": "https://github.com/rust-cli/anstyle.git", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] @@ -344,15 +380,33 @@ "MIT" ] }, + { + "name": "const-oid", + "version": "0.10.2", + "repository": "https://github.com/RustCrypto/formats", + "declaredLicense": "Apache-2.0 OR MIT", + "selectedLicenses": [ + "MIT" + ] + }, { "name": "constant_time_eq", - "version": "0.3.0", + "version": "0.4.2", "repository": "https://github.com/cesarb/constant_time_eq", "declaredLicense": "CC0-1.0 OR MIT-0 OR Apache-2.0", "selectedLicenses": [ "Apache-2.0" ] }, + { + "name": "convert_case", + "version": "0.10.0", + "repository": "https://github.com/rutrum/convert-case", + "declaredLicense": "MIT", + "selectedLicenses": [ + "MIT" + ] + }, { "name": "cooked-waker", "version": "5.0.0", @@ -362,9 +416,18 @@ "MPL-2.0" ] }, + { + "name": "cpp_demangle", + "version": "0.5.1", + "repository": "https://github.com/gimli-rs/cpp_demangle", + "declaredLicense": "MIT OR Apache-2.0", + "selectedLicenses": [ + "MIT" + ] + }, { "name": "crc32fast", - "version": "1.3.2", + "version": "1.5.1", "repository": "https://github.com/srijs/rust-crc32fast", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -373,7 +436,7 @@ }, { "name": "crossbeam-channel", - "version": "0.5.15", + "version": "0.5.16", "repository": "https://github.com/crossbeam-rs/crossbeam", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -382,7 +445,7 @@ }, { "name": "crossbeam-deque", - "version": "0.8.5", + "version": "0.8.7", "repository": "https://github.com/crossbeam-rs/crossbeam", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -391,7 +454,7 @@ }, { "name": "crossbeam-epoch", - "version": "0.9.18", + "version": "0.9.20", "repository": "https://github.com/crossbeam-rs/crossbeam", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -399,8 +462,8 @@ ] }, { - "name": "crossbeam-utils", - "version": "0.8.19", + "name": "crossbeam-queue", + "version": "0.3.13", "repository": "https://github.com/crossbeam-rs/crossbeam", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -408,53 +471,35 @@ ] }, { - "name": "crypto-common", - "version": "0.1.6", - "repository": "https://github.com/RustCrypto/traits", + "name": "crossbeam-skiplist", + "version": "0.1.3", + "repository": "https://github.com/crossbeam-rs/crossbeam", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "cynic", - "version": "3.10.0", - "repository": "https://github.com/obmarg/cynic", - "declaredLicense": "MPL-2.0", - "selectedLicenses": [ - "MPL-2.0" - ] - }, - { - "name": "cynic-codegen", - "version": "3.10.0", - "repository": "https://github.com/obmarg/cynic", - "declaredLicense": "MPL-2.0", - "selectedLicenses": [ - "MPL-2.0" - ] - }, - { - "name": "cynic-parser", - "version": "0.9.1", - "repository": "https://github.com/obmarg/cynic", - "declaredLicense": "MPL-2.0", + "name": "crossbeam-utils", + "version": "0.8.22", + "repository": "https://github.com/crossbeam-rs/crossbeam", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ - "MPL-2.0" + "MIT" ] }, { - "name": "cynic-proc-macros", - "version": "3.10.0", - "repository": "https://github.com/obmarg/cynic", - "declaredLicense": "MPL-2.0", + "name": "crypto-common", + "version": "0.2.2", + "repository": "https://github.com/RustCrypto/traits", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ - "MPL-2.0" + "MIT" ] }, { "name": "darling", - "version": "0.14.4", + "version": "0.20.11", "repository": "https://github.com/TedDriggs/darling", "declaredLicense": "MIT", "selectedLicenses": [ @@ -463,7 +508,7 @@ }, { "name": "darling", - "version": "0.20.3", + "version": "0.21.3", "repository": "https://github.com/TedDriggs/darling", "declaredLicense": "MIT", "selectedLicenses": [ @@ -472,7 +517,7 @@ }, { "name": "darling_core", - "version": "0.14.4", + "version": "0.20.11", "repository": "https://github.com/TedDriggs/darling", "declaredLicense": "MIT", "selectedLicenses": [ @@ -481,7 +526,7 @@ }, { "name": "darling_core", - "version": "0.20.3", + "version": "0.21.3", "repository": "https://github.com/TedDriggs/darling", "declaredLicense": "MIT", "selectedLicenses": [ @@ -490,7 +535,7 @@ }, { "name": "darling_macro", - "version": "0.14.4", + "version": "0.20.11", "repository": "https://github.com/TedDriggs/darling", "declaredLicense": "MIT", "selectedLicenses": [ @@ -499,7 +544,7 @@ }, { "name": "darling_macro", - "version": "0.20.3", + "version": "0.21.3", "repository": "https://github.com/TedDriggs/darling", "declaredLicense": "MIT", "selectedLicenses": [ @@ -508,7 +553,7 @@ }, { "name": "dashmap", - "version": "6.1.0", + "version": "6.2.1", "repository": "https://github.com/xacrimon/dashmap", "declaredLicense": "MIT", "selectedLicenses": [ @@ -517,7 +562,7 @@ }, { "name": "deranged", - "version": "0.3.11", + "version": "0.5.8", "repository": "https://github.com/jhpratt/deranged", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -535,7 +580,7 @@ }, { "name": "derive_builder", - "version": "0.12.0", + "version": "0.20.2", "repository": "https://github.com/colin-kiegel/rust-derive-builder", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -544,7 +589,7 @@ }, { "name": "derive_builder_core", - "version": "0.12.0", + "version": "0.20.2", "repository": "https://github.com/colin-kiegel/rust-derive-builder", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -553,7 +598,7 @@ }, { "name": "derive_builder_macro", - "version": "0.12.0", + "version": "0.20.2", "repository": "https://github.com/colin-kiegel/rust-derive-builder", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -562,16 +607,7 @@ }, { "name": "derive_more", - "version": "0.99.19", - "repository": "https://github.com/JelteF/derive_more", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "derive_more", - "version": "2.0.1", + "version": "2.1.1", "repository": "https://github.com/JelteF/derive_more", "declaredLicense": "MIT", "selectedLicenses": [ @@ -580,7 +616,7 @@ }, { "name": "derive_more-impl", - "version": "2.0.1", + "version": "2.1.1", "repository": "https://github.com/JelteF/derive_more", "declaredLicense": "MIT", "selectedLicenses": [ @@ -589,7 +625,7 @@ }, { "name": "digest", - "version": "0.10.7", + "version": "0.11.3", "repository": "https://github.com/RustCrypto/traits", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -598,7 +634,7 @@ }, { "name": "displaydoc", - "version": "0.2.5", + "version": "0.2.7", "repository": "https://github.com/yaahc/displaydoc", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -607,7 +643,7 @@ }, { "name": "document-features", - "version": "0.2.8", + "version": "0.2.12", "repository": "https://github.com/slint-ui/document-features", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -625,7 +661,7 @@ }, { "name": "dyn-clone", - "version": "1.0.17", + "version": "1.0.20", "repository": "https://github.com/dtolnay/dyn-clone", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -633,17 +669,17 @@ ] }, { - "name": "educe", - "version": "0.5.11", - "repository": "https://github.com/magiclen/educe", - "declaredLicense": "MIT", + "name": "either", + "version": "1.18.0", + "repository": "https://github.com/rayon-rs/either", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { "name": "enum-iterator", - "version": "0.7.0", + "version": "2.3.0", "repository": "https://github.com/stephaneyfx/enum-iterator.git", "declaredLicense": "0BSD", "selectedLicenses": [ @@ -652,34 +688,16 @@ }, { "name": "enum-iterator-derive", - "version": "0.7.0", + "version": "1.5.0", "repository": "https://github.com/stephaneyfx/enum-iterator.git", "declaredLicense": "0BSD", "selectedLicenses": [ "0BSD" ] }, - { - "name": "enum-ordinalize", - "version": "4.3.2", - "repository": "https://github.com/magiclen/enum-ordinalize", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "enum-ordinalize-derive", - "version": "4.3.2", - "repository": "https://github.com/magiclen/enum-ordinalize", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "enumset", - "version": "1.1.3", + "version": "1.1.14", "repository": "https://github.com/Lymia/enumset", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -688,7 +706,7 @@ }, { "name": "enumset_derive", - "version": "0.8.1", + "version": "0.15.0", "repository": "https://github.com/Lymia/enumset", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -697,31 +715,40 @@ }, { "name": "equivalent", - "version": "1.0.1", - "repository": "https://github.com/cuviper/equivalent", + "version": "1.0.2", + "repository": "https://github.com/indexmap-rs/equivalent", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ "MIT" ] }, { - "name": "fastrand", - "version": "2.0.1", - "repository": "https://github.com/smol-rs/fastrand", - "declaredLicense": "Apache-2.0 OR MIT", + "name": "escape8259", + "version": "0.5.3", + "repository": "https://github.com/ericseppanen/escape8259", + "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, { - "name": "filetime", - "version": "0.2.23", - "repository": "https://github.com/alexcrichton/filetime", + "name": "fallible-iterator", + "version": "0.3.0", + "repository": "https://github.com/sfackler/rust-fallible-iterator", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, + { + "name": "fastrand", + "version": "2.5.0", + "repository": "https://github.com/smol-rs/fastrand", + "declaredLicense": "Apache-2.0 OR MIT", + "selectedLicenses": [ + "MIT" + ] + }, { "name": "fixedbitset", "version": "0.5.7", @@ -733,7 +760,7 @@ }, { "name": "flate2", - "version": "1.1.1", + "version": "1.1.10", "repository": "https://github.com/rust-lang/flate2-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -749,18 +776,45 @@ "MIT" ] }, + { + "name": "foldhash", + "version": "0.1.5", + "repository": "https://github.com/orlp/foldhash", + "declaredLicense": "Zlib", + "selectedLicenses": [ + "Zlib" + ] + }, + { + "name": "foldhash", + "version": "0.2.0", + "repository": "https://github.com/orlp/foldhash", + "declaredLicense": "Zlib", + "selectedLicenses": [ + "Zlib" + ] + }, { "name": "form_urlencoded", - "version": "1.2.1", + "version": "1.2.2", "repository": "https://github.com/servo/rust-url", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, + { + "name": "fs_extra", + "version": "1.3.0", + "repository": "https://github.com/webdesus/fs_extra", + "declaredLicense": "MIT", + "selectedLicenses": [ + "MIT" + ] + }, { "name": "futures", - "version": "0.3.30", + "version": "0.3.34", "repository": "https://github.com/rust-lang/futures-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -769,7 +823,7 @@ }, { "name": "futures-channel", - "version": "0.3.31", + "version": "0.3.34", "repository": "https://github.com/rust-lang/futures-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -778,7 +832,7 @@ }, { "name": "futures-core", - "version": "0.3.31", + "version": "0.3.34", "repository": "https://github.com/rust-lang/futures-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -787,7 +841,7 @@ }, { "name": "futures-executor", - "version": "0.3.30", + "version": "0.3.34", "repository": "https://github.com/rust-lang/futures-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -796,7 +850,7 @@ }, { "name": "futures-io", - "version": "0.3.31", + "version": "0.3.34", "repository": "https://github.com/rust-lang/futures-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -805,7 +859,7 @@ }, { "name": "futures-macro", - "version": "0.3.31", + "version": "0.3.34", "repository": "https://github.com/rust-lang/futures-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -814,7 +868,7 @@ }, { "name": "futures-sink", - "version": "0.3.31", + "version": "0.3.34", "repository": "https://github.com/rust-lang/futures-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -823,7 +877,7 @@ }, { "name": "futures-task", - "version": "0.3.31", + "version": "0.3.34", "repository": "https://github.com/rust-lang/futures-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -832,7 +886,7 @@ }, { "name": "futures-util", - "version": "0.3.31", + "version": "0.3.34", "repository": "https://github.com/rust-lang/futures-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -840,17 +894,17 @@ ] }, { - "name": "generic-array", - "version": "0.14.7", - "repository": "https://github.com/fizyk20/generic-array.git", - "declaredLicense": "MIT", + "name": "getrandom", + "version": "0.3.4", + "repository": "https://github.com/rust-random/getrandom", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { "name": "getrandom", - "version": "0.2.15", + "version": "0.4.3", "repository": "https://github.com/rust-random/getrandom", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -859,7 +913,16 @@ }, { "name": "gimli", - "version": "0.28.1", + "version": "0.32.3", + "repository": "https://github.com/gimli-rs/gimli", + "declaredLicense": "MIT OR Apache-2.0", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "gimli", + "version": "0.34.0", "repository": "https://github.com/gimli-rs/gimli", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -868,7 +931,7 @@ }, { "name": "globset", - "version": "0.4.14", + "version": "0.4.20", "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", "declaredLicense": "Unlicense OR MIT", "selectedLicenses": [ @@ -877,26 +940,26 @@ }, { "name": "half", - "version": "2.4.1", - "repository": "https://github.com/starkat99/half-rs", + "version": "2.7.1", + "repository": "https://github.com/VoidStarKat/half-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "harsh", - "version": "0.2.2", - "repository": "https://github.com/archer884/harsh", + "name": "hash32", + "version": "0.3.1", + "repository": "https://github.com/japaric/hash32", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "hash32", - "version": "0.3.1", - "repository": "https://github.com/japaric/hash32", + "name": "hashbrown", + "version": "0.14.5", + "repository": "https://github.com/rust-lang/hashbrown", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -904,7 +967,7 @@ }, { "name": "hashbrown", - "version": "0.14.3", + "version": "0.15.5", "repository": "https://github.com/rust-lang/hashbrown", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -913,7 +976,7 @@ }, { "name": "hashbrown", - "version": "0.15.2", + "version": "0.17.1", "repository": "https://github.com/rust-lang/hashbrown", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -922,7 +985,7 @@ }, { "name": "heapless", - "version": "0.8.0", + "version": "0.9.3", "repository": "https://github.com/rust-embedded/heapless", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -940,7 +1003,7 @@ }, { "name": "heck", - "version": "0.4.1", + "version": "0.5.0", "repository": "https://github.com/withoutboats/heck", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -958,7 +1021,7 @@ }, { "name": "http", - "version": "1.3.1", + "version": "1.5.0", "repository": "https://github.com/hyperium/http", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -966,26 +1029,26 @@ ] }, { - "name": "icu_collections", - "version": "1.5.0", - "repository": "https://github.com/unicode-org/icu4x", - "declaredLicense": "Unicode-3.0", + "name": "httparse", + "version": "1.10.1", + "repository": "https://github.com/seanmonstar/httparse", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ - "Unicode-3.0" + "MIT" ] }, { - "name": "icu_locid", - "version": "1.5.0", - "repository": "https://github.com/unicode-org/icu4x", - "declaredLicense": "Unicode-3.0", + "name": "hybrid-array", + "version": "0.4.14", + "repository": "https://github.com/RustCrypto/hybrid-array", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ - "Unicode-3.0" + "MIT" ] }, { - "name": "icu_locid_transform", - "version": "1.5.0", + "name": "icu_collections", + "version": "2.3.0", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -993,8 +1056,8 @@ ] }, { - "name": "icu_locid_transform_data", - "version": "1.5.1", + "name": "icu_locale_core", + "version": "2.3.0", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -1003,7 +1066,7 @@ }, { "name": "icu_normalizer", - "version": "1.5.0", + "version": "2.3.0", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -1012,7 +1075,7 @@ }, { "name": "icu_normalizer_data", - "version": "1.5.1", + "version": "2.3.0", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -1021,7 +1084,7 @@ }, { "name": "icu_properties", - "version": "1.5.1", + "version": "2.3.0", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -1030,7 +1093,7 @@ }, { "name": "icu_properties_data", - "version": "1.5.1", + "version": "2.3.0", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -1039,16 +1102,7 @@ }, { "name": "icu_provider", - "version": "1.5.0", - "repository": "https://github.com/unicode-org/icu4x", - "declaredLicense": "Unicode-3.0", - "selectedLicenses": [ - "Unicode-3.0" - ] - }, - { - "name": "icu_provider_macros", - "version": "1.5.0", + "version": "2.3.1", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -1057,7 +1111,7 @@ }, { "name": "id-arena", - "version": "2.2.1", + "version": "2.3.0", "repository": "https://github.com/fitzgen/id-arena", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1075,7 +1129,7 @@ }, { "name": "idna", - "version": "1.0.3", + "version": "1.1.0", "repository": "https://github.com/servo/rust-url/", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1084,7 +1138,7 @@ }, { "name": "idna_adapter", - "version": "1.2.0", + "version": "1.2.2", "repository": "https://github.com/hsivonen/idna_adapter", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ @@ -1093,7 +1147,7 @@ }, { "name": "ignore", - "version": "0.4.22", + "version": "0.4.33", "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", "declaredLicense": "Unlicense OR MIT", "selectedLicenses": [ @@ -1102,25 +1156,16 @@ }, { "name": "indexmap", - "version": "2.9.0", + "version": "2.14.1", "repository": "https://github.com/indexmap-rs/indexmap", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ "MIT" ] }, - { - "name": "insta", - "version": "1.41.1", - "repository": "https://github.com/mitsuhiko/insta", - "declaredLicense": "Apache-2.0", - "selectedLicenses": [ - "Apache-2.0" - ] - }, { "name": "instant", - "version": "0.1.12", + "version": "0.1.13", "repository": "https://github.com/sebcrozet/instant", "declaredLicense": "BSD-3-Clause", "selectedLicenses": [ @@ -1129,7 +1174,7 @@ }, { "name": "ipnet", - "version": "2.11.0", + "version": "2.12.1", "repository": "https://github.com/krisprice/ipnet", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1146,36 +1191,36 @@ ] }, { - "name": "itoa", - "version": "1.0.10", - "repository": "https://github.com/dtolnay/itoa", + "name": "is_terminal_polyfill", + "version": "1.70.2", + "repository": "https://github.com/polyfill-rs/is_terminal_polyfill", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "js-sys", - "version": "0.3.78", - "repository": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/js-sys", + "name": "itertools", + "version": "0.15.0", + "repository": "https://github.com/rust-itertools/itertools", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "lalrpop-util", - "version": "0.22.1", - "repository": "https://github.com/lalrpop/lalrpop", - "declaredLicense": "Apache-2.0 OR MIT", + "name": "itoa", + "version": "1.0.18", + "repository": "https://github.com/dtolnay/itoa", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "lazy_static", - "version": "1.5.0", - "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", + "name": "js-sys", + "version": "0.3.103", + "repository": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/js-sys", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -1183,9 +1228,9 @@ }, { "name": "leb128", - "version": "0.2.5", + "version": "0.2.7", "repository": "https://github.com/gimli-rs/leb128", - "declaredLicense": "Apache-2.0 OR MIT", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] @@ -1200,18 +1245,18 @@ ] }, { - "name": "lexical-sort", - "version": "0.3.1", - "repository": "https://github.com/Aloso/lexical-sort", + "name": "libc", + "version": "0.2.189", + "repository": "https://github.com/rust-lang/libc", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "libc", - "version": "0.2.171", - "repository": "https://github.com/rust-lang/libc", + "name": "libtest-mimic", + "version": "0.8.2", + "repository": "https://github.com/LukasKalbertodt/libtest-mimic", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -1228,7 +1273,7 @@ }, { "name": "linked_hash_set", - "version": "0.1.4", + "version": "0.1.6", "repository": "https://github.com/alexheretic/linked-hash-set", "declaredLicense": "Apache-2.0", "selectedLicenses": [ @@ -1237,7 +1282,7 @@ }, { "name": "litemap", - "version": "0.7.5", + "version": "0.8.3", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -1246,8 +1291,8 @@ }, { "name": "litrs", - "version": "0.4.1", - "repository": "https://github.com/LukasKalbertodt/litrs/", + "version": "1.0.0", + "repository": "https://github.com/LukasKalbertodt/litrs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -1255,7 +1300,7 @@ }, { "name": "lock_api", - "version": "0.4.11", + "version": "0.4.14", "repository": "https://github.com/Amanieu/parking_lot", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1264,43 +1309,16 @@ }, { "name": "log", - "version": "0.4.20", + "version": "0.4.34", "repository": "https://github.com/rust-lang/log", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, - { - "name": "logos", - "version": "0.14.4", - "repository": "https://github.com/maciejhirsz/logos", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "logos-codegen", - "version": "0.14.4", - "repository": "https://github.com/maciejhirsz/logos", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "logos-derive", - "version": "0.14.4", - "repository": "https://github.com/maciejhirsz/logos", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "lz4_flex", - "version": "0.11.1", + "version": "0.14.0", "repository": "https://github.com/pseitz/lz4_flex", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1316,18 +1334,9 @@ "0BSD" ] }, - { - "name": "matchers", - "version": "0.1.0", - "repository": "https://github.com/hawkw/matchers", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "memchr", - "version": "2.7.1", + "version": "2.8.3", "repository": "https://github.com/BurntSushi/memchr", "declaredLicense": "Unlicense OR MIT", "selectedLicenses": [ @@ -1344,9 +1353,9 @@ ] }, { - "name": "merge-streams", - "version": "0.1.2", - "repository": "https://github.com/phil-opp/merge-streams", + "name": "memmap2", + "version": "0.9.11", + "repository": "https://github.com/RazrFalcon/memmap2-rs", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -1354,7 +1363,7 @@ }, { "name": "miniz_oxide", - "version": "0.7.1", + "version": "0.8.9", "repository": "https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide", "declaredLicense": "MIT OR Zlib OR Apache-2.0", "selectedLicenses": [ @@ -1363,7 +1372,7 @@ }, { "name": "miniz_oxide", - "version": "0.8.7", + "version": "0.9.1", "repository": "https://github.com/Frommi/miniz_oxide/tree/master/miniz_oxide", "declaredLicense": "MIT OR Zlib OR Apache-2.0", "selectedLicenses": [ @@ -1372,16 +1381,16 @@ }, { "name": "more-asserts", - "version": "0.2.2", + "version": "0.3.1", "repository": "https://github.com/thomcc/rust-more-asserts", - "declaredLicense": "CC0-1.0", + "declaredLicense": "Unlicense OR MIT OR Apache-2.0 OR CC0-1.0", "selectedLicenses": [ - "CC0-1.0" + "MIT" ] }, { "name": "munge", - "version": "0.4.3", + "version": "0.4.7", "repository": "https://github.com/djkoloski/munge", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1390,7 +1399,7 @@ }, { "name": "munge_macro", - "version": "0.4.3", + "version": "0.4.7", "repository": "https://github.com/djkoloski/munge", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1407,17 +1416,17 @@ ] }, { - "name": "nu-ansi-term", - "version": "0.46.0", - "repository": "https://github.com/nushell/nu-ansi-term", - "declaredLicense": "MIT", + "name": "nonmax", + "version": "0.5.5", + "repository": "https://github.com/LPGhatguy/nonmax", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { "name": "num-conv", - "version": "0.1.0", + "version": "0.2.2", "repository": "https://github.com/jhpratt/num-conv", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1426,7 +1435,7 @@ }, { "name": "num-traits", - "version": "0.2.17", + "version": "0.2.19", "repository": "https://github.com/rust-num/num-traits", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1444,7 +1453,7 @@ }, { "name": "num_enum", - "version": "0.7.3", + "version": "0.7.6", "repository": "https://github.com/illicitonion/num_enum", "declaredLicense": "BSD-3-Clause OR MIT OR Apache-2.0", "selectedLicenses": [ @@ -1453,7 +1462,7 @@ }, { "name": "num_enum_derive", - "version": "0.7.3", + "version": "0.7.6", "repository": "https://github.com/illicitonion/num_enum", "declaredLicense": "BSD-3-Clause OR MIT OR Apache-2.0", "selectedLicenses": [ @@ -1462,7 +1471,7 @@ }, { "name": "object", - "version": "0.32.2", + "version": "0.37.3", "repository": "https://github.com/gimli-rs/object", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ @@ -1470,44 +1479,44 @@ ] }, { - "name": "once_cell", - "version": "1.19.0", - "repository": "https://github.com/matklad/once_cell", - "declaredLicense": "MIT OR Apache-2.0", + "name": "object", + "version": "0.40.0", + "repository": "https://github.com/gimli-rs/object", + "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ "MIT" ] }, { - "name": "ouroboros", - "version": "0.18.5", - "repository": "https://github.com/someguynamedjosh/ouroboros", - "declaredLicense": "MIT OR Apache-2.0", + "name": "offset-allocator", + "version": "0.2.0", + "repository": "https://github.com/pcwalton/offset-allocator/", + "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, { - "name": "ouroboros_macro", - "version": "0.18.5", - "repository": "https://github.com/someguynamedjosh/ouroboros", + "name": "once_cell", + "version": "1.21.4", + "repository": "https://github.com/matklad/once_cell", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "overload", - "version": "0.1.1", - "repository": "https://github.com/danaugrs/overload", - "declaredLicense": "MIT", + "name": "parking", + "version": "2.2.1", + "repository": "https://github.com/smol-rs/parking", + "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ "MIT" ] }, { "name": "parking_lot_core", - "version": "0.9.9", + "version": "0.9.12", "repository": "https://github.com/Amanieu/parking_lot", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1524,35 +1533,62 @@ ] }, { - "name": "path-clean", - "version": "1.0.1", - "repository": "https://github.com/danreeves/path-clean", + "name": "percent-encoding", + "version": "2.3.2", + "repository": "https://github.com/servo/rust-url/", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "percent-encoding", - "version": "2.3.1", - "repository": "https://github.com/servo/rust-url/", + "name": "petgraph", + "version": "0.8.3", + "repository": "https://github.com/petgraph/petgraph", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "petgraph", - "version": "0.7.1", - "repository": "https://github.com/petgraph/petgraph", - "declaredLicense": "MIT OR Apache-2.0", + "name": "phf", + "version": "0.14.0", + "repository": "https://github.com/rust-phf/rust-phf", + "declaredLicense": "MIT", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "phf_generator", + "version": "0.14.0", + "repository": "https://github.com/rust-phf/rust-phf", + "declaredLicense": "MIT", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "phf_macros", + "version": "0.14.0", + "repository": "https://github.com/rust-phf/rust-phf", + "declaredLicense": "MIT", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "phf_shared", + "version": "0.14.0", + "repository": "https://github.com/rust-phf/rust-phf", + "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, { "name": "pin-project", - "version": "1.1.3", + "version": "1.1.13", "repository": "https://github.com/taiki-e/pin-project", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ @@ -1561,7 +1597,7 @@ }, { "name": "pin-project-internal", - "version": "1.1.3", + "version": "1.1.13", "repository": "https://github.com/taiki-e/pin-project", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ @@ -1570,7 +1606,7 @@ }, { "name": "pin-project-lite", - "version": "0.2.13", + "version": "0.2.17", "repository": "https://github.com/taiki-e/pin-project-lite", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ @@ -1587,18 +1623,18 @@ ] }, { - "name": "powerfmt", - "version": "0.2.0", - "repository": "https://github.com/jhpratt/powerfmt", - "declaredLicense": "MIT OR Apache-2.0", + "name": "potential_utf", + "version": "0.1.6", + "repository": "https://github.com/unicode-org/icu4x", + "declaredLicense": "Unicode-3.0", "selectedLicenses": [ - "MIT" + "Unicode-3.0" ] }, { - "name": "ppv-lite86", - "version": "0.2.17", - "repository": "https://github.com/cryptocorrosion/cryptocorrosion", + "name": "powerfmt", + "version": "0.2.0", + "repository": "https://github.com/jhpratt/powerfmt", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -1606,7 +1642,7 @@ }, { "name": "proc-macro-crate", - "version": "1.3.1", + "version": "3.5.0", "repository": "https://github.com/bkchr/proc-macro-crate", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1633,34 +1669,16 @@ }, { "name": "proc-macro2", - "version": "1.0.94", + "version": "1.0.107", "repository": "https://github.com/dtolnay/proc-macro2", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, - { - "name": "proc-macro2-diagnostics", - "version": "0.10.1", - "repository": "https://github.com/SergioBenitez/proc-macro2-diagnostics", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "ptr_meta", - "version": "0.1.4", - "repository": "https://github.com/djkoloski/ptr_meta", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "ptr_meta", - "version": "0.3.0", + "version": "0.3.2", "repository": "https://github.com/rkyv/ptr_meta", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1669,16 +1687,7 @@ }, { "name": "ptr_meta_derive", - "version": "0.1.4", - "repository": "https://github.com/djkoloski/ptr_meta", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "ptr_meta_derive", - "version": "0.3.0", + "version": "0.3.2", "repository": "https://github.com/rkyv/ptr_meta", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1696,7 +1705,7 @@ }, { "name": "quote", - "version": "1.0.35", + "version": "1.0.47", "repository": "https://github.com/dtolnay/quote", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1705,7 +1714,7 @@ }, { "name": "rancor", - "version": "0.1.0", + "version": "0.1.3", "repository": "https://github.com/rkyv/rancor", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1714,7 +1723,7 @@ }, { "name": "rand", - "version": "0.8.5", + "version": "0.10.2", "repository": "https://github.com/rust-random/rand", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1722,18 +1731,18 @@ ] }, { - "name": "rand_chacha", - "version": "0.3.1", - "repository": "https://github.com/rust-random/rand", + "name": "rand_core", + "version": "0.10.1", + "repository": "https://github.com/rust-random/rand_core", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "rand_core", - "version": "0.6.4", - "repository": "https://github.com/rust-random/rand", + "name": "rangemap", + "version": "1.8.0", + "repository": "https://github.com/jeffparsons/rangemap", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -1741,7 +1750,7 @@ }, { "name": "ref-cast", - "version": "1.0.24", + "version": "1.0.27", "repository": "https://github.com/dtolnay/ref-cast", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1750,43 +1759,16 @@ }, { "name": "ref-cast-impl", - "version": "1.0.24", + "version": "1.0.27", "repository": "https://github.com/dtolnay/ref-cast", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, - { - "name": "regex", - "version": "1.10.2", - "repository": "https://github.com/rust-lang/regex", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "regex-automata", - "version": "0.1.10", - "repository": "https://github.com/BurntSushi/regex-automata", - "declaredLicense": "Unlicense OR MIT", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "regex-automata", - "version": "0.4.3", - "repository": "https://github.com/rust-lang/regex/tree/master/regex-automata", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "regex-syntax", - "version": "0.6.29", + "version": "0.4.18", "repository": "https://github.com/rust-lang/regex", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1795,8 +1777,8 @@ }, { "name": "regex-syntax", - "version": "0.8.2", - "repository": "https://github.com/rust-lang/regex/tree/master/regex-syntax", + "version": "0.8.11", + "repository": "https://github.com/rust-lang/regex", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -1804,7 +1786,7 @@ }, { "name": "rend", - "version": "0.5.2", + "version": "0.5.4", "repository": "https://github.com/djkoloski/rend", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1813,25 +1795,16 @@ }, { "name": "replace_with", - "version": "0.1.7", + "version": "0.1.8", "repository": "https://github.com/alecmocatta/replace_with", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, - { - "name": "reqwest", - "version": "0.12.7", - "repository": "https://github.com/seanmonstar/reqwest", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "rkyv", - "version": "0.8.10", + "version": "0.8.18", "repository": "https://github.com/rkyv/rkyv", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1840,7 +1813,7 @@ }, { "name": "rkyv_derive", - "version": "0.8.10", + "version": "0.8.18", "repository": "https://github.com/rkyv/rkyv", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1849,8 +1822,8 @@ }, { "name": "rustc-demangle", - "version": "0.1.23", - "repository": "https://github.com/alexcrichton/rustc-demangle", + "version": "0.1.28", + "repository": "https://github.com/rust-lang/rustc-demangle", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -1858,7 +1831,7 @@ }, { "name": "rustversion", - "version": "1.0.20", + "version": "1.0.23", "repository": "https://github.com/dtolnay/rustversion", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1867,7 +1840,7 @@ }, { "name": "ruzstd", - "version": "0.5.0", + "version": "0.9.0", "repository": "https://github.com/KillingSpark/zstd-rs", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1876,7 +1849,7 @@ }, { "name": "ryu", - "version": "1.0.16", + "version": "1.0.23", "repository": "https://github.com/dtolnay/ryu", "declaredLicense": "Apache-2.0 OR BSL-1.0", "selectedLicenses": [ @@ -1903,7 +1876,7 @@ }, { "name": "schemars", - "version": "0.8.21", + "version": "1.2.2", "repository": "https://github.com/GREsau/schemars", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1912,7 +1885,7 @@ }, { "name": "schemars_derive", - "version": "0.8.21", + "version": "1.2.2", "repository": "https://github.com/GREsau/schemars", "declaredLicense": "MIT", "selectedLicenses": [ @@ -1930,16 +1903,16 @@ }, { "name": "self_cell", - "version": "1.0.3", + "version": "1.3.0", "repository": "https://github.com/Voultapher/self_cell", - "declaredLicense": "Apache-2.0", + "declaredLicense": "Apache-2.0 OR GPL-2.0-only", "selectedLicenses": [ "Apache-2.0" ] }, { "name": "semver", - "version": "1.0.26", + "version": "1.0.28", "repository": "https://github.com/dtolnay/semver", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1948,7 +1921,7 @@ }, { "name": "serde", - "version": "1.0.219", + "version": "1.0.229", "repository": "https://github.com/serde-rs/serde", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1966,7 +1939,7 @@ }, { "name": "serde_bytes", - "version": "0.11.14", + "version": "0.11.19", "repository": "https://github.com/serde-rs/bytes", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1974,8 +1947,8 @@ ] }, { - "name": "serde_derive", - "version": "1.0.219", + "name": "serde_core", + "version": "1.0.229", "repository": "https://github.com/serde-rs/serde", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1983,8 +1956,8 @@ ] }, { - "name": "serde_derive_internals", - "version": "0.29.1", + "name": "serde_derive", + "version": "1.0.229", "repository": "https://github.com/serde-rs/serde", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -1992,18 +1965,18 @@ ] }, { - "name": "serde_json", - "version": "1.0.140", - "repository": "https://github.com/serde-rs/json", + "name": "serde_derive_internals", + "version": "0.30.0", + "repository": "https://github.com/serde-rs/serde", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "serde_path_to_error", - "version": "0.1.17", - "repository": "https://github.com/dtolnay/path-to-error", + "name": "serde_json", + "version": "1.0.151", + "repository": "https://github.com/serde-rs/json", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" @@ -2011,22 +1984,13 @@ }, { "name": "serde_spanned", - "version": "0.6.6", + "version": "1.1.1", "repository": "https://github.com/toml-rs/toml", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, - { - "name": "serde_urlencoded", - "version": "0.7.1", - "repository": "https://github.com/nox/serde_urlencoded", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "serde_yaml", "version": "0.9.34+deprecated", @@ -2038,7 +2002,7 @@ }, { "name": "sha2", - "version": "0.10.8", + "version": "0.11.0", "repository": "https://github.com/RustCrypto/hashes", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2046,26 +2010,26 @@ ] }, { - "name": "sharded-slab", - "version": "0.1.7", - "repository": "https://github.com/hawkw/sharded-slab", - "declaredLicense": "MIT", + "name": "shared-buffer", + "version": "0.1.4", + "repository": "https://github.com/wasmerio/shared-buffer.git", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { - "name": "shared-buffer", - "version": "0.1.4", - "repository": "https://github.com/wasmerio/shared-buffer.git", - "declaredLicense": "MIT OR Apache-2.0", + "name": "simd-adler32", + "version": "0.3.10", + "repository": "https://github.com/mcountryman/simd-adler32", + "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, { "name": "simdutf8", - "version": "0.1.4", + "version": "0.1.5", "repository": "https://github.com/rusticstuff/simdutf8", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2073,17 +2037,17 @@ ] }, { - "name": "similar", - "version": "2.7.0", - "repository": "https://github.com/mitsuhiko/similar", - "declaredLicense": "Apache-2.0", + "name": "siphasher", + "version": "1.0.3", + "repository": "https://github.com/jedisct1/rust-siphash", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ - "Apache-2.0" + "MIT" ] }, { "name": "slab", - "version": "0.4.9", + "version": "0.4.12", "repository": "https://github.com/tokio-rs/slab", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2092,7 +2056,7 @@ }, { "name": "smallvec", - "version": "1.13.2", + "version": "1.15.2", "repository": "https://github.com/servo/rust-smallvec", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2101,7 +2065,7 @@ }, { "name": "smoltcp", - "version": "0.8.2", + "version": "0.13.1", "repository": "https://github.com/smoltcp-rs/smoltcp.git", "declaredLicense": "0BSD", "selectedLicenses": [ @@ -2110,26 +2074,17 @@ }, { "name": "stable_deref_trait", - "version": "1.2.0", + "version": "1.2.1", "repository": "https://github.com/storyyeller/stable_deref_trait", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, - { - "name": "static_assertions", - "version": "1.1.0", - "repository": "https://github.com/nvzqz/static-assertions-rs", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "strsim", - "version": "0.10.0", - "repository": "https://github.com/dguo/strsim-rs", + "version": "0.11.1", + "repository": "https://github.com/rapidfuzz/strsim-rs", "declaredLicense": "MIT", "selectedLicenses": [ "MIT" @@ -2146,7 +2101,7 @@ }, { "name": "syn", - "version": "2.0.100", + "version": "2.0.119", "repository": "https://github.com/dtolnay/syn", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2154,35 +2109,26 @@ ] }, { - "name": "sync_wrapper", - "version": "1.0.1", - "repository": "https://github.com/Actyx/sync_wrapper", - "declaredLicense": "Apache-2.0", + "name": "syn", + "version": "3.0.4", + "repository": "https://github.com/dtolnay/syn", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ - "Apache-2.0" + "MIT" ] }, { "name": "synstructure", - "version": "0.13.1", + "version": "0.13.2", "repository": "https://github.com/mystor/synstructure", "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, - { - "name": "tar", - "version": "0.4.44", - "repository": "https://github.com/alexcrichton/tar-rs", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "target-lexicon", - "version": "0.12.16", + "version": "0.13.5", "repository": "https://github.com/bytecodealliance/target-lexicon", "declaredLicense": "Apache-2.0 WITH LLVM-exception", "selectedLicenses": [ @@ -2191,7 +2137,7 @@ }, { "name": "tempfile", - "version": "3.12.0", + "version": "3.27.0", "repository": "https://github.com/Stebalien/tempfile", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2200,7 +2146,7 @@ }, { "name": "thiserror", - "version": "1.0.69", + "version": "2.0.20", "repository": "https://github.com/dtolnay/thiserror", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2209,25 +2155,16 @@ }, { "name": "thiserror-impl", - "version": "1.0.69", + "version": "2.0.20", "repository": "https://github.com/dtolnay/thiserror", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, - { - "name": "thread_local", - "version": "1.1.7", - "repository": "https://github.com/Amanieu/thread_local-rs", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "time", - "version": "0.3.36", + "version": "0.3.55", "repository": "https://github.com/time-rs/time", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2236,7 +2173,7 @@ }, { "name": "time-core", - "version": "0.1.2", + "version": "0.1.9", "repository": "https://github.com/time-rs/time", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2245,7 +2182,7 @@ }, { "name": "tinystr", - "version": "0.7.6", + "version": "0.8.4", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -2254,7 +2191,7 @@ }, { "name": "tinyvec", - "version": "1.6.0", + "version": "1.12.0", "repository": "https://github.com/Lokathor/tinyvec", "declaredLicense": "Zlib OR Apache-2.0 OR MIT", "selectedLicenses": [ @@ -2272,7 +2209,7 @@ }, { "name": "tokio", - "version": "1.44.2", + "version": "1.53.1", "repository": "https://github.com/tokio-rs/tokio", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2281,25 +2218,16 @@ }, { "name": "tokio-macros", - "version": "2.5.0", + "version": "2.7.2", "repository": "https://github.com/tokio-rs/tokio", "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, - { - "name": "tokio-serde", - "version": "0.9.0", - "repository": "https://github.com/carllerche/tokio-serde", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "tokio-stream", - "version": "0.1.15", + "version": "0.1.19", "repository": "https://github.com/tokio-rs/tokio", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2308,7 +2236,7 @@ }, { "name": "tokio-util", - "version": "0.7.11", + "version": "0.7.19", "repository": "https://github.com/tokio-rs/tokio", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2317,7 +2245,7 @@ }, { "name": "toml", - "version": "0.8.14", + "version": "1.1.4+spec-1.1.0", "repository": "https://github.com/toml-rs/toml", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2326,7 +2254,7 @@ }, { "name": "toml_datetime", - "version": "0.6.6", + "version": "1.1.1+spec-1.1.0", "repository": "https://github.com/toml-rs/toml", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2335,7 +2263,7 @@ }, { "name": "toml_edit", - "version": "0.19.15", + "version": "0.25.13+spec-1.1.0", "repository": "https://github.com/toml-rs/toml", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2343,8 +2271,8 @@ ] }, { - "name": "toml_edit", - "version": "0.22.14", + "name": "toml_parser", + "version": "1.1.3+spec-1.1.0", "repository": "https://github.com/toml-rs/toml", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2352,17 +2280,17 @@ ] }, { - "name": "tower-service", - "version": "0.3.2", - "repository": "https://github.com/tower-rs/tower", - "declaredLicense": "MIT", + "name": "toml_writer", + "version": "1.1.2+spec-1.1.0", + "repository": "https://github.com/toml-rs/toml", + "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, { "name": "tracing", - "version": "0.1.41", + "version": "0.1.44", "repository": "https://github.com/tokio-rs/tracing", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2371,7 +2299,7 @@ }, { "name": "tracing-attributes", - "version": "0.1.28", + "version": "0.1.31", "repository": "https://github.com/tokio-rs/tracing", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2380,16 +2308,7 @@ }, { "name": "tracing-core", - "version": "0.1.33", - "repository": "https://github.com/tokio-rs/tracing", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "tracing-log", - "version": "0.2.0", + "version": "0.1.36", "repository": "https://github.com/tokio-rs/tracing", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2397,18 +2316,18 @@ ] }, { - "name": "tracing-subscriber", - "version": "0.3.18", - "repository": "https://github.com/tokio-rs/tracing", + "name": "twox-hash", + "version": "2.1.4", + "repository": "https://github.com/shepmaster/twox-hash", "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, { - "name": "twox-hash", - "version": "1.6.3", - "repository": "https://github.com/shepmaster/twox-hash", + "name": "typed-arena", + "version": "2.0.2", + "repository": "https://github.com/SimonSapin/rust-typed-arena", "declaredLicense": "MIT", "selectedLicenses": [ "MIT" @@ -2416,7 +2335,7 @@ }, { "name": "typenum", - "version": "1.17.0", + "version": "1.20.1", "repository": "https://github.com/paholg/typenum", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2425,7 +2344,7 @@ }, { "name": "unicase", - "version": "2.7.0", + "version": "2.9.0", "repository": "https://github.com/seanmonstar/unicase", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2434,17 +2353,17 @@ }, { "name": "unicode-ident", - "version": "1.0.12", + "version": "1.0.24", "repository": "https://github.com/dtolnay/unicode-ident", - "declaredLicense": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", + "declaredLicense": "(MIT OR Apache-2.0) AND Unicode-3.0", "selectedLicenses": [ "MIT", - "Unicode-DFS-2016" + "Unicode-3.0" ] }, { "name": "unicode-normalization", - "version": "0.1.22", + "version": "0.1.25", "repository": "https://github.com/unicode-rs/unicode-normalization", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2453,25 +2372,16 @@ }, { "name": "unicode-segmentation", - "version": "1.10.1", + "version": "1.13.3", "repository": "https://github.com/unicode-rs/unicode-segmentation", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, - { - "name": "unicode-width", - "version": "0.2.0", - "repository": "https://github.com/unicode-rs/unicode-width", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "unicode-xid", - "version": "0.2.4", + "version": "0.2.6", "repository": "https://github.com/unicode-rs/unicode-xid", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2487,9 +2397,18 @@ "MIT" ] }, + { + "name": "unty", + "version": "0.0.4", + "repository": "https://github.com/bincode-org/unty", + "declaredLicense": "MIT OR Apache-2.0", + "selectedLicenses": [ + "MIT" + ] + }, { "name": "url", - "version": "2.5.4", + "version": "2.5.8", "repository": "https://github.com/servo/rust-url", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2506,18 +2425,18 @@ ] }, { - "name": "utf16_iter", - "version": "1.0.5", - "repository": "https://github.com/hsivonen/utf16_iter", + "name": "utf8_iter", + "version": "1.0.4", + "repository": "https://github.com/hsivonen/utf8_iter", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ "MIT" ] }, { - "name": "utf8_iter", - "version": "1.0.4", - "repository": "https://github.com/hsivonen/utf8_iter", + "name": "utf8parse", + "version": "0.2.2", + "repository": "https://github.com/alacritty/vte", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ "MIT" @@ -2525,7 +2444,7 @@ }, { "name": "virtual-fs", - "version": "0.601.0", + "version": "0.704.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2534,7 +2453,7 @@ }, { "name": "virtual-mio", - "version": "0.601.0", + "version": "0.704.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2543,13 +2462,22 @@ }, { "name": "virtual-net", - "version": "0.601.0", + "version": "0.704.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, + { + "name": "virtue", + "version": "0.0.18", + "repository": "https://github.com/bincode-org/virtue", + "declaredLicense": "MIT", + "selectedLicenses": [ + "MIT" + ] + }, { "name": "wai-bindgen-gen-core", "version": "0.2.3", @@ -2606,7 +2534,7 @@ }, { "name": "waker-fn", - "version": "1.1.1", + "version": "1.2.0", "repository": "https://github.com/smol-rs/waker-fn", "declaredLicense": "Apache-2.0 OR MIT", "selectedLicenses": [ @@ -2615,7 +2543,7 @@ }, { "name": "walkdir", - "version": "2.4.0", + "version": "2.5.0", "repository": "https://github.com/BurntSushi/walkdir", "declaredLicense": "Unlicense OR MIT", "selectedLicenses": [ @@ -2624,43 +2552,16 @@ }, { "name": "wasm-bindgen", - "version": "0.2.101", + "version": "0.2.126", "repository": "https://github.com/wasm-bindgen/wasm-bindgen", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, - { - "name": "wasm-bindgen-backend", - "version": "0.2.101", - "repository": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/backend", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "wasm-bindgen-derive", - "version": "0.2.1", - "repository": "https://github.com/fjarri/wasm-bindgen-derive", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "wasm-bindgen-derive-macro", - "version": "0.2.1", - "repository": "https://github.com/fjarri/wasm-bindgen-derive", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "wasm-bindgen-futures", - "version": "0.4.51", + "version": "0.4.76", "repository": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/futures", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2669,7 +2570,7 @@ }, { "name": "wasm-bindgen-macro", - "version": "0.2.101", + "version": "0.2.126", "repository": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/macro", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2678,7 +2579,7 @@ }, { "name": "wasm-bindgen-macro-support", - "version": "0.2.101", + "version": "0.2.126", "repository": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/macro-support", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2687,34 +2588,16 @@ }, { "name": "wasm-bindgen-shared", - "version": "0.2.101", + "version": "0.2.126", "repository": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/shared", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ "MIT" ] }, - { - "name": "wasm-bindgen-test", - "version": "0.3.51", - "repository": "https://github.com/wasm-bindgen/wasm-bindgen", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "wasm-bindgen-test-macro", - "version": "0.3.51", - "repository": "https://github.com/wasm-bindgen/wasm-bindgen", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "wasm-encoder", - "version": "0.221.3", + "version": "0.257.1", "repository": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasm-encoder", "declaredLicense": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "selectedLicenses": [ @@ -2722,26 +2605,26 @@ ] }, { - "name": "wasm-encoder", - "version": "0.235.0", - "repository": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasm-encoder", - "declaredLicense": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "name": "wasmer", + "version": "7.4.0", + "repository": "https://github.com/wasmerio/wasmer", + "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, { - "name": "wasm-streams", - "version": "0.4.0", - "repository": "https://github.com/MattiasBuelens/wasm-streams/", - "declaredLicense": "MIT OR Apache-2.0", + "name": "wasmer-c-api-imports", + "version": "7.4.0", + "repository": "https://github.com/wasmerio/wasmer", + "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, { - "name": "wasmer", - "version": "6.1.0", + "name": "wasmer-compiler", + "version": "7.4.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2749,8 +2632,8 @@ ] }, { - "name": "wasmer-backend-api", - "version": "0.601.0", + "name": "wasmer-config", + "version": "0.704.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2758,8 +2641,8 @@ ] }, { - "name": "wasmer-compiler", - "version": "6.1.0", + "name": "wasmer-derive", + "version": "7.4.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2767,8 +2650,8 @@ ] }, { - "name": "wasmer-config", - "version": "0.601.0", + "name": "wasmer-journal", + "version": "0.704.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2776,17 +2659,17 @@ ] }, { - "name": "wasmer-derive", - "version": "6.1.0", - "repository": "https://github.com/wasmerio/wasmer", + "name": "wasmer-napi", + "version": "0.704.0", + "repository": null, "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, { - "name": "wasmer-journal", - "version": "0.601.0", + "name": "wasmer-package", + "version": "0.704.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2794,17 +2677,17 @@ ] }, { - "name": "wasmer-package", - "version": "0.601.0", - "repository": "https://github.com/wasmerio/wasmer", - "declaredLicense": "MIT", + "name": "wasmer-sdk", + "version": "0.1.0", + "repository": "https://github.com/wasmerio/wasmer-sdk", + "declaredLicense": "Unknown", "selectedLicenses": [ "MIT" ] }, { "name": "wasmer-types", - "version": "6.1.0", + "version": "7.4.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT OR Apache-2.0 WITH LLVM-exception", "selectedLicenses": [ @@ -2813,7 +2696,7 @@ }, { "name": "wasmer-wasix", - "version": "0.601.0", + "version": "0.704.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2822,7 +2705,7 @@ }, { "name": "wasmer-wasix-types", - "version": "0.601.0", + "version": "0.704.0", "repository": "https://github.com/wasmerio/wasmer", "declaredLicense": "MIT", "selectedLicenses": [ @@ -2831,34 +2714,16 @@ }, { "name": "wasmparser", - "version": "0.224.1", + "version": "0.257.1", "repository": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser", "declaredLicense": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", "selectedLicenses": [ "MIT" ] }, - { - "name": "wast", - "version": "221.0.3", - "repository": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wast", - "declaredLicense": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "wat", - "version": "1.221.3", - "repository": "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wat", - "declaredLicense": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "web-sys", - "version": "0.3.78", + "version": "0.3.103", "repository": "https://github.com/wasm-bindgen/wasm-bindgen/tree/master/crates/web-sys", "declaredLicense": "MIT OR Apache-2.0", "selectedLicenses": [ @@ -2876,52 +2741,25 @@ }, { "name": "webc", - "version": "10.0.1", + "version": "12.0.1", "repository": "https://github.com/wasmerio/pirita", "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, - { - "name": "weezl", - "version": "0.1.7", - "repository": "https://github.com/image-rs/lzw.git", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "winnow", - "version": "0.5.33", - "repository": "https://github.com/winnow-rs/winnow", - "declaredLicense": "MIT", - "selectedLicenses": [ - "MIT" - ] - }, - { - "name": "winnow", - "version": "0.6.13", + "version": "1.0.4", "repository": "https://github.com/winnow-rs/winnow", "declaredLicense": "MIT", "selectedLicenses": [ "MIT" ] }, - { - "name": "write16", - "version": "1.0.0", - "repository": "https://github.com/hsivonen/write16", - "declaredLicense": "Apache-2.0 OR MIT", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "writeable", - "version": "0.5.5", + "version": "0.6.4", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -2930,25 +2768,16 @@ }, { "name": "xxhash-rust", - "version": "0.8.11", + "version": "0.8.18", "repository": "https://github.com/DoumanAsh/xxhash-rust", "declaredLicense": "BSL-1.0", "selectedLicenses": [ "BSL-1.0" ] }, - { - "name": "yansi", - "version": "1.0.1", - "repository": "https://github.com/SergioBenitez/yansi", - "declaredLicense": "MIT OR Apache-2.0", - "selectedLicenses": [ - "MIT" - ] - }, { "name": "yoke", - "version": "0.7.5", + "version": "0.8.3", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -2957,7 +2786,7 @@ }, { "name": "yoke-derive", - "version": "0.7.5", + "version": "0.8.2", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -2966,7 +2795,16 @@ }, { "name": "zerocopy", - "version": "0.7.35", + "version": "0.8.56", + "repository": "https://github.com/google/zerocopy", + "declaredLicense": "BSD-2-Clause OR Apache-2.0 OR MIT", + "selectedLicenses": [ + "MIT" + ] + }, + { + "name": "zerocopy-derive", + "version": "0.8.56", "repository": "https://github.com/google/zerocopy", "declaredLicense": "BSD-2-Clause OR Apache-2.0 OR MIT", "selectedLicenses": [ @@ -2975,7 +2813,7 @@ }, { "name": "zerofrom", - "version": "0.1.6", + "version": "0.1.8", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -2984,7 +2822,16 @@ }, { "name": "zerofrom-derive", - "version": "0.1.6", + "version": "0.1.7", + "repository": "https://github.com/unicode-org/icu4x", + "declaredLicense": "Unicode-3.0", + "selectedLicenses": [ + "Unicode-3.0" + ] + }, + { + "name": "zerotrie", + "version": "0.2.5", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -2993,7 +2840,7 @@ }, { "name": "zerovec", - "version": "0.10.4", + "version": "0.11.8", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ @@ -3002,12 +2849,21 @@ }, { "name": "zerovec-derive", - "version": "0.10.3", + "version": "0.11.6", "repository": "https://github.com/unicode-org/icu4x", "declaredLicense": "Unicode-3.0", "selectedLicenses": [ "Unicode-3.0" ] + }, + { + "name": "zmij", + "version": "1.0.23", + "repository": "https://github.com/dtolnay/zmij", + "declaredLicense": "MIT", + "selectedLicenses": [ + "MIT" + ] } ] } diff --git a/package.json b/package.json index 0102e63..a360d83 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "@wasm-oj/toolchain-javascript": "workspace:0.2.0", "@wasm-oj/toolchain-python": "workspace:0.2.0", "@wasm-oj/toolchain-rust": "workspace:0.2.0", - "@wasmer/sdk": "0.10.0", + "@wasmer/sdk": "0.19.0", "buffer": "6.0.3", "es-module-lexer": "2.3.1", "events": "3.3.0", diff --git a/packages/browser/package.json b/packages/browser/package.json index b6e50d9..53f79e4 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -50,7 +50,6 @@ "dependencies": { "@wasm-oj/contracts": "workspace:0.2.3", "@wasm-oj/core": "workspace:0.2.3", - "@wasmer/sdk": "0.10.0", "es-module-lexer": "2.3.1", "fflate": "0.8.3" } diff --git a/packages/cli/THIRD_PARTY_NOTICES.md b/packages/cli/THIRD_PARTY_NOTICES.md index bd70d1b..86dbc20 100644 --- a/packages/cli/THIRD_PARTY_NOTICES.md +++ b/packages/cli/THIRD_PARTY_NOTICES.md @@ -49,21 +49,28 @@ canonical Go module `h1:` hash over module ZIP entries. ### Wasmer JavaScript SDK -- Component: `@wasmer/sdk` 0.10.0 -- npm integrity: `sha512-YQ+s5tGag6P/I8kp9BTH+XhjoS9UFvWiZJvnWEEovClHffhYToKhprWr4UJG7wLP7c/2HQpGkF7ZrjoUvKjdmA==` -- Source revision: `wasmerio/wasmer-js@93b8b738ebd3ee57e118da0f0eb795b97d5b999e` +- Component: `@wasmer/sdk` 0.19.0 +- npm integrity: `sha512-4gdWiIlne8ti3dQl1yD6jrLLNKrQXmZXA6iy6ta/6sgYpixPLwymeGqhMzjAIHqhmwrD+SjPFpZ5ugd9SytDrQ==` +- Source revision: `wasmerio/wasmer-sdk@7e69332b7f65dbc5584d64bb79f547eaf4302b69` - Locked Rust graph: `Cargo.lock` SHA-256 - `d352926f3f05e3d4308c4e261711d07db568e5c2b4387067180f920da074791f` -- Source: -- License: MIT -- License material: `licenses/wasmer-sdk-MIT.txt`, + `34156a76319127aa4152e8b25bd92a5657f7ddc0669ba3e26209cc57053f56a1` +- Source: +- License: Wasmer Modified MIT License. It adds one condition to MIT: a commercial product or + service with more than 1 million monthly active users or more than 1 million US dollars in + monthly revenue must prominently display "Wasmer" in its user interface. +- Bundled JavaScript dependency: `acorn` 8.18.0 (MIT), shipped in the SDK's wasm-bindgen snippets +- License material: `licenses/wasmer-sdk-LICENSE.txt`, `licenses/wasmer-sdk-acorn-MIT.txt`, `licenses/wasmer-sdk-dependencies.html`, and `licenses/wasmer-sdk-dependencies.json` -WASM-OJ consumes the official npm artifact without patching the installed -package. The generated dependency report covers every package in the pinned -normal Cargo dependency graph selected for `wasm32-unknown-unknown`; its compact -inventory binds all 332 package identities and the exact HTML report digest. +WASM-OJ ships the official npm runtime files unbundled. Its only modification replaces the +wasm-bindgen glue's `new Function` construction of Wasmer's fixed host-function trampoline with +the equivalent closure so the SDK runs under a strict Content Security Policy. The browser build +omits the SDK's WISP networking module, the only path to its AGPL-3.0 `@mercuryworkshop/wisp-js` +dependency, which WASM-OJ therefore does not distribute. The generated dependency report covers +every package in the pinned normal Cargo dependency graph of the SDK's `wasmer-sdk-js` crate +selected for `wasm32-unknown-unknown`; its compact inventory binds all 316 package identities and +the exact HTML report digest. ### QuickJS guest Node standard I/O libraries diff --git a/packages/server/package.json b/packages/server/package.json index 6c55e7b..de16e48 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -65,7 +65,7 @@ "dependencies": { "@wasm-oj/contracts": "workspace:0.2.3", "@wasm-oj/core": "workspace:0.2.3", - "@wasmer/sdk": "0.10.0", + "@wasmer/sdk": "0.19.0", "fflate": "0.8.3" } } diff --git a/packages/toolchain-java/THIRD_PARTY_NOTICES.md b/packages/toolchain-java/THIRD_PARTY_NOTICES.md index bd70d1b..86dbc20 100644 --- a/packages/toolchain-java/THIRD_PARTY_NOTICES.md +++ b/packages/toolchain-java/THIRD_PARTY_NOTICES.md @@ -49,21 +49,28 @@ canonical Go module `h1:` hash over module ZIP entries. ### Wasmer JavaScript SDK -- Component: `@wasmer/sdk` 0.10.0 -- npm integrity: `sha512-YQ+s5tGag6P/I8kp9BTH+XhjoS9UFvWiZJvnWEEovClHffhYToKhprWr4UJG7wLP7c/2HQpGkF7ZrjoUvKjdmA==` -- Source revision: `wasmerio/wasmer-js@93b8b738ebd3ee57e118da0f0eb795b97d5b999e` +- Component: `@wasmer/sdk` 0.19.0 +- npm integrity: `sha512-4gdWiIlne8ti3dQl1yD6jrLLNKrQXmZXA6iy6ta/6sgYpixPLwymeGqhMzjAIHqhmwrD+SjPFpZ5ugd9SytDrQ==` +- Source revision: `wasmerio/wasmer-sdk@7e69332b7f65dbc5584d64bb79f547eaf4302b69` - Locked Rust graph: `Cargo.lock` SHA-256 - `d352926f3f05e3d4308c4e261711d07db568e5c2b4387067180f920da074791f` -- Source: -- License: MIT -- License material: `licenses/wasmer-sdk-MIT.txt`, + `34156a76319127aa4152e8b25bd92a5657f7ddc0669ba3e26209cc57053f56a1` +- Source: +- License: Wasmer Modified MIT License. It adds one condition to MIT: a commercial product or + service with more than 1 million monthly active users or more than 1 million US dollars in + monthly revenue must prominently display "Wasmer" in its user interface. +- Bundled JavaScript dependency: `acorn` 8.18.0 (MIT), shipped in the SDK's wasm-bindgen snippets +- License material: `licenses/wasmer-sdk-LICENSE.txt`, `licenses/wasmer-sdk-acorn-MIT.txt`, `licenses/wasmer-sdk-dependencies.html`, and `licenses/wasmer-sdk-dependencies.json` -WASM-OJ consumes the official npm artifact without patching the installed -package. The generated dependency report covers every package in the pinned -normal Cargo dependency graph selected for `wasm32-unknown-unknown`; its compact -inventory binds all 332 package identities and the exact HTML report digest. +WASM-OJ ships the official npm runtime files unbundled. Its only modification replaces the +wasm-bindgen glue's `new Function` construction of Wasmer's fixed host-function trampoline with +the equivalent closure so the SDK runs under a strict Content Security Policy. The browser build +omits the SDK's WISP networking module, the only path to its AGPL-3.0 `@mercuryworkshop/wisp-js` +dependency, which WASM-OJ therefore does not distribute. The generated dependency report covers +every package in the pinned normal Cargo dependency graph of the SDK's `wasmer-sdk-js` crate +selected for `wasm32-unknown-unknown`; its compact inventory binds all 316 package identities and +the exact HTML report digest. ### QuickJS guest Node standard I/O libraries diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b95f157..4f47846 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,8 +61,8 @@ importers: specifier: workspace:0.2.0 version: link:packages/toolchain-rust '@wasmer/sdk': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.19.0 + version: 0.19.0 buffer: specifier: 6.0.3 version: 6.0.3 @@ -81,7 +81,7 @@ importers: devDependencies: '@cloudflare/vite-plugin': specifier: 1.51.1 - version: 1.51.1(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(terser@5.49.0))(wrangler@4.120.0(@cloudflare/workers-types@5.20260808.1)) + version: 1.51.1(bufferutil@4.1.0)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(terser@5.49.0))(wrangler@4.120.0(@cloudflare/workers-types@5.20260808.1)(bufferutil@4.1.0)) '@cloudflare/workers-types': specifier: 5.20260808.1 version: 5.20260808.1 @@ -162,7 +162,7 @@ importers: version: 4.1.10(@types/node@24.13.3)(@vitest/coverage-v8@4.1.10)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(terser@5.49.0)) wrangler: specifier: 4.120.0 - version: 4.120.0(@cloudflare/workers-types@5.20260808.1) + version: 4.120.0(@cloudflare/workers-types@5.20260808.1)(bufferutil@4.1.0) packages/browser: dependencies: @@ -172,9 +172,6 @@ importers: '@wasm-oj/core': specifier: workspace:0.2.3 version: link:../core - '@wasmer/sdk': - specifier: 0.10.0 - version: 0.10.0 es-module-lexer: specifier: 2.3.1 version: 2.3.1 @@ -241,8 +238,8 @@ importers: specifier: workspace:0.2.3 version: link:../core '@wasmer/sdk': - specifier: 0.10.0 - version: 0.10.0 + specifier: 0.19.0 + version: 0.19.0 fflate: specifier: 0.8.3 version: 0.8.3 @@ -992,6 +989,10 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@mercuryworkshop/wisp-js@0.4.1': + resolution: {integrity: sha512-104LwiXiuhti/e32gmv0Da0u0yuLFDHX8JawCzleTPWJ5t5qTX4EYi4E7/ucbjBPN9wwVPWHE5g5yGqzl/NzQA==} + hasBin: true + '@napi-rs/keyring-darwin-arm64@1.3.0': resolution: {integrity: sha512-pl76hJvdYUBn6I24bXiOBMA9nbDapo3I5B+f3OorjDU4dUMSypXeKbOVehJe8fhgTiH24flMyTS3aAIy43xegQ==} engines: {node: '>= 10'} @@ -1846,8 +1847,9 @@ packages: '@vitest/utils@4.1.10': resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - '@wasmer/sdk@0.10.0': - resolution: {integrity: sha512-YQ+s5tGag6P/I8kp9BTH+XhjoS9UFvWiZJvnWEEovClHffhYToKhprWr4UJG7wLP7c/2HQpGkF7ZrjoUvKjdmA==} + '@wasmer/sdk@0.19.0': + resolution: {integrity: sha512-4gdWiIlne8ti3dQl1yD6jrLLNKrQXmZXA6iy6ta/6sgYpixPLwymeGqhMzjAIHqhmwrD+SjPFpZ5ugd9SytDrQ==} + engines: {node: '>=20'} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -2056,6 +2058,10 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bufferutil@4.1.0: + resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} + engines: {node: '>=6.14.2'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -2100,6 +2106,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -3057,6 +3067,10 @@ packages: resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} engines: {node: '>= 0.4'} + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + node-releases@2.0.51: resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} engines: {node: '>=18'} @@ -3691,9 +3705,6 @@ packages: web-vitals@4.2.4: resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} - web-worker@1.5.0: - resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} - webpack-sources@3.5.1: resolution: {integrity: sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==} engines: {node: '>=10.13.0'} @@ -3913,15 +3924,15 @@ snapshots: optionalDependencies: workerd: 1.20260801.1 - '@cloudflare/vite-plugin@1.51.1(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(terser@5.49.0))(wrangler@4.120.0(@cloudflare/workers-types@5.20260808.1))': + '@cloudflare/vite-plugin@1.51.1(bufferutil@4.1.0)(vite@8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(terser@5.49.0))(wrangler@4.120.0(@cloudflare/workers-types@5.20260808.1)(bufferutil@4.1.0))': dependencies: '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260801.1) - miniflare: 5.20260801.1-alpha + miniflare: 5.20260801.1-alpha(bufferutil@4.1.0) unenv: 2.0.0-rc.24 vite: 8.2.1(@types/node@24.13.3)(esbuild@0.28.1)(terser@5.49.0) workerd: 1.20260801.1 - wrangler: 4.120.0(@cloudflare/workers-types@5.20260808.1) - ws: 8.21.0 + wrangler: 4.120.0(@cloudflare/workers-types@5.20260808.1)(bufferutil@4.1.0) + ws: 8.21.0(bufferutil@4.1.0) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -4347,6 +4358,15 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@mercuryworkshop/wisp-js@0.4.1': + dependencies: + bufferutil: 4.1.0 + commander: 14.0.3 + ipaddr.js: 2.4.0 + ws: 8.21.0(bufferutil@4.1.0) + transitivePeerDependencies: + - utf-8-validate + '@napi-rs/keyring-darwin-arm64@1.3.0': optional: true @@ -4960,9 +4980,11 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@wasmer/sdk@0.10.0': + '@wasmer/sdk@0.19.0': dependencies: - web-worker: 1.5.0 + '@mercuryworkshop/wisp-js': 0.4.1 + transitivePeerDependencies: + - utf-8-validate '@webassemblyjs/ast@1.14.1': dependencies: @@ -5220,6 +5242,10 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bufferutil@4.1.0: + dependencies: + node-gyp-build: 4.8.4 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -5260,6 +5286,8 @@ snapshots: color-name@1.1.4: {} + commander@14.0.3: {} + commander@2.20.3: {} concat-map@0.0.1: {} @@ -6228,13 +6256,13 @@ snapshots: mime-db@1.54.0: {} - miniflare@5.20260801.1-alpha: + miniflare@5.20260801.1-alpha(bufferutil@4.1.0): dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.35.2 undici: 7.29.0 workerd: 1.20260801.1 - ws: 8.21.0 + ws: 8.21.0(bufferutil@4.1.0) youch: 4.1.0-beta.10 transitivePeerDependencies: - bufferutil @@ -6307,6 +6335,8 @@ snapshots: object.entries: 1.1.9 semver: 6.3.1 + node-gyp-build@4.8.4: {} + node-releases@2.0.51: {} object-assign@4.1.1: {} @@ -7101,8 +7131,6 @@ snapshots: web-vitals@4.2.4: {} - web-worker@1.5.0: {} - webpack-sources@3.5.1: {} webpack@5.108.4(esbuild@0.28.1): @@ -7203,13 +7231,13 @@ snapshots: '@cloudflare/workerd-linux-arm64': 1.20260801.1 '@cloudflare/workerd-windows-64': 1.20260801.1 - wrangler@4.120.0(@cloudflare/workers-types@5.20260808.1): + wrangler@4.120.0(@cloudflare/workers-types@5.20260808.1)(bufferutil@4.1.0): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260801.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 5.20260801.1-alpha + miniflare: 5.20260801.1-alpha(bufferutil@4.1.0) path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 workerd: 1.20260801.1 @@ -7220,7 +7248,9 @@ snapshots: - bufferutil - utf-8-validate - ws@8.21.0: {} + ws@8.21.0(bufferutil@4.1.0): + optionalDependencies: + bufferutil: 4.1.0 yallist@3.1.1: {} diff --git a/scripts/build-library.mjs b/scripts/build-library.mjs index fb656ce..6d4eac6 100644 --- a/scripts/build-library.mjs +++ b/scripts/build-library.mjs @@ -30,6 +30,7 @@ import { import { resolveTypeScriptCli } from "./typescript-cli.mjs"; import { buildQuickJsStdlib } from "./build-quickjs-stdlib.mjs"; +import { wasmerSdkRuntime } from "./wasmer-sdk-runtime.mjs"; const run = promisify(execFile); const alias = { "@": repositoryRoot }; @@ -96,7 +97,7 @@ async function buildSourcePackage(definition, stagingDir) { publicDir: false, plugins: definition.name === "@wasm-oj/contracts" ? [] : [contractsBoundaryPlugin()], resolve: { alias }, - worker: { format: "es" }, + worker: { format: "es", plugins: () => [wasmerSdkRuntime()] }, build: { outDir: stagingDir, emptyOutDir: false, diff --git a/scripts/build-site.ts b/scripts/build-site.ts index 0212831..2235aeb 100644 --- a/scripts/build-site.ts +++ b/scripts/build-site.ts @@ -125,9 +125,10 @@ async function removeVerifiedBrowserOnlyDuplicatesFromServer(): Promise { for (const directory of serverRoots) { const entries = await readdir(directory); const wasmNames = entries.filter((name) => name.endsWith(".wasm")); + const sdkRuntimeNames = entries.filter((name) => name.startsWith("wasmer-sdk-")); const monacoWorkerNames = entries.filter((name) => /^(?:css|editor|html|json|ts)\.worker-.*\.js$/.test(name)); for (const name of wasmNames) { - if (!/^(?:runtime-core_bg|wasmer_js_bg)-[A-Za-z0-9_-]+\.wasm$/.test(name)) { + if (!/^runtime-core_bg-[A-Za-z0-9_-]+\.wasm$/.test(name)) { throw new Error(`Sites build emitted unexpected server Wasm module '${name}'.`); } } @@ -143,6 +144,22 @@ async function removeVerifiedBrowserOnlyDuplicatesFromServer(): Promise { if (JSON.stringify(actualMonacoWorkers) !== JSON.stringify(expectedMonacoWorkers)) { throw new Error(`Sites build emitted an incomplete or duplicate Monaco Worker set in '${path.relative(root, directory)}'.`); } + for (const name of sdkRuntimeNames) { + if (!/^wasmer-sdk-[0-9a-f]{16}$/.test(name)) { + throw new Error(`Sites build emitted an invalid Wasmer SDK runtime directory '${name}'.`); + } + for (const relative of await recursiveFiles(path.join(directory, name))) { + const [serverBytes, clientBytes] = await Promise.all([ + readFile(path.join(directory, name, relative)), + readFile(path.join(clientAssets, name, relative)), + ]); + if (serverBytes.byteLength !== clientBytes.byteLength + || sha256(serverBytes) !== sha256(clientBytes)) { + throw new Error(`Server browser-only module '${name}/${relative}' is not identical to its browser-static copy.`); + } + } + await rm(path.join(directory, name), { recursive: true }); + } for (const name of [...wasmNames, ...monacoWorkerNames]) { const [serverBytes, clientBytes] = await Promise.all([ readFile(path.join(directory, name)), diff --git a/scripts/compact-wasmer-sdk-license-report.mjs b/scripts/compact-wasmer-sdk-license-report.mjs index b0b2761..3e0ce34 100644 --- a/scripts/compact-wasmer-sdk-license-report.mjs +++ b/scripts/compact-wasmer-sdk-license-report.mjs @@ -15,9 +15,9 @@ await publishCargoLicenseInventory({ inventoryPath, schema: "wasm-oj-v2/wasmer-sdk-licenses", graph: { - package: "@wasmer/sdk@0.10.0", - sourceRevision: "93b8b738ebd3ee57e118da0f0eb795b97d5b999e", - cargoLockSha256: "d352926f3f05e3d4308c4e261711d07db568e5c2b4387067180f920da074791f", + package: "@wasmer/sdk@0.19.0", + sourceRevision: "7e69332b7f65dbc5584d64bb79f547eaf4302b69", + cargoLockSha256: "34156a76319127aa4152e8b25bd92a5657f7ddc0669ba3e26209cc57053f56a1", target: "wasm32-unknown-unknown", defaultFeatures: true, features: [], diff --git a/scripts/generate-third-party-components.mjs b/scripts/generate-third-party-components.mjs index ba9aad8..13085b6 100644 --- a/scripts/generate-third-party-components.mjs +++ b/scripts/generate-third-party-components.mjs @@ -399,18 +399,19 @@ const definitions = [ { id: "wasmer-sdk", name: "Wasmer JavaScript SDK and locked Rust dependency closure", - version: "0.10.0", + version: "0.19.0", source: { - url: "https://github.com/wasmerio/wasmer-js", - revision: "93b8b738ebd3ee57e118da0f0eb795b97d5b999e", + url: "https://github.com/wasmerio/wasmer-sdk", + revision: "7e69332b7f65dbc5584d64bb79f547eaf4302b69", }, files: [ "licenses/wasmer-sdk-dependencies.html", "licenses/wasmer-sdk-dependencies.json", ], - npm: "@wasmer/sdk@0.10.0", + npm: "@wasmer/sdk@0.19.0", licenses: [ - "licenses/wasmer-sdk-MIT.txt", + "licenses/wasmer-sdk-LICENSE.txt", + "licenses/wasmer-sdk-acorn-MIT.txt", "licenses/wasmer-sdk-dependencies.html", "licenses/wasmer-sdk-dependencies.json", ], diff --git a/scripts/generate-wasmer-sdk-licenses.sh b/scripts/generate-wasmer-sdk-licenses.sh index 860ee68..3b683f8 100755 --- a/scripts/generate-wasmer-sdk-licenses.sh +++ b/scripts/generate-wasmer-sdk-licenses.sh @@ -4,10 +4,10 @@ set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" CARGO_ABOUT="${CARGO_ABOUT:-cargo-about}" EXPECTED_ABOUT_VERSION="cargo-about 0.9.1" -RUST_TOOLCHAIN="1.91.1" -SOURCE_REVISION="93b8b738ebd3ee57e118da0f0eb795b97d5b999e" -SOURCE_REPOSITORY="https://github.com/wasmerio/wasmer-js.git" -EXPECTED_LOCK_SHA256="d352926f3f05e3d4308c4e261711d07db568e5c2b4387067180f920da074791f" +RUST_TOOLCHAIN="1.97.1" +SOURCE_REVISION="7e69332b7f65dbc5584d64bb79f547eaf4302b69" +SOURCE_REPOSITORY="https://github.com/wasmerio/wasmer-sdk.git" +EXPECTED_LOCK_SHA256="34156a76319127aa4152e8b25bd92a5657f7ddc0669ba3e26209cc57053f56a1" if ! command -v "$CARGO_ABOUT" >/dev/null 2>&1; then echo "cargo-about 0.9.1 is required. Install it with:" >&2 @@ -39,7 +39,7 @@ RUSTUP_TOOLCHAIN="$RUST_TOOLCHAIN" cargo fetch \ COMMON=( generate --config "$ROOT/scripts/wasmer-sdk-about.toml" - --manifest-path "$WORK/source/Cargo.toml" + --manifest-path "$WORK/source/js/bindgen/Cargo.toml" --locked --offline --target wasm32-unknown-unknown diff --git a/scripts/inspect-python-runtime-files.mjs b/scripts/inspect-python-runtime-files.mjs index 956ba3a..49847b9 100644 --- a/scripts/inspect-python-runtime-files.mjs +++ b/scripts/inspect-python-runtime-files.mjs @@ -2,7 +2,7 @@ import { createHash } from "node:crypto"; import { readFile } from "node:fs/promises"; import path from "node:path"; import { gunzipSync } from "node:zlib"; -import { init, Runtime, Wasmer } from "@wasmer/sdk/node"; +import { Wasmer } from "@wasmer/sdk/node"; import { PYTHON_RUNTIME_FILES_EXPORT_SCRIPT, decodeRuntimeFiles, @@ -13,17 +13,15 @@ if (process.argv.length !== 3) { } const packagePath = path.resolve(process.argv[2]); -let runtime; let exitCode = 0; try { - await init({ log: "warn" }); - runtime = new Runtime({ registry: null }); const packageBytes = gunzipSync(await readFile(packagePath)); - const pkg = await Wasmer.fromFile(new Uint8Array(packageBytes), runtime); - const command = pkg.commands.python; - if (!command) throw new Error(`Python package '${packagePath}' does not expose python.`); - const output = await runPython(command, `${String.raw` + const wasmer = new Wasmer({ cache: false, outputBytes: 256 * 1024 * 1024 }); + const pkg = await wasmer.packages.load(new Uint8Array(packageBytes)); + if (!pkg.commands.includes("python")) throw new Error(`Python package '${packagePath}' does not expose python.`); + const sandbox = await wasmer.sandboxes.create({ packages: [pkg] }); + const output = await runPython(sandbox, `${String.raw` import hashlib import importlib.util import json @@ -42,7 +40,7 @@ sys.stderr.write("WASM_OJ_SMOKE:" + json.dumps({ }, sort_keys=True, separators=(",", ":")) + "\n") `} ${PYTHON_RUNTIME_FILES_EXPORT_SCRIPT}`); - const smokeLine = output.stderr.split("\n").find((line) => line.startsWith("WASM_OJ_SMOKE:")); + const smokeLine = output.stderr.text().split("\n").find((line) => line.startsWith("WASM_OJ_SMOKE:")); if (!smokeLine) throw new Error(`Python runtime smoke emitted no result: ${output.stderr}`); const smoke = JSON.parse(smokeLine.slice("WASM_OJ_SMOKE:".length)); const expectedSmoke = { @@ -58,7 +56,7 @@ ${PYTHON_RUNTIME_FILES_EXPORT_SCRIPT}`); if (JSON.stringify(smoke) !== JSON.stringify(expectedSmoke)) { throw new Error(`Python runtime smoke mismatch: ${JSON.stringify(smoke)}.`); } - const archive = output.stdoutBytes.slice(); + const archive = output.stdout.bytes; const files = decodeRuntimeFiles(archive); process.stdout.write(`WASM_OJ_PYTHON_INSPECTION:${JSON.stringify({ archiveSha256: createHash("sha256").update(archive).digest("hex"), @@ -73,32 +71,24 @@ ${PYTHON_RUNTIME_FILES_EXPORT_SCRIPT}`); exitCode = 1; } finally { setTimeout(() => process.exit(exitCode), 50); - try { - runtime?.free(); - } catch (error) { - process.stderr.write(`Unable to release the Python smoke runtime: ${String(error)}\n`); - exitCode = 1; - } } -async function runPython(command, script) { - const instance = await command.run({ - args: ["-c", script], - env: { - PYTHONHOME: "/usr/local", - PYTHONHASHSEED: "0", - PYTHONDONTWRITEBYTECODE: "1", - }, - }); +async function runPython(sandbox, script) { const keepAlive = setInterval(() => {}, 1_000); let output; try { - output = await instance.wait(); + output = await sandbox.command("python", ["-c", script], { + env: { + PYTHONHOME: "/usr/local", + PYTHONHASHSEED: "0", + PYTHONDONTWRITEBYTECODE: "1", + }, + }).run({ check: false }); } finally { clearInterval(keepAlive); } if (!output.ok) { - throw new Error(`Python command failed with exit ${output.code}: ${output.stderr}`); + throw new Error(`Python command failed with ${output.reason} ${output.exitCode}: ${output.stderr.text()}`); } return output; } diff --git a/scripts/library-packages.mjs b/scripts/library-packages.mjs index 78bdcfd..73347c7 100644 --- a/scripts/library-packages.mjs +++ b/scripts/library-packages.mjs @@ -30,7 +30,8 @@ export const CODE_PACKAGES = Object.freeze([ "quickjs-node-stdlib-LICENSES.txt", "es-module-lexer-MIT.txt", "fflate-MIT.txt", - "wasmer-sdk-MIT.txt", + "wasmer-sdk-LICENSE.txt", + "wasmer-sdk-acorn-MIT.txt", "wasmer-sdk-dependencies.html", "wasmer-sdk-dependencies.json", "runtime-core-dependencies.html", @@ -39,7 +40,6 @@ export const CODE_PACKAGES = Object.freeze([ runtimeDependencies: [ "@wasm-oj/contracts", "@wasm-oj/core", - "@wasmer/sdk", "es-module-lexer", "fflate", ], @@ -49,7 +49,7 @@ export const CODE_PACKAGES = Object.freeze([ licenses: [ "quickjs-node-stdlib-LICENSES.txt", "fflate-MIT.txt", - "wasmer-sdk-MIT.txt", + "wasmer-sdk-LICENSE.txt", "wasmer-sdk-dependencies.html", "wasmer-sdk-dependencies.json", "runtime-core-dependencies.html", diff --git a/scripts/verify-wasmer-sdk-init.mjs b/scripts/verify-wasmer-sdk-init.mjs index da6540e..b1f86cc 100644 --- a/scripts/verify-wasmer-sdk-init.mjs +++ b/scripts/verify-wasmer-sdk-init.mjs @@ -3,23 +3,26 @@ import { readFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { readPnpmLock, requireLockedPackage } from "./pnpm-lock.mjs"; +import { wasmerSdkRuntimeFiles } from "./wasmer-sdk-runtime.mjs"; -const EXPECTED_VERSION = "0.10.0"; -const EXPECTED_INTEGRITY = "sha512-YQ+s5tGag6P/I8kp9BTH+XhjoS9UFvWiZJvnWEEovClHffhYToKhprWr4UJG7wLP7c/2HQpGkF7ZrjoUvKjdmA=="; +const EXPECTED_VERSION = "0.19.0"; +const EXPECTED_INTEGRITY = "sha512-4gdWiIlne8ti3dQl1yD6jrLLNKrQXmZXA6iy6ta/6sgYpixPLwymeGqhMzjAIHqhmwrD+SjPFpZ5ugd9SytDrQ=="; const EXPECTED_FILES = Object.freeze({ - "dist/index.mjs": "d5e0424d9de8173c0c7bc6a6b704aecde620d3f424050e0e6a079d863a44d58b", - "dist/node.mjs": "6896c497347069f69432241648d5c64b4d265d689504a48e9804022139ac0dda", - "dist/wasmer_js_bg.wasm": "49a6646209f5ab5e7c737eac33407d87d9a9959ac83e5ecaaab9261b2323589e", - "package.json": "5c207c6ff1fc02bd633a13461a77d7f8fe47d14494cf2192763226f39cab373b", + "dist/browser-worker.js": "775223c87ff60c22515e88945e1152c02f7bfa49898edab748e89e573ec4d439", + "dist/index.js": "8a4753fcc7bda778da38a9f3e8ecfaae52497e0756f8a1f06da9f87a2ecf2c11", + "dist/node.js": "0a917fce4d5b22e8d39d746e9c2ae0a37715802d712e6f0f67486919b237412c", + "pkg/wasmer_sdk_js.js": "b8f330f47fcbc48541af65985b2db63116f220231a3225e9ec347c5f74dcfa35", + "pkg/wasmer_sdk_js_bg.wasm": "86aefc8940d29045f94646421bec6fac528a736aa4e78718fdb7924897ee4c25", + "package.json": "19570be326d74717571202df1a0b8998d991e07e5bafb77655eca78bdc99cf24", }); -const EXPECTED_SOURCE_REVISION = "93b8b738ebd3ee57e118da0f0eb795b97d5b999e"; -const EXPECTED_CARGO_LOCK_SHA256 = "d352926f3f05e3d4308c4e261711d07db568e5c2b4387067180f920da074791f"; +const EXPECTED_SOURCE_REVISION = "7e69332b7f65dbc5584d64bb79f547eaf4302b69"; +const EXPECTED_CARGO_LOCK_SHA256 = "34156a76319127aa4152e8b25bd92a5657f7ddc0669ba3e26209cc57053f56a1"; const root = path.resolve(fileURLToPath(new URL("..", import.meta.url))); const rootPackage = JSON.parse(await readFile(path.join(root, "package.json"), "utf8")); const lock = await readPnpmLock(root); -const sdkPackagePath = fileURLToPath(import.meta.resolve("@wasmer/sdk/package.json")); -const sdkRoot = path.dirname(sdkPackagePath); +const sdkRoot = path.dirname(path.dirname(fileURLToPath(import.meta.resolve("@wasmer/sdk/browser")))); +const sdkPackagePath = path.join(sdkRoot, "package.json"); const sdkPackage = JSON.parse(await readFile(sdkPackagePath, "utf8")); const licenseInventoryPath = path.join(root, "licenses/wasmer-sdk-dependencies.json"); const licenseReportPath = path.join(root, "licenses/wasmer-sdk-dependencies.html"); @@ -100,25 +103,19 @@ if (packageIdentities.size < 300 || licenseReportBytes.byteLength < 100_000) { throw new Error("The Wasmer SDK dependency license closure is unexpectedly incomplete."); } -const { Runtime, init } = await import("@wasmer/sdk/node"); -const browserSdk = await import("@wasmer/sdk"); -if (typeof browserSdk.ThreadPoolWorker !== "function") { - throw new Error("The official Wasmer SDK does not expose its required ThreadPoolWorker primitive."); +const runtimeFiles = await wasmerSdkRuntimeFiles(); +for (const relative of ["dist/index.js", "dist/browser-worker.js", "pkg/wasmer_sdk_js.js", "pkg/wasmer_sdk_js_bg.wasm"]) { + if (!runtimeFiles.has(relative)) throw new Error(`The shipped Wasmer SDK runtime omits '${relative}'.`); } -// SDK 0.10.0 internally invokes its wasm-bindgen loader through the deprecated -// positional form and therefore emits an upstream warning. Keep -// it visible: the verifier proves functionality without mutating or masking -// the official package. -await init({ log: "error" }); -const runtime = new Runtime({ registry: null }); -try { - if (!(runtime instanceof Runtime) || runtime.__getClassname() !== "JsRuntime") { - throw new Error("The official Wasmer SDK did not construct a Runtime instance."); - } -} finally { - runtime.free(); +if ([...runtimeFiles.keys()].some((relative) => relative.includes("wisp"))) { + throw new Error("The shipped Wasmer SDK runtime must not include the WISP networking module."); } +const { Wasmer } = await import("@wasmer/sdk/node"); +const wasmer = await new Wasmer({ cache: false }).ready(); +if (!(wasmer instanceof Wasmer)) throw new Error("The official Wasmer SDK did not construct a client."); + process.stdout.write( - `Verified official @wasmer/sdk ${EXPECTED_VERSION} integrity and Runtime initialization.\n`, + `Verified official @wasmer/sdk ${EXPECTED_VERSION} integrity, shipped runtime files, and client initialization.\n`, ); +process.exit(0); diff --git a/scripts/wasmer-sdk-licenses.hbs b/scripts/wasmer-sdk-licenses.hbs index 593d811..cb46851 100644 --- a/scripts/wasmer-sdk-licenses.hbs +++ b/scripts/wasmer-sdk-licenses.hbs @@ -15,7 +15,7 @@

    Wasmer JavaScript SDK dependency licenses

    Generated from the locked normal dependency graph for the exact source - revision used by @wasmer/sdk 0.10.0, targeting + revision used by @wasmer/sdk 0.19.0, targeting wasm32-unknown-unknown.

    {{#each licenses}} diff --git a/scripts/wasmer-sdk-runtime.d.mts b/scripts/wasmer-sdk-runtime.d.mts new file mode 100644 index 0000000..b97509f --- /dev/null +++ b/scripts/wasmer-sdk-runtime.d.mts @@ -0,0 +1,5 @@ +import type { Plugin } from "vite"; + +export declare const WASMER_SDK_VIRTUAL_MODULE: "virtual:wasm-oj/wasmer-sdk"; +export declare function wasmerSdkRuntimeFiles(): Promise>; +export declare function wasmerSdkRuntime(): Plugin; diff --git a/scripts/wasmer-sdk-runtime.mjs b/scripts/wasmer-sdk-runtime.mjs new file mode 100644 index 0000000..f5f284e --- /dev/null +++ b/scripts/wasmer-sdk-runtime.mjs @@ -0,0 +1,108 @@ +import { createHash } from "node:crypto"; +import { realpathSync } from "node:fs"; +import { readdir, readFile } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { init, parse } from "es-module-lexer"; + +export const WASMER_SDK_VIRTUAL_MODULE = "virtual:wasm-oj/wasmer-sdk"; +const RESOLVED_VIRTUAL_MODULE = `\0${WASMER_SDK_VIRTUAL_MODULE}`; +const SDK_ROOT = path.dirname(path.dirname(realpathSync(fileURLToPath(import.meta.resolve("@wasmer/sdk/browser"))))); +const ENTRY = "dist/index.js"; +const BINDING = "pkg/wasmer_sdk_js.js"; +const ROOTS = [ENTRY, "dist/browser-worker.js", "pkg/wasmer_sdk_js_bg.wasm"]; +// Only the browser WISP networking mode loads this module, and it is the SDK's +// sole path to the AGPL-3.0 `@mercuryworkshop/wisp-js` package. WASM-OJ +// sandboxes run with networking disabled, so the module is not shipped. +const EXCLUDED = new Set(["dist/wisp-network.js"]); +const DYNAMIC_FUNCTION = "new Function(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3))"; +// Wasmer's web backend builds every dynamic host-function trampoline from this +// fixed source through `new Function`, which a strict CSP rejects as eval. +const CSP_SAFE_FUNCTION = ` +function __wasmOjFunction(parameters, body) { + if (parameters === "f" && body === "return f(Array.prototype.slice.call(arguments, 1))") { + return function (f) { return f(Array.prototype.slice.call(arguments, 1)); }; + } + return new Function(parameters, body); +} +`; + +/** + * The SDK's browser entry, its Worker entry and their relative module graph, + * keyed by package-relative path. These files load each other through + * `import.meta.url`, so they are shipped unbundled with their layout intact. + */ +export async function wasmerSdkRuntimeFiles() { + await init; + const files = new Map(); + const visit = async (relative) => { + if (files.has(relative) || EXCLUDED.has(relative)) return; + const source = cspSafeRuntimeSource(relative, await readFile(path.join(SDK_ROOT, relative))); + files.set(relative, source); + if (!relative.endsWith(".js") && !relative.endsWith(".mjs")) return; + const directory = path.posix.dirname(relative); + for (const { n: specifier } of parse(source.toString("utf8"), relative)[0]) { + if (specifier === undefined) continue; + if (!specifier.startsWith("./") && !specifier.startsWith("../")) { + throw new Error(`@wasmer/sdk runtime module '${relative}' imports bare specifier '${specifier}'.`); + } + await visit(path.posix.normalize(path.posix.join(directory, specifier))); + } + if (relative.startsWith("pkg/snippets/")) { + for (const name of await readdir(path.join(SDK_ROOT, directory))) { + if (name.endsWith(".LICENSE")) files.set(path.posix.join(directory, name), await readFile(path.join(SDK_ROOT, directory, name))); + } + } + }; + for (const root of ROOTS) await visit(root); + return new Map([...files].sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))); +} + +function cspSafeRuntimeSource(relative, source) { + if (relative !== BINDING) return source; + const text = source.toString("utf8"); + if (text.split(DYNAMIC_FUNCTION).length !== 2) { + throw new Error(`@wasmer/sdk '${BINDING}' no longer contains exactly one dynamic Function constructor.`); + } + return Buffer.from(`${text.replace(DYNAMIC_FUNCTION, "__wasmOjFunction(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3))")}${CSP_SAFE_FUNCTION}`); +} + +/** + * Ships the official SDK runtime files as content-addressed assets and exposes + * their URLs through `virtual:wasm-oj/wasmer-sdk`. Browser Workers import the + * SDK from those URLs instead of bundling it. + */ +export function wasmerSdkRuntime() { + let command = "build"; + let assetsDir = "assets"; + return { + name: "wasm-oj-wasmer-sdk-runtime", + config: () => ({ optimizeDeps: { exclude: ["@wasmer/sdk"] } }), + configResolved(config) { + command = config.command; + assetsDir = config.build.assetsDir; + }, + resolveId: (id) => (id === WASMER_SDK_VIRTUAL_MODULE ? RESOLVED_VIRTUAL_MODULE : null), + transform(code, id) { + if (command !== "serve" || id.split("?", 1)[0] !== path.join(SDK_ROOT, BINDING)) return null; + return { code: cspSafeRuntimeSource(BINDING, Buffer.from(code)).toString("utf8"), map: null }; + }, + async load(id) { + if (id !== RESOLVED_VIRTUAL_MODULE) return null; + if (command === "serve") { + const url = (relative) => JSON.stringify(`/@fs${path.join(SDK_ROOT, relative).split(path.sep).join("/")}`); + return `export const wasmerSdkEntryUrl = ${url(ENTRY)};\nexport const wasmerSdkBindingUrl = ${url(BINDING)};\n`; + } + const files = await wasmerSdkRuntimeFiles(); + const hash = createHash("sha256"); + for (const [relative, source] of files) hash.update(relative).update("\0").update(source).update("\0"); + const directory = path.posix.join(assetsDir, `wasmer-sdk-${hash.digest("hex").slice(0, 16)}`); + const references = new Map(); + for (const [relative, source] of files) { + references.set(relative, this.emitFile({ type: "asset", fileName: `${directory}/${relative}`, source })); + } + return `export const wasmerSdkEntryUrl = import.meta.ROLLUP_FILE_URL_${references.get(ENTRY)};\n` + + `export const wasmerSdkBindingUrl = import.meta.ROLLUP_FILE_URL_${references.get(BINDING)};\n`; + }, + }; +} diff --git a/src/compiler/clang-object-cache.test.ts b/src/compiler/clang-object-cache.test.ts index 55a5537..7a0da77 100644 --- a/src/compiler/clang-object-cache.test.ts +++ b/src/compiler/clang-object-cache.test.ts @@ -25,7 +25,7 @@ describe("ClangObjectCache", () => { const key = await cache.unitManifestKey(pins, "c-release", "src/main.c", files.get("src/main.c")!); await expect(cache.store( key, - ["src/main.c", "/project/src/value.h", "/sysroot/include/stdio.h"], + ["src/main.c", "/workspace/src/value.h", "/sysroot/include/stdio.h"], files, new Uint8Array([1, 2, 3]), )).resolves.toBe(true); diff --git a/src/compiler/clang-object-cache.ts b/src/compiler/clang-object-cache.ts index 7537a89..1f73ed3 100644 --- a/src/compiler/clang-object-cache.ts +++ b/src/compiler/clang-object-cache.ts @@ -129,9 +129,7 @@ function normalizeProjectDependencies( const normalized = new Set(); for (const raw of dependencyPaths) { if (raw.startsWith("/usr/") || raw.startsWith("/sysroot/") || raw.startsWith("/lib/")) continue; - const path = raw.startsWith("/project/") - ? raw.slice("/project/".length) - : raw.replace(/^\.\//, ""); + const path = raw.replace(/^(?:\/workspace\/|\/project\/|\.\/)/, ""); if (!path || path.startsWith("/") || path.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) { return undefined; } diff --git a/src/compiler/dependency-input.ts b/src/compiler/dependency-input.ts index bb4458b..03c8998 100644 --- a/src/compiler/dependency-input.ts +++ b/src/compiler/dependency-input.ts @@ -161,7 +161,7 @@ export function rustDependencyInput(project: Project): RustDependencyInput { if (!/^(?:2015|2018|2021|2024)$/.test(edition)) { throw new Error(`Cargo dependency '${item.package.id}' has unsupported Rust edition '${edition}'.`); } - const outputPath = `/work/build/deps/lib${String(index).padStart(4, "0")}_${crateName}.rlib`; + const outputPath = `/workspace/build/deps/lib${String(index).padStart(4, "0")}_${crateName}.rlib`; descriptorById.set(item.package.id, { id: item.package.id, crateName, diff --git a/src/compiler/rust-linker.test.ts b/src/compiler/rust-linker.test.ts index a8f0b61..929ab37 100644 --- a/src/compiler/rust-linker.test.ts +++ b/src/compiler/rust-linker.test.ts @@ -34,18 +34,18 @@ function contract(overrides: Record = {}) { describe("Rust linker argument contract", () => { it("validates and instantiates the pinned optimization-specific templates", () => { const decoded = decodeRustLinkerArgumentContract(contract()); - expect(instantiateRustLinkerArguments(decoded, "debug", "/work/build/main.allocator.rcgu.bc")).toEqual([ + expect(instantiateRustLinkerArguments(decoded, "debug", "/workspace/build/main.allocator.rcgu.bc")).toEqual([ "--shared-memory", "/rust/lib/rustlib/wasm32-wasip1-threads/lib/self-contained/crt1-command.o", RUST_OBJECT_PATH, - "/work/build/main.allocator.rcgu.bc", + "/workspace/build/main.allocator.rcgu.bc", "/rust/lib/rustlib/wasm32-wasip1-threads/lib/libstd-example.rlib", "-o", RUST_FINAL_OUTPUT_PATH, "--gc-sections", "-O0", ]); - expect(instantiateRustLinkerArguments(decoded, "release", "/work/build/main.allocator.rcgu.bc")).toContain("-O2"); + expect(instantiateRustLinkerArguments(decoded, "release", "/workspace/build/main.allocator.rcgu.bc")).toContain("-O2"); }); it.each([ diff --git a/src/compiler/rust-linker.ts b/src/compiler/rust-linker.ts index 82bbd0a..c1b1732 100644 --- a/src/compiler/rust-linker.ts +++ b/src/compiler/rust-linker.ts @@ -1,8 +1,8 @@ import type { OptimizationLevel } from "../core/types.ts"; export const RUST_LINKER_COMMAND = "wasm-ld"; -export const RUST_OBJECT_PATH = "/work/build/main.o"; -export const RUST_FINAL_OUTPUT_PATH = "/work/build/main.wasm"; +export const RUST_OBJECT_PATH = "/workspace/build/main.o"; +export const RUST_FINAL_OUTPUT_PATH = "/workspace/build/main.wasm"; export const RUST_OBJECT_PLACEHOLDER = "__WASM_OJ_RUST_OBJECT__"; export const RUST_ALLOCATOR_PLACEHOLDER = "__WASM_OJ_RUST_ALLOCATOR_BITCODE__"; export const RUST_OUTPUT_PLACEHOLDER = "__WASM_OJ_RUST_OUTPUT__"; @@ -43,7 +43,7 @@ export function instantiateRustLinkerArguments( optimization: OptimizationLevel, allocatorBitcodePath: string, ): string[] { - if (!/^\/work\/build\/main\.[a-z0-9]+\.rcgu\.bc$/i.test(allocatorBitcodePath)) { + if (!/^\/workspace\/build\/main\.[a-z0-9]+\.rcgu\.bc$/i.test(allocatorBitcodePath)) { throw new Error(`Rust allocator bitcode has unexpected path '${allocatorBitcodePath}'.`); } const template = optimization === "release" ? contract.release : contract.debug; diff --git a/src/compiler/rust-sandbox.ts b/src/compiler/rust-sandbox.ts new file mode 100644 index 0000000..7ea4deb --- /dev/null +++ b/src/compiler/rust-sandbox.ts @@ -0,0 +1,82 @@ +import type { Sandbox } from "@wasmer/sdk"; +import { isLlvmBitcode, selectRustAllocatorBitcodeName } from "./rust-allocator-bitcode.ts"; +import { RUST_COMPILE_TIMEOUT_MS } from "./rust-toolchain.ts"; +import { runSandboxCommand, type SandboxCommandState } from "./sandbox-command.ts"; + +export const RUST_GUEST_ROOT = "/workspace"; +const decoder = new TextDecoder(); + +export interface RustStageOptions { + command: string; + args: string[]; + env: Record; + outputPath: string; + stage: string; + requiresAllocatorBitcode?: boolean; + outputValidator?: (bytes: Uint8Array) => boolean; +} + +export interface RustStageObservation { + success: boolean; + bytes?: Uint8Array; + allocatorBitcodePath?: string; + stdout: string; + stderr: string; +} + +/** Run one rustc or wasm-ld process to exit and read its output. */ +export async function runRustStage( + sandbox: Sandbox, + state: SandboxCommandState, + options: RustStageOptions, +): Promise { + const validator = options.outputValidator ?? ((bytes: Uint8Array) => WebAssembly.validate(new Uint8Array(bytes))); + const output = await runSandboxCommand(sandbox, state, { + command: options.command, + args: options.args, + cwd: RUST_GUEST_ROOT, + env: options.env, + timeoutMs: RUST_COMPILE_TIMEOUT_MS, + timeoutMessage: `${options.stage} exceeded ${RUST_COMPILE_TIMEOUT_MS} ms.`, + }); + const stdout = decoder.decode(output.stdout); + const stderr = decoder.decode(output.stderr); + if (!output.ok) return { success: false, stdout, stderr }; + const bytes = await readValidOutput(sandbox, options.outputPath, validator); + const allocator = options.requiresAllocatorBitcode ? await readRustAllocatorBitcode(sandbox) : undefined; + if (!bytes || (options.requiresAllocatorBitcode && !allocator)) { + return { success: false, stdout, stderr: stderr || `${options.stage} exited without its complete output.\n` }; + } + return { success: true, bytes, allocatorBitcodePath: allocator?.path, stdout, stderr }; +} + +export function isRustArchive(bytes: Uint8Array): boolean { + return bytes.byteLength > 8 && new TextDecoder().decode(bytes.subarray(0, 8)) === "!\n"; +} + +async function readValidOutput( + sandbox: Sandbox, + guestPath: string, + validator: (bytes: Uint8Array) => boolean, +): Promise { + try { + const bytes = await sandbox.fs.readFile(guestPath); + return bytes.byteLength > 8 && validator(bytes) ? bytes : undefined; + } catch { + return undefined; + } +} + +async function readRustAllocatorBitcode(sandbox: Sandbox): Promise<{ path: string; bytes: Uint8Array } | undefined> { + const directory = `${RUST_GUEST_ROOT}/build`; + let name: string | undefined; + try { + name = selectRustAllocatorBitcodeName((await sandbox.fs.readDir(directory)).map((entry) => entry.name)); + } catch (error) { + if (error instanceof Error && error.message.startsWith("rustc emitted multiple")) throw error; + return undefined; + } + if (!name) return undefined; + const bytes = await sandbox.fs.readFile(`${directory}/${name}`).catch(() => undefined); + return bytes && isLlvmBitcode(bytes) ? { path: `${directory}/${name}`, bytes } : undefined; +} diff --git a/src/compiler/rust-toolchain.ts b/src/compiler/rust-toolchain.ts index 95be781..f03b791 100644 --- a/src/compiler/rust-toolchain.ts +++ b/src/compiler/rust-toolchain.ts @@ -75,13 +75,13 @@ export function rustcObjectArguments( "-C", "llvm-args=-rng-seed=1", "-C", "metadata=wasm_oj_submission", "-C", "target-feature=+atomics,+bulk-memory,+mutable-globals", - `/work/${entry}`, + `/workspace/${entry}`, "--crate-name", "wasm_oj_submission", "--crate-type", "bin", "--edition", RUST_EDITION, "--error-format=json", "--json=diagnostic-rendered-ansi", - "--remap-path-prefix=/work=.", + "--remap-path-prefix=/workspace=.", "-C", "panic=abort", ...rootExterns.flatMap((item) => ["--extern", `${item.crateName}=${item.path}`]), ...optimizationArguments, @@ -109,13 +109,13 @@ export function rustcDependencyArguments( "-C", "llvm-args=-rng-seed=1", "-C", `metadata=${dependency.id}`, "-C", "target-feature=+atomics,+bulk-memory,+mutable-globals", - `/work/${dependency.root}`, + `/workspace/${dependency.root}`, "--crate-name", dependency.crateName, "--crate-type", "rlib", "--edition", dependency.edition, "--error-format=json", "--json=diagnostic-rendered-ansi", - "--remap-path-prefix=/work=.", + "--remap-path-prefix=/workspace=.", "-C", "panic=abort", "--cap-lints", "allow", ...dependency.features.flatMap((feature) => ["--cfg", `feature=${JSON.stringify(feature)}`]), diff --git a/src/compiler/sandbox-command.ts b/src/compiler/sandbox-command.ts new file mode 100644 index 0000000..80b144f --- /dev/null +++ b/src/compiler/sandbox-command.ts @@ -0,0 +1,86 @@ +import type { CommandSelector, Sandbox } from "@wasmer/sdk"; + +export interface SandboxCommandState { + /** Set when a process outlived its budget; its sandbox must not be closed. */ + abandoned: boolean; +} + +export interface SandboxCommandRequest { + command: CommandSelector; + args: readonly string[]; + cwd?: string; + env?: Record; + stdin?: string; + timeoutMs?: number; + timeoutMessage?: string; +} + +export interface SandboxCommandOutput { + /** The process exited on its own with status zero. */ + ok: boolean; + stdout: Uint8Array; + stderr: Uint8Array; +} + +/** + * Run one sandbox process to exit with piped output. A guest trap (rustc exits + * through `unreachable` after reporting errors) rejects the SDK's `wait()`, but + * its streams still end, so the diagnostics survive as an unsuccessful result. + * + * A process that exceeds `timeoutMs` is abandoned rather than killed: the caller + * discards the whole Worker or child process, which avoids SDK termination of a + * thread that may hold the shared allocator lock. + */ +export async function runSandboxCommand( + sandbox: Sandbox, + state: SandboxCommandState, + request: SandboxCommandRequest, +): Promise { + const child = await sandbox.command(request.command, [...request.args], { + ...(request.cwd === undefined ? {} : { cwd: request.cwd }), + ...(request.env === undefined ? {} : { env: request.env }), + }).spawn({ stdin: request.stdin === undefined ? "closed" : "pipe", stdout: "pipe", stderr: "pipe" }); + const completion = Promise.all([ + child.stdin && request.stdin !== undefined + ? child.stdin.write(request.stdin).then(() => child.stdin!.close()) + : undefined, + readAll(child.stdout), + readAll(child.stderr), + child.wait({ check: false }).then((output) => output.ok, () => false), + ]); + void completion.catch(() => undefined); + let timer: ReturnType | undefined; + try { + const [, stdout, stderr, ok] = request.timeoutMs === undefined + ? await completion + : await Promise.race([ + completion, + new Promise((_resolve, reject) => { + timer = setTimeout(() => { + state.abandoned = true; + reject(new Error(request.timeoutMessage ?? `The sandbox process exceeded ${request.timeoutMs} ms.`)); + }, request.timeoutMs); + }), + ]); + return { ok, stdout, stderr }; + } finally { + clearTimeout(timer); + } +} + +async function readAll(stream: AsyncIterable | null): Promise { + if (!stream) return new Uint8Array(); + const chunks: Uint8Array[] = []; + let length = 0; + for await (const chunk of stream) { + chunks.push(chunk); + length += chunk.byteLength; + } + const output = new Uint8Array(length); + let offset = 0; + for (const chunk of chunks) { + output.set(chunk, offset); + offset += chunk.byteLength; + } + return output; +} diff --git a/src/compiler/sdk-direct-clang.ts b/src/compiler/sdk-direct-clang.ts index 9ae7199..1d81571 100644 --- a/src/compiler/sdk-direct-clang.ts +++ b/src/compiler/sdk-direct-clang.ts @@ -1,9 +1,4 @@ -import { - Directory, - Runtime, - Wasmer, - type Command, -} from "@wasmer/sdk"; +import type { Package, Sandbox, Wasmer } from "@wasmer/sdk"; import { WASM_OJ_CONTRACT_VERSION } from "../core/contract.ts"; import { decodeClangPins, @@ -44,11 +39,11 @@ import { DETERMINISTIC_NATIVE_RUNTIME, DETERMINISTIC_NATIVE_SOURCE_PATH, } from "../runtime/determinism.ts"; -import { MountedOutputStabilityObserver } from "../runtime/mounted-output-stability.ts"; import { cppDependencyInput } from "./dependency-input.ts"; +import { runSandboxCommand } from "./sandbox-command.ts"; export interface SdkDirectClangHost { - runtime: Runtime; + wasmer: Wasmer; loadToolchainAsset(path: string): Promise; loadToolchainFile(path: string): Promise; progress(requestId: string, phase: WorkerPhase, label: string, value?: number): void; @@ -56,19 +51,26 @@ export interface SdkDirectClangHost { } interface LoadedToolchain { - pkg: Wasmer; + pkg: Package; pins: ClangPins; - compiler: Command; - linker: Command; } -let loadedToolchain: Promise | undefined; +let loadedToolchain: { wasmer: Wasmer; toolchain: Promise } | undefined; let loadedLibcxxPchManifest: Promise | undefined; const loadedLibcxxPch = new Map>(); const objectCache = new ClangObjectCache(64 * 1024 * 1024); const encoder = new TextEncoder(); const decoder = new TextDecoder(); const STAGE_OUTPUT_TIMEOUT_MS = 55_000; +const GUEST_ROOT = "/workspace"; +// The pinned argv and the admitted libc++ PCH name `/project`; SDK sandboxes keep files in `/workspace`. +const PINNED_GUEST_ROOT = "/project"; +// The admitted libc++ PCH asset records its header at the pinned `/project` path. +const LIBCXX_PCH_OVERLAY_PATH = `${GUEST_ROOT}/.wasm-oj/libcxx-pch-overlay.yaml`; +const PREFIX_MAPS = [ + `-fmacro-prefix-map=${GUEST_ROOT}/=${PINNED_GUEST_ROOT}/`, + `-fdebug-prefix-map=${GUEST_ROOT}/=${PINNED_GUEST_ROOT}/`, +]; interface ClangStageResult { diagnostics: Diagnostic[]; @@ -93,9 +95,8 @@ interface StageObservation { } /** - * Browser compiler that drives the pinned cc1 and wasm-ld jobs through - * the official SDK threadpool while keeping every command and project volume - * isolated. No Clang driver or guest subprocess is involved. + * Compiler that drives the pinned cc1 and wasm-ld jobs through the official + * SDK, one sandbox per build. No Clang driver or guest subprocess is involved. */ export async function buildClangWithSdkDirect( project: Project, @@ -112,7 +113,7 @@ export async function buildClangWithSdkDirect( const started = performance.now(); host.progress(requestId, "loading-toolchain", "Loading pinned Clang 22 toolchain", 0.15); - const { pins, compiler, linker } = await ensureToolchain(requestId, host); + const { pins, pkg } = await ensureToolchain(requestId, host); const configKey = `${project.config.language}-${project.config.optimization}`; const config = pins.configs[configKey]; if (!config) throw new Error(`The pinned Clang manifest has no '${configKey}' configuration.`); @@ -123,12 +124,20 @@ export async function buildClangWithSdkDirect( const dependencies = cppDependencyInput(project); for (const [path, bytes] of dependencies.files) projectFiles.set(path, bytes); projectFiles.set(DETERMINISTIC_NATIVE_SOURCE_PATH, encoder.encode(DETERMINISTIC_NATIVE_RUNTIME)); - const directory = new Directory(Object.fromEntries( - [...projectFiles].map(([path, bytes]) => [`/${path}`, bytes]), - )); + const sandbox = await host.wasmer.sandboxes.create({ + packages: [pkg], + files: Object.fromEntries([...projectFiles].map(([path, bytes]) => [`${GUEST_ROOT}/${path}`, bytes])), + env: { + PATH: "/bin", + SOURCE_DATE_EPOCH: "946684800", + TZ: "UTC", + LC_ALL: "C", + }, + }); + const stages = { compiler: pins.command, linker: pins.linkerCommand, sandbox, abandoned: false }; try { - await ensureDirectory(directory, "/build"); - await ensureDirectory(directory, "/.wasm-oj"); + await sandbox.fs.mkdir(`${GUEST_ROOT}/build`, { recursive: true }); + await sandbox.fs.mkdir(`${GUEST_ROOT}/.wasm-oj`, { recursive: true }); host.trace(requestId, "filesystemPrepare", "end"); const isCpp = project.config.language === "cpp"; @@ -151,7 +160,7 @@ export async function buildClangWithSdkDirect( const structuredDiagnostics: Diagnostic[] = []; const pchHeader = isCpp ? findPrecompiledHeader(project) : undefined; - const pchPath = "/project/build/wasm-oj.pch"; + const pchPath = `${GUEST_ROOT}/build/wasm-oj.pch`; let pchInput: BuildGraphInput | undefined; let admittedPch = false; if (pchHeader) { @@ -164,28 +173,37 @@ export async function buildClangWithSdkDirect( throw new Error(`C++ projects using WASM-OJ's admitted libc++ PCH may not define reserved path '${reservedHeader}'.`); } projectFiles.set(reservedHeader, headerBytes); - await directory.writeFile(`/${reservedHeader}`, headerBytes); + await sandbox.fs.writeFile(`${GUEST_ROOT}/${reservedHeader}`, headerBytes); + await sandbox.fs.writeText(LIBCXX_PCH_OVERLAY_PATH, JSON.stringify({ + version: 0, + "case-sensitive": "true", + roots: [{ + type: "directory", + name: PINNED_GUEST_ROOT, + contents: [{ type: "file", name: reservedHeader, "external-contents": `${GUEST_ROOT}/${reservedHeader}` }], + }], + })); pch = await loadLibcxxPch(configKey as LibcxxPchProfile, requestId, host); pchHits += 1; - await directory.writeFile(pchPath.slice("/project".length), pch); + await sandbox.fs.writeFile(pchPath, pch); } else { const baseKey = await objectCache.unitManifestKey(pins, configKey, pchHeader, headerBytes); - const pchManifestKey = await sha256Hex(JSON.stringify({ baseKey, mode: "c++-header" })); + // A PCH records absolute input paths, so entries built under another root never match. + const pchManifestKey = await sha256Hex(JSON.stringify({ baseKey, mode: "c++-header", root: GUEST_ROOT })); const cached = await objectCache.lookupPch(pchManifestKey, projectFiles); if (cached) { pch = cached; pchHits += 1; - await directory.writeFile(pchPath.slice("/project".length), pch); + await sandbox.fs.writeFile(pchPath, pch); } else { pchMisses += 1; - const dependencyPath = "/project/build/wasm-oj.pch.d"; + const dependencyPath = `${GUEST_ROOT}/build/wasm-oj.pch.d`; const args = instantiateClangPch(config.cc1, pins.placeholders, pchHeader, pchPath); args.splice(args.length - 1, 0, ...dependencies.includeDirectories.flatMap((directory) => ["-I", directory])); args.push("-dependency-file", dependencyPath, "-MT", pchPath); const output = await runPchStage( - compiler, + stages, args, - directory, host, requestId, pchPath, @@ -219,8 +237,8 @@ export async function buildClangWithSdkDirect( host.trace(requestId, "projectCompile", "end"); host.trace(requestId, "runtimeShimCompile", "start"); } - const objectPath = `/project/build/${String(index).padStart(4, "0")}.o`; - const dependencyPath = `/project/build/${String(index).padStart(4, "0")}.d`; + const objectPath = `${GUEST_ROOT}/build/${String(index).padStart(4, "0")}.o`; + const dependencyPath = `${GUEST_ROOT}/build/${String(index).padStart(4, "0")}.d`; const sourceBytes = projectFiles.get(source); if (!sourceBytes) throw new Error(`SDK-direct Clang is missing source bytes for '${source}'.`); const baseManifestKey = await objectCache.unitManifestKey(pins, configKey, source, sourceBytes); @@ -231,7 +249,7 @@ export async function buildClangWithSdkDirect( const additionalInputs = unitPchInput ? [unitPchInput] : []; const cached = await objectCache.lookup(manifestKey, projectFiles, additionalInputs); if (cached) { - await directory.writeFile(objectPath.slice("/project".length), cached); + await sandbox.fs.writeFile(objectPath, cached); objectCacheHits += 1; objectPaths.push(objectPath); objectInputs.push({ kind: "object", identity: source, bytes: cached }); @@ -245,14 +263,13 @@ export async function buildClangWithSdkDirect( 0, "-include-pch", pchPath, - ...(admittedPch ? ["-fno-validate-pch"] : []), + ...(admittedPch ? ["-fno-validate-pch", "-ivfsoverlay", LIBCXX_PCH_OVERLAY_PATH] : []), ); } args.push("-dependency-file", dependencyPath, "-MT", objectPath); const output = await runClangStage( - compiler, + stages, args, - directory, host, requestId, source === DETERMINISTIC_NATIVE_SOURCE_PATH ? "runtimeShimSpawn" : "projectSpawn", @@ -291,7 +308,7 @@ export async function buildClangWithSdkDirect( host.progress(requestId, "linking", "Linking SDK-direct Clang objects", 0.8); host.trace(requestId, "link", "start"); - const outputPath = "/project/build/app.wasm"; + const outputPath = `${GUEST_ROOT}/build/app.wasm`; const linkArguments = instantiateClangLink(config.link, pins.placeholders, objectPaths, outputPath); const linkManifestKey = await sha256Hex(JSON.stringify({ pins: pins.sourceSha256, @@ -307,9 +324,8 @@ export async function buildClangWithSdkDirect( } else { linkMisses += 1; const linked = await runLinkStage( - linker, + stages, linkArguments, - directory, host, requestId, "linkSpawn", @@ -364,7 +380,7 @@ export async function buildClangWithSdkDirect( }, }; } finally { - directory.free(); + if (!stages.abandoned) await sandbox.close(); } } @@ -375,6 +391,10 @@ export async function clearSdkDirectClangCaches(): Promise { objectCache.clear(); } +export function clearSdkDirectClangBuildGraph(): void { + objectCache.clear(); +} + export function exportSdkDirectClangBuildGraphState(): IncrementalBuildGraphState { return objectCache.exportState(); } @@ -383,29 +403,23 @@ export function restoreSdkDirectClangBuildGraphState(state: IncrementalBuildGrap return objectCache.restoreState(state); } -/** Release all SDK resources tied to one Runtime while preserving object-cache bytes. */ +/** Forget the package loaded into one SDK client while preserving object-cache bytes. */ export async function disposeSdkDirectClangToolchain(): Promise { - const pending = loadedToolchain; loadedToolchain = undefined; - if (!pending) return; - const { pkg, compiler, linker } = await pending; - compiler.free(); - linker.free(); - pkg.free(); } async function ensureToolchain( requestId: string, host: SdkDirectClangHost, ): Promise { - if (loadedToolchain) { + if (loadedToolchain?.wasmer === host.wasmer) { for (const operation of ["toolchainFetch", "toolchainDecode", "toolchainLoad"] as const) { host.trace(requestId, operation, "start"); host.trace(requestId, operation, "end"); } - return loadedToolchain; + return loadedToolchain.toolchain; } - loadedToolchain = (async () => { + const pending = (async () => { host.trace(requestId, "toolchainFetch", "start"); const [packageBytes, pinsBytes] = await Promise.all([ host.loadToolchainAsset(CLANG_PACKAGE_ASSET_PATH), @@ -420,26 +434,25 @@ async function ensureToolchain( } host.trace(requestId, "toolchainDecode", "end"); host.trace(requestId, "toolchainLoad", "start"); - const pkg = await Wasmer.fromFile(packageBytes, host.runtime); - const compiler = requireCommand(pkg, pins.command); - const linker = requireCommand(pkg, pins.linkerCommand); + const pkg = await host.wasmer.packages.load(packageBytes); + for (const command of [pins.command, pins.linkerCommand]) { + if (!pkg.commands.includes(command)) { + throw new Error(`The SDK-direct Clang package does not expose '${command}'.`); + } + } host.trace(requestId, "toolchainLoad", "end"); - return { pkg, pins, compiler, linker }; + return { pkg, pins }; })(); + const entry = { wasmer: host.wasmer, toolchain: pending }; + loadedToolchain = entry; try { - return await loadedToolchain; + return await pending; } catch (error) { - loadedToolchain = undefined; + if (loadedToolchain === entry) loadedToolchain = undefined; throw error; } } -function requireCommand(pkg: Wasmer, name: string): Command { - const selected = pkg.commands[name]; - if (!selected) throw new Error(`The SDK-direct Clang package does not expose '${name}'.`); - return selected; -} - async function loadLibcxxPch( profile: LibcxxPchProfile, requestId: string, @@ -478,48 +491,50 @@ function findPrecompiledHeader(project: Project): string | undefined { return headers[0]; } -function runPchStage( - command: Command, +interface ClangStages { + compiler: string; + linker: string; + sandbox: Sandbox; + abandoned: boolean; +} + +async function runPchStage( + stages: ClangStages, args: string[], - directory: Directory, host: SdkDirectClangHost, requestId: string, outputPath: string, dependencyPath: string, ): Promise { - const stability = new MountedOutputStabilityObserver(); - return runUntilOutputReady( - command, - args, - directory, + const observed = await runStage( + stages, + stages.compiler, + [...guestArguments(args), ...PREFIX_MAPS], host, requestId, "projectSpawn", "projectWait", "projectOutputReady", - async (capturedStderr) => { - const [snapshot, dependency] = await Promise.all([ - readOptionalFile(directory, outputPath), - readOptionalFile(directory, dependencyPath), + async (succeeded) => { + if (!succeeded) return {}; + const [pch, dependency] = await Promise.all([ + readOptionalFile(stages.sandbox, outputPath), + readOptionalFile(stages.sandbox, dependencyPath), ]); - const pch = stability.observe(snapshot?.byteLength ? snapshot : undefined, performance.now()); - if (pch && dependency?.byteLength && decoder.decode(dependency).endsWith("\n")) { - return { pch, dependency }; - } - return /\d+ errors? generated\.\s*$/.test(capturedStderr) ? {} : undefined; + return pch?.byteLength && dependency?.byteLength ? { pch, dependency } : {}; }, - ).then((observed) => ({ + ); + return { ...observed.value, diagnostics: parseClangDiagnostics(`${observed.stderr}\n${observed.stdout}`), stdout: observed.stdout, stderr: observed.stderr, - })); + }; } -function runClangStage( - command: Command, +async function runClangStage( + stages: ClangStages, args: string[], - directory: Directory, host: SdkDirectClangHost, requestId: string, spawnOperation: CompilerTraceOperation, @@ -528,37 +543,35 @@ function runClangStage( outputPath: string, dependencyPath: string, ): Promise { - return runUntilOutputReady( - command, - args, - directory, + const observed = await runStage( + stages, + stages.compiler, + [...guestArguments(args), ...PREFIX_MAPS], host, requestId, spawnOperation, waitOperation, outputReadyOperation, - async (capturedStderr) => { + async (succeeded) => { + if (!succeeded) return {}; const [object, dependency] = await Promise.all([ - readValidWasmFile(directory, outputPath), - readOptionalFile(directory, dependencyPath), + readValidWasmFile(stages.sandbox, outputPath), + readOptionalFile(stages.sandbox, dependencyPath), ]); - if (object && dependency?.byteLength && decoder.decode(dependency).endsWith("\n")) { - return { object, dependency }; - } - return /\d+ errors? generated\.\s*$/.test(capturedStderr) ? {} : undefined; + return object && dependency?.byteLength ? { object, dependency } : {}; }, - ).then((observed) => ({ + ); + return { ...observed.value, diagnostics: parseClangDiagnostics(`${observed.stderr}\n${observed.stdout}`), stdout: observed.stdout, stderr: observed.stderr, - })); + }; } function runLinkStage( - command: Command, + stages: ClangStages, args: string[], - directory: Directory, host: SdkDirectClangHost, requestId: string, spawnOperation: CompilerTraceOperation, @@ -566,135 +579,72 @@ function runLinkStage( outputReadyOperation: CompilerTraceOperation, outputPath: string, ): Promise> { - return runUntilOutputReady( - command, - args, - directory, + return runStage( + stages, + stages.linker, + guestArguments(args), host, requestId, spawnOperation, waitOperation, outputReadyOperation, - async (capturedStderr) => { - const output = await readValidWasmFile(directory, outputPath); - if (output) return output; - return /(?:wasm-ld|lld): error:/i.test(capturedStderr) ? null : undefined; - }, - ).then((observed) => ({ ...observed, value: observed.value ?? undefined })); + async (succeeded) => succeeded ? readValidWasmFile(stages.sandbox, outputPath) : undefined, + ); } -async function runUntilOutputReady( - command: Command, +async function runStage( + stages: ClangStages, + command: string, args: string[], - directory: Directory, host: SdkDirectClangHost, requestId: string, spawnOperation: CompilerTraceOperation, waitOperation: CompilerTraceOperation, outputReadyOperation: CompilerTraceOperation, - probe: (capturedStderr: string) => Promise, -): Promise> { + readOutput: (succeeded: boolean) => Promise, +): Promise> { host.trace(requestId, spawnOperation, "start"); - const instance = await command.run({ + const running = runSandboxCommand(stages.sandbox, stages, { + command, args, - cwd: "/project", - env: { - PATH: "/bin", - SOURCE_DATE_EPOCH: "946684800", - TZ: "UTC", - LC_ALL: "C", - }, - mount: { "/project": directory }, + cwd: GUEST_ROOT, + timeoutMs: STAGE_OUTPUT_TIMEOUT_MS, + timeoutMessage: `Compiler stage did not complete within ${STAGE_OUTPUT_TIMEOUT_MS} ms.`, }); host.trace(requestId, spawnOperation, "end"); host.trace(requestId, waitOperation, "start"); host.trace(requestId, outputReadyOperation, "start"); - const stdoutCapture = captureReadable(instance.stdout); - const stderrCapture = captureReadable(instance.stderr); - const deadline = performance.now() + STAGE_OUTPUT_TIMEOUT_MS; try { - while (performance.now() < deadline) { - const result = await probe(stderrCapture.text()); - if (result !== undefined) { - await new Promise((resolve) => setTimeout(resolve, 0)); - return { - value: result, - stdout: stdoutCapture.text(), - stderr: stderrCapture.text(), - }; - } - await new Promise((resolve) => setTimeout(resolve, 5)); - } - throw new Error(`Compiler stage did not produce a complete output within ${STAGE_OUTPUT_TIMEOUT_MS} ms.`); + const output = await running; + return { + value: await readOutput(output.ok), + stdout: decoder.decode(output.stdout), + stderr: decoder.decode(output.stderr), + }; } finally { host.trace(requestId, outputReadyOperation, "end"); host.trace(requestId, waitOperation, "end"); - await Promise.all([stdoutCapture.cancel(), stderrCapture.cancel()]); - instance.free(); } } -function captureReadable(stream: ReadableStream): { text(): string; cancel(): Promise } { - const reader = stream.getReader(); - const chunks: Uint8Array[] = []; - void (async () => { - try { - while (true) { - const result = await reader.read(); - if (result.done) return; - const bytes = result.value instanceof Uint8Array - ? result.value - : new Uint8Array(result.value as ArrayBuffer); - chunks.push(bytes.slice()); - } - } catch { - // Cancellation is expected when a complete mounted output is observed. - } - })(); - return { - text: () => decoder.decode(concatenate(chunks)), - cancel: async () => { - try { - await reader.cancel(); - } catch { - // The process may have closed the stream between observation and cancellation. - } - }, - }; -} - -function concatenate(chunks: readonly Uint8Array[]): Uint8Array { - const output = new Uint8Array(chunks.reduce((total, chunk) => total + chunk.byteLength, 0)); - let offset = 0; - for (const chunk of chunks) { - output.set(chunk, offset); - offset += chunk.byteLength; - } - return output; +function guestArguments(args: readonly string[]): string[] { + return args.map((argument) => ( + argument === PINNED_GUEST_ROOT || argument.startsWith(`${PINNED_GUEST_ROOT}/`) + ? `${GUEST_ROOT}${argument.slice(PINNED_GUEST_ROOT.length)}` + : argument + )); } -async function readValidWasmFile(directory: Directory, guestPath: string): Promise { - const bytes = await readOptionalFile(directory, guestPath); +async function readValidWasmFile(sandbox: Sandbox, guestPath: string): Promise { + const bytes = await readOptionalFile(sandbox, guestPath); if (!bytes || bytes.byteLength <= 8) return undefined; - const copy = new Uint8Array(bytes.byteLength); - copy.set(bytes); - return WebAssembly.validate(copy.buffer) ? copy : undefined; -} - -async function ensureDirectory(directory: Directory, path: string): Promise { - try { - await directory.createDir(path); - } catch (error) { - if (!String(error).toLowerCase().includes("exist")) throw error; - } + const copy = new Uint8Array(bytes); + return WebAssembly.validate(copy) ? copy : undefined; } -async function readOptionalFile(directory: Directory, guestPath: string): Promise { - const mountRelativePath = guestPath.startsWith("/project/") - ? guestPath.slice("/project".length) - : guestPath; +async function readOptionalFile(sandbox: Sandbox, guestPath: string): Promise { try { - return await directory.readFile(mountRelativePath); + return await sandbox.fs.readFile(guestPath); } catch { return undefined; } diff --git a/src/compiler/wasmer-engine.ts b/src/compiler/wasmer-engine.ts index ca662d7..8cde906 100644 --- a/src/compiler/wasmer-engine.ts +++ b/src/compiler/wasmer-engine.ts @@ -1,8 +1,4 @@ -import { - Runtime, - Wasmer, - type Output, -} from "@wasmer/sdk"; +import type { Package, Wasmer } from "@wasmer/sdk"; import { WASM_OJ_CONTRACT_VERSION } from "../core/contract.ts"; import { canonicalRuntimeBundleFiles, @@ -44,6 +40,7 @@ import type { import type { GoCompileRequest, GoCompileResult } from "./go-toolchain.ts"; import type { JavaCompileRequest, JavaCompileResult } from "./java-toolchain.ts"; import { buildClangWithSdkDirect } from "./sdk-direct-clang.ts"; +import { runSandboxCommand } from "./sandbox-command.ts"; import { assertProjectDependencyEcosystem, goDependencyInput, @@ -52,10 +49,12 @@ import { rustDependencyInput, } from "./dependency-input.ts"; const encoder = new TextEncoder(); +const decoder = new TextDecoder(); let typescriptCompilerBytes: Promise | undefined; +let typescriptCompiler: { wasmer: Wasmer; pkg: Promise } | undefined; export interface WasmerCompilerHost { - getRuntime(): Runtime; + getWasmer(): Wasmer; loadToolchainAsset(path: string): Promise; loadToolchainFile(path: string): Promise; compileRust(request: RustCompileRequest): Promise; @@ -80,16 +79,21 @@ function requireHost(): WasmerCompilerHost { return host; } -function requireRuntime(): Runtime { - return requireHost().getRuntime(); -} - -async function getTypeScriptCompiler(): Promise { - typescriptCompilerBytes ??= requireHost().loadToolchainAsset(TYPESCRIPT_ASSET_PATH); +async function getTypeScriptCompiler(): Promise { + const wasmer = requireHost().getWasmer(); + if (typescriptCompiler?.wasmer !== wasmer) { + typescriptCompilerBytes ??= requireHost().loadToolchainAsset(TYPESCRIPT_ASSET_PATH); + const bytes = typescriptCompilerBytes; + typescriptCompiler = { wasmer, pkg: bytes.then((value) => wasmer.packages.load(value)) }; + } + const pending = typescriptCompiler; try { - return Wasmer.fromWasm(await typescriptCompilerBytes, requireRuntime()); + return await pending.pkg; } catch (error) { - typescriptCompilerBytes = undefined; + if (typescriptCompiler === pending) { + typescriptCompiler = undefined; + typescriptCompilerBytes = undefined; + } throw error; } } @@ -307,17 +311,24 @@ interface TypeScriptWasiResponse { files: Record; } -async function transpileScriptProject(project: Project, requestId: string): Promise<{ files: Record; output: Output; response?: TypeScriptWasiResponse }> { +interface TypeScriptWasiOutput { + ok: boolean; + stderr: string; +} + +async function transpileScriptProject(project: Project, requestId: string): Promise<{ files: Record; output: TypeScriptWasiOutput; response?: TypeScriptWasiResponse }> { const scriptFiles = scriptSourceFiles(project); const emittedFiles = emittedSourceFiles(project); const dependencyFiles = npmDependencyFiles(project); progress(requestId, "loading-toolchain", `Loading TypeScript ${TYPESCRIPT_VERSION}/WASI`); const compiler = await getTypeScriptCompiler(); - const entrypoint = compiler.entrypoint; - if (!entrypoint) throw new Error("The TypeScript/WASI compiler has no executable entrypoint."); + if (!compiler.entrypoint) throw new Error("The TypeScript/WASI compiler has no executable entrypoint."); const outputPaths = emittedFiles.map((file) => emittedScriptPath(file.path)); const declarationPath = "/project/.wasm-oj/quickjs.d.ts"; - const instance = await entrypoint.run({ + const sandbox = await requireHost().getWasmer().sandboxes.create({ packages: [compiler] }); + const completed = await runSandboxCommand(sandbox, { abandoned: false }, { + command: compiler, + args: [], stdin: JSON.stringify({ files: { ...Object.fromEntries(project.files.map((file) => [`/project/${file.path}`, file.content])), @@ -337,11 +348,12 @@ async function transpileScriptProject(project: Project, requestId: string): Prom outputs: outputPaths.map((path) => `/project/build/${path}`), }), }); - const output = await instance.wait(); + await sandbox.close(); + const output = { ok: completed.ok, stderr: decoder.decode(completed.stderr) }; let response: TypeScriptWasiResponse | undefined; if (output.ok) { try { - response = JSON.parse(output.stdout) as TypeScriptWasiResponse; + response = JSON.parse(decoder.decode(completed.stdout)) as TypeScriptWasiResponse; } catch { response = undefined; } @@ -440,7 +452,7 @@ export async function buildProject(project: Project, cacheKey: string, requestId if (canonicalProject.config.language === "c" || canonicalProject.config.language === "cpp") { const activeHost = requireHost(); return buildClangWithSdkDirect(canonicalProject, cacheKey, requestId, { - runtime: activeHost.getRuntime(), + wasmer: activeHost.getWasmer(), loadToolchainAsset: activeHost.loadToolchainAsset, loadToolchainFile: activeHost.loadToolchainFile, progress: activeHost.progress, @@ -479,4 +491,5 @@ languageDrivers.register({ export function clearCompilerHostCaches(): void { typescriptCompilerBytes = undefined; + typescriptCompiler = undefined; } diff --git a/src/core/runtime-identity.test.ts b/src/core/runtime-identity.test.ts index 5690492..77d7e28 100644 --- a/src/core/runtime-identity.test.ts +++ b/src/core/runtime-identity.test.ts @@ -22,7 +22,7 @@ describe("WASM-OJ runtime identity", () => { it("pins the executable browser runtime bytes", async () => { const runtimeCore = await readFile(fileURLToPath(new URL("../runner/generated/runtime-core_bg.wasm", import.meta.url))); - const wasmerSdk = await readFile(fileURLToPath(import.meta.resolve("@wasmer/sdk/wasm"))); + const wasmerSdk = await readFile(fileURLToPath(new URL("../pkg/wasmer_sdk_js_bg.wasm", import.meta.resolve("@wasmer/sdk/browser")))); expect(await sha256Hex(runtimeCore)).toBe(WASM_OJ_RUNTIME_COMPONENTS.runtimeCoreWasmSha256); expect(await sha256Hex(wasmerSdk)).toBe(WASM_OJ_RUNTIME_COMPONENTS.wasmerSdkWasmSha256); }); diff --git a/src/core/runtime-identity.ts b/src/core/runtime-identity.ts index 390b632..d402f32 100644 --- a/src/core/runtime-identity.ts +++ b/src/core/runtime-identity.ts @@ -6,8 +6,8 @@ export const WASM_OJ_RUNTIME_COMPONENTS = Object.freeze({ runtimeCoreWasmSha256: "1e297ad276694e9fb8a23afdc6ccc0b2ae56a223ced19d5cae26fe4661daa11e", runtimeSourceRootSha256: "27c52cb1887aa8a7e0d8a238514fc8881cf86c68267e2a1691f110423692e2b6", wasmerNativeVersion: "7.2.1", - wasmerSdkVersion: "0.10.0", - wasmerSdkWasmSha256: "49a6646209f5ab5e7c737eac33407d87d9a9959ac83e5ecaaab9261b2323589e", + wasmerSdkVersion: "0.19.0", + wasmerSdkWasmSha256: "86aefc8940d29045f94646421bec6fac528a736aa4e78718fdb7924897ee4c25", wasmerWasixVersion: "0.702.1", } as const); @@ -17,7 +17,7 @@ export const WASM_OJ_RUNTIME_COMPONENTS = Object.freeze({ * identity is admitted into a calibrated release. */ export const WASM_OJ_RUNTIME_IDENTITY_SHA256 = - "d3e44cafdc1c17a7e6bd4591ef04ec8161af805a519b5bbff437b15e5accd442"; + "4b7a0a876d97c53675bc9b1a5a1d5da8a07e632b9cdafc2f775ab65efd9d2ae2"; /** Exact canonical serialization hashed by `WASM_OJ_RUNTIME_IDENTITY_SHA256`. */ export function runtimeIdentityBytes(): Uint8Array { diff --git a/src/core/toolchains.ts b/src/core/toolchains.ts index 9d0fbba..2c8fce1 100644 --- a/src/core/toolchains.ts +++ b/src/core/toolchains.ts @@ -55,6 +55,7 @@ export const PYTHON_PACKAGE_ASSET_PATH = `/toolchains/python-${PYTHON_VERSION}-w export const PYTHON_PACKAGE_MANIFEST_ASSET_PATH = `/toolchains/python-${PYTHON_VERSION}-wasip1.manifest.json`; export const PYTHON_COMPRESSED_PACKAGE_SHA256 = "10027f0e32c77dfa0ce1c413b6cb27307d6744fe5e18e6da8cea738bd020260c"; export const PYTHON_PACKAGE_MANIFEST_SHA256 = "53a24b258363a8494af164121111a34b49c85fa0e054627fe131feba6f359cd5"; +export const PYTHON_COMMAND_SHA256 = "69e9d87da6c8a628694ffd76c3d0f26e7aaa0bc91d952a28d9fc37c7144e354c"; export const PYTHON_RUNTIME_FILES_ARCHIVE_SHA256 = "c1acac884af1c86833db5b65f8cc0a2304fce74dea0a05d9b40bde0a4e3e7c0e"; export const QUICKJS_VERSION = "0.15.1"; export const QUICKJS_PACKAGE = `wasm-oj/quickjs-ng@${QUICKJS_VERSION}`; diff --git a/src/runner/package-handle-cache.test.ts b/src/runner/package-handle-cache.test.ts deleted file mode 100644 index d9d1b81..0000000 --- a/src/runner/package-handle-cache.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import type { Command, Wasmer } from "@wasmer/sdk"; -import { describe, expect, it, vi } from "vitest"; -import { - PackageHandleCache, - WasmerPackageHandle, - withHandleLease, - withWasmerCommand, -} from "./package-handle-cache"; - -interface TrackedHandle { - free(): void; -} - -describe("PackageHandleCache", () => { - it("defers retired handle release until every lease is idle", async () => { - const cache = new PackageHandleCache(); - const first = { free: vi.fn() }; - const second = { free: vi.fn() }; - const firstLoad = vi.fn(async () => first); - const secondLoad = vi.fn(async () => second); - const firstLease = await cache.acquire("python", firstLoad); - const sharedLease = await cache.acquire("python", firstLoad); - - const retirement = cache.retireAll(); - expect(first.free).not.toHaveBeenCalled(); - - const replacementLease = await cache.acquire("python", secondLoad); - expect(firstLoad).toHaveBeenCalledOnce(); - expect(secondLoad).toHaveBeenCalledOnce(); - firstLease.release(); - expect(first.free).not.toHaveBeenCalled(); - sharedLease.release(); - await retirement.wait(); - expect(first.free).toHaveBeenCalledOnce(); - expect(second.free).not.toHaveBeenCalled(); - - replacementLease.release(); - await cache.retireAll().wait(); - expect(second.free).toHaveBeenCalledOnce(); - }); - - it("evicts a rejected load without manufacturing a handle to release", async () => { - const cache = new PackageHandleCache(); - const failure = new Error("load failed"); - await expect(cache.acquire("python", async () => { throw failure; })).rejects.toBe(failure); - - const replacement = { free: vi.fn() }; - const lease = await cache.acquire("python", async () => replacement); - lease.release(); - await cache.retireAll().wait(); - expect(replacement.free).toHaveBeenCalledOnce(); - }); - - it("retires a pending load but waits for its eventual lease release", async () => { - const cache = new PackageHandleCache(); - const handle = { free: vi.fn() }; - let finishLoad!: (value: TrackedHandle) => void; - const acquisition = cache.acquire("python", () => new Promise((resolve) => { - finishLoad = resolve; - })); - await Promise.resolve(); - const retirement = cache.retireAll(); - - finishLoad(handle); - const lease = await acquisition; - expect(handle.free).not.toHaveBeenCalled(); - lease.release(); - await retirement.wait(); - expect(handle.free).toHaveBeenCalledOnce(); - }); - - it("rejects double lease release", async () => { - const cache = new PackageHandleCache(); - const handle = { free: vi.fn() }; - const lease = await cache.acquire("python", async () => handle); - lease.release(); - expect(() => lease.release()).toThrow("more than once"); - await cache.retireAll().wait(); - expect(handle.free).toHaveBeenCalledOnce(); - }); - - it("surfaces synchronous and deferred handle-release failures", async () => { - const synchronousFailure = new Error("synchronous free failed"); - const synchronousCache = new PackageHandleCache(); - const synchronousLease = await synchronousCache.acquire("python", async () => ({ - free() { throw synchronousFailure; }, - })); - synchronousLease.release(); - expect(() => synchronousCache.retireAll()).toThrow(expect.objectContaining({ - errors: [synchronousFailure], - })); - - const deferredFailure = new Error("deferred free failed"); - const deferredCache = new PackageHandleCache(); - const deferredLease = await deferredCache.acquire("python", async () => ({ - free() { throw deferredFailure; }, - })); - const retirement = deferredCache.retireAll(); - expect(() => deferredLease.release()).toThrow(deferredFailure); - await expect(retirement.wait()).rejects.toBe(deferredFailure); - }); -}); - -describe("withWasmerCommand", () => { - it("keeps package-owned command wrappers alive through use and frees each unique wrapper once", async () => { - const selected = command(); - const dependency = command(); - const packageFree = vi.fn(); - const pkg = wasmerPackage( - { - python: selected.value, - dependency: dependency.value, - dependencyAlias: dependency.value, - }, - packageFree, - ); - let finish!: (value: Uint8Array) => void; - const operation = withWasmerCommand(pkg, "python", () => new Promise((resolve) => { - finish = resolve; - })); - - expect(selected.free).not.toHaveBeenCalled(); - expect(dependency.free).not.toHaveBeenCalled(); - const binary = new Uint8Array([0, 97, 115, 109]); - finish(binary); - await expect(operation).resolves.toBe(binary); - expect(selected.free).not.toHaveBeenCalled(); - expect(dependency.free).not.toHaveBeenCalled(); - - pkg.free(); - expect(selected.free).toHaveBeenCalledOnce(); - expect(dependency.free).toHaveBeenCalledOnce(); - expect(packageFree).toHaveBeenCalledOnce(); - }); - - it("does not release package-owned wrappers when the requested command is absent", async () => { - const dependency = command(); - const pkg = wasmerPackage({ dependency: dependency.value }); - - await expect(withWasmerCommand(pkg, "python", () => undefined)).rejects.toThrow( - "does not expose the 'python' command", - ); - expect(dependency.free).not.toHaveBeenCalled(); - pkg.free(); - expect(dependency.free).toHaveBeenCalledOnce(); - }); - - it("preserves command failures independently from later package cleanup", async () => { - const operationFailure = new Error("command failed"); - const selected = command(); - const pkg = wasmerPackage({ python: selected.value }); - - await expect(withWasmerCommand(pkg, "python", async () => { - throw operationFailure; - })).rejects.toBe(operationFailure); - expect(selected.free).not.toHaveBeenCalled(); - pkg.free(); - expect(selected.free).toHaveBeenCalledOnce(); - }); - - it("attempts all package-owned cleanup and aggregates its errors", () => { - const commandFailure = new Error("command free failed"); - const packageFailure = new Error("package free failed"); - const pkg = wasmerPackage( - { python: { free() { throw commandFailure; } } as unknown as Command }, - () => { throw packageFailure; }, - ); - - expect(() => pkg.free()).toThrow(expect.objectContaining({ - errors: [commandFailure, packageFailure], - })); - }); -}); - -describe("withHandleLease", () => { - it("preserves the primary operation failure when lease cleanup also fails", async () => { - const operationFailure = new Error("operation failed"); - const cleanupFailure = new Error("lease release failed"); - const caught = await withHandleLease( - { - value: "package", - release() { throw cleanupFailure; }, - }, - async () => { throw operationFailure; }, - ).catch((error: unknown) => error); - - expect(caught).toBeInstanceOf(AggregateError); - expect((caught as AggregateError).errors).toEqual([operationFailure, cleanupFailure]); - }); -}); - -function command(): { value: Command; free: ReturnType } { - const free = vi.fn(); - return { - value: { free } as unknown as Command, - free, - }; -} - -function wasmerPackage( - commands: Record, - free: () => void = vi.fn(), -): WasmerPackageHandle { - return new WasmerPackageHandle({ commands, free } as unknown as Wasmer); -} diff --git a/src/runner/package-handle-cache.ts b/src/runner/package-handle-cache.ts deleted file mode 100644 index 76329a1..0000000 --- a/src/runner/package-handle-cache.ts +++ /dev/null @@ -1,224 +0,0 @@ -import type { Command, Wasmer } from "@wasmer/sdk"; - -interface ReleasableHandle { - free(): void; -} - -interface HandleEntry { - readonly promise: Promise; - handle?: Handle; - borrowers: number; - retired: boolean; - releaseState: "pending" | "released" | "failed"; - releaseError?: unknown; - readonly releaseListeners: Set<() => void>; -} - -export interface HandleLease { - readonly value: Handle; - release(): void; -} - -export interface HandleRetirement { - wait(): Promise; -} - -export class WasmerPackageHandle implements ReleasableHandle { - readonly commands: Record; - private released = false; - - constructor(readonly wasmer: Wasmer) { - this.commands = wasmer.commands; - } - - free(): void { - if (this.released) throw new Error("Wasmer package handle was released more than once."); - this.released = true; - const errors: unknown[] = []; - for (const command of new Set(Object.values(this.commands))) { - try { - command.free(); - } catch (error) { - errors.push(error); - } - } - try { - this.wasmer.free(); - } catch (error) { - errors.push(error); - } - if (errors.length === 1) throw errors[0]; - if (errors.length > 1) { - throw new AggregateError(errors, "Unable to release a Wasmer package and its command handles."); - } - } -} - -export class PackageHandleCache { - private readonly entries = new Map>(); - - async acquire(key: Key, load: () => Promise): Promise> { - let entry = this.entries.get(key); - if (!entry) { - entry = this.createEntry(key, load); - this.entries.set(key, entry); - } - entry.borrowers += 1; - - let handle: Handle; - try { - handle = await entry.promise; - } catch (error) { - entry.borrowers -= 1; - throw error; - } - - let released = false; - return { - value: handle, - release: () => { - if (released) throw new Error("Package handle lease was released more than once."); - released = true; - entry.borrowers -= 1; - if (entry.borrowers < 0) throw new Error("Package handle lease count became negative."); - this.releaseRetiredEntry(entry); - }, - }; - } - - retireAll(): HandleRetirement { - const retired = [...this.entries.values()]; - this.entries.clear(); - const releaseErrors: unknown[] = []; - for (const entry of retired) { - entry.retired = true; - try { - this.releaseRetiredEntry(entry); - } catch (error) { - releaseErrors.push(error); - } - } - if (releaseErrors.length > 0) { - throw new AggregateError(releaseErrors, "Unable to release one or more retired package handles."); - } - // Completion is deliberately lazy: synchronous idle-release failures are - // thrown above, while active leases surface deferred failures from release(). - // Explicit async clear operations may additionally wait for all borrowers. - return { - wait: () => Promise.all(retired.map((entry) => waitForRelease(entry))).then(() => undefined), - }; - } - - private createEntry(key: Key, load: () => Promise): HandleEntry { - const reference: { current?: HandleEntry } = {}; - const promise = Promise.resolve().then(load).then( - (handle) => { - const entry = reference.current; - if (!entry) throw new Error("Package handle cache entry was not initialized."); - entry.handle = handle; - return handle; - }, - (error: unknown) => { - const entry = reference.current; - if (!entry) throw new Error("Package handle cache entry was not initialized."); - if (this.entries.get(key) === entry) this.entries.delete(key); - entry.releaseState = "released"; - notifyReleaseListeners(entry); - throw error; - }, - ); - const entry: HandleEntry = { - promise, - borrowers: 0, - retired: false, - releaseState: "pending", - releaseListeners: new Set<() => void>(), - }; - reference.current = entry; - return entry; - } - - private releaseRetiredEntry(entry: HandleEntry): void { - if (!entry.retired || entry.borrowers !== 0 || !entry.handle || entry.releaseState !== "pending") return; - try { - entry.handle.free(); - entry.releaseState = "released"; - } catch (error) { - entry.releaseState = "failed"; - entry.releaseError = error; - throw error; - } finally { - notifyReleaseListeners(entry); - } - } -} - -export async function withWasmerCommand( - pkg: WasmerPackageHandle, - commandName: string, - operation: (command: Command) => Result | Promise, -): Promise { - const selected = pkg.commands[commandName]; - if (!selected) throw new Error(`Package does not expose the '${commandName}' command.`); - return operation(selected); -} - -export function withHandleLease( - lease: HandleLease, - operation: (handle: Handle) => Result | Promise, -): Promise { - return withCleanup( - () => operation(lease.value), - () => lease.release(), - "Package operation and package-handle cleanup both failed.", - ); -} - -function waitForRelease(entry: HandleEntry): Promise { - if (entry.releaseState === "released") return Promise.resolve(); - if (entry.releaseState === "failed") return Promise.reject(entry.releaseError); - return new Promise((resolve, reject) => { - const listener = () => { - entry.releaseListeners.delete(listener); - if (entry.releaseState === "released") resolve(); - else reject(entry.releaseError); - }; - entry.releaseListeners.add(listener); - }); -} - -function notifyReleaseListeners(entry: HandleEntry): void { - for (const listener of [...entry.releaseListeners]) listener(); -} - -async function withCleanup( - operation: () => Result | Promise, - cleanup: () => void, - aggregateMessage: string, -): Promise { - let result: Result | undefined; - let operationFailed = false; - let operationError: unknown; - try { - result = await operation(); - } catch (error) { - operationFailed = true; - operationError = error; - } - - let cleanupFailed = false; - let cleanupError: unknown; - try { - cleanup(); - } catch (error) { - cleanupFailed = true; - cleanupError = error; - } - - if (operationFailed && cleanupFailed) { - throw new AggregateError([operationError, cleanupError], aggregateMessage); - } - if (operationFailed) throw operationError; - if (cleanupFailed) throw cleanupError; - return result as Result; -} diff --git a/src/runner/webc.test.ts b/src/runner/webc.test.ts new file mode 100644 index 0000000..b8d8e0e --- /dev/null +++ b/src/runner/webc.test.ts @@ -0,0 +1,64 @@ +import { describe, expect, it } from "vitest"; +import { readWebcAtom } from "./webc"; + +const encoder = new TextEncoder(); + +function u64(value: number): Uint8Array { + const bytes = new Uint8Array(8); + new DataView(bytes.buffer).setBigUint64(0, BigInt(value), true); + return bytes; +} + +function concat(...parts: Uint8Array[]): Uint8Array { + const output = new Uint8Array(parts.reduce((total, part) => total + part.byteLength, 0)); + let offset = 0; + for (const part of parts) { + output.set(part, offset); + offset += part.byteLength; + } + return output; +} + +function webc(atoms: Record): Uint8Array { + const names = Object.keys(atoms); + const data = concat(...Object.values(atoms)); + const entryBytes = names.map((name) => 8 + 32 + 8 + encoder.encode(name).byteLength); + const directoryLength = 3 * 8 + 32 + entryBytes.reduce((total, size) => total + size, 0); + let fileOffset = 1 + 8 + directoryLength; + let dataOffset = 0; + const entries: Uint8Array[] = []; + const files: Uint8Array[] = []; + for (const name of names) { + const encoded = encoder.encode(name); + entries.push(u64(fileOffset), new Uint8Array(32), u64(encoded.byteLength), encoded); + const size = atoms[name]!.byteLength; + files.push(Uint8Array.of(31), u64(dataOffset), u64(dataOffset + size), new Uint8Array(32 + 3 * 8)); + fileOffset += 1 + 8 + 8 + 32 + 3 * 8; + dataOffset += size; + } + const header = concat(Uint8Array.of(30), u64(directoryLength), new Uint8Array(3 * 8 + 32), ...entries, ...files); + const atomsSection = concat(u64(header.byteLength), header, u64(data.byteLength), data); + const manifest = encoder.encode("manifest"); + return concat( + encoder.encode("\0webc003"), + Uint8Array.of(2), u64(4), encoder.encode("idx!"), + Uint8Array.of(1), new Uint8Array(32), u64(manifest.byteLength), manifest, + Uint8Array.of(3), new Uint8Array(32), u64(atomsSection.byteLength), atomsSection, + ); +} + +describe("WEBC atom reader", () => { + it("returns the named atom's exact bytes", () => { + const python = encoder.encode("\0asm-python-module"); + const container = webc({ helper: encoder.encode("other"), python }); + expect(readWebcAtom(container, "python")).toEqual(python); + expect(readWebcAtom(container, "helper")).toEqual(encoder.encode("other")); + }); + + it("rejects foreign containers, missing atoms, and truncated sections", () => { + const container = webc({ python: encoder.encode("module") }); + expect(() => readWebcAtom(encoder.encode("\0webc002........"), "python")).toThrow("not a WEBC v3"); + expect(() => readWebcAtom(container, "rustc")).toThrow("no 'rustc' atom"); + expect(() => readWebcAtom(container.subarray(0, container.byteLength - 3), "python")).toThrow("truncated"); + }); +}); diff --git a/src/runner/webc.ts b/src/runner/webc.ts new file mode 100644 index 0000000..8cd2359 --- /dev/null +++ b/src/runner/webc.ts @@ -0,0 +1,72 @@ +const MAGIC = "\0webc003"; +const INDEX_TAG = 2; +const ATOMS_TAG = 3; +const DIRECTORY_TAG = 30; +const FILE_TAG = 31; +const DIRECTORY_METADATA_BYTES = 3 * 8 + 32; +const decoder = new TextDecoder("utf-8", { fatal: true }); + +/** + * Return the bytes of one named atom from a WEBC v3 container. + * + * Callers verify the returned module against a pinned digest; this reader only + * enforces the container layout and bounds. + */ +export function readWebcAtom(webc: Uint8Array, name: string): Uint8Array { + if (webc.byteLength < MAGIC.length || decoder.decode(webc.subarray(0, MAGIC.length)) !== MAGIC) { + throw new Error("The package is not a WEBC v3 container."); + } + const view = new DataView(webc.buffer, webc.byteOffset, webc.byteLength); + let offset = MAGIC.length; + while (offset < webc.byteLength) { + const tag = webc[offset]!; + const headerBytes = tag === INDEX_TAG ? 1 + 8 : 1 + 32 + 8; + const length = readLength(view, offset + headerBytes - 8); + const start = offset + headerBytes; + const end = checkedEnd(webc, start, length); + if (tag === ATOMS_TAG) return findAtom(webc.subarray(start, end), name); + offset = end; + } + throw new Error("The WEBC container has no atoms section."); +} + +function findAtom(section: Uint8Array, name: string): Uint8Array { + const view = new DataView(section.buffer, section.byteOffset, section.byteLength); + const headerLength = readLength(view, 0); + const header = section.subarray(8, checkedEnd(section, 8, headerLength)); + const dataStart = 8 + headerLength; + const data = section.subarray(dataStart + 8, checkedEnd(section, dataStart + 8, readLength(view, dataStart))); + const headerView = new DataView(header.buffer, header.byteOffset, header.byteLength); + if (header[0] !== DIRECTORY_TAG) throw new Error("The WEBC atoms header has no root directory."); + const entriesEnd = checkedEnd(header, 9, readLength(headerView, 1)); + let cursor = 9 + DIRECTORY_METADATA_BYTES; + while (cursor < entriesEnd) { + const entryOffset = readLength(headerView, cursor); + const nameLength = readLength(headerView, cursor + 8 + 32); + const nameStart = cursor + 8 + 32 + 8; + const entryName = decoder.decode(header.subarray(nameStart, checkedEnd(header, nameStart, nameLength))); + cursor = nameStart + nameLength; + if (entryName !== name) continue; + if (header[entryOffset] !== FILE_TAG) throw new Error(`WEBC atom '${name}' is not a file.`); + const fileStart = readLength(headerView, entryOffset + 1); + const fileEnd = readLength(headerView, entryOffset + 9); + if (fileStart > fileEnd || fileEnd > data.byteLength) { + throw new Error(`WEBC atom '${name}' lies outside its data section.`); + } + return data.subarray(fileStart, fileEnd); + } + throw new Error(`The WEBC container has no '${name}' atom.`); +} + +function readLength(view: DataView, offset: number): number { + if (offset < 0 || offset + 8 > view.byteLength) throw new Error("The WEBC container is truncated."); + const value = view.getBigUint64(offset, true); + if (value > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error("The WEBC container has an invalid length."); + return Number(value); +} + +function checkedEnd(bytes: Uint8Array, start: number, length: number): number { + const end = start + length; + if (end > bytes.byteLength) throw new Error("The WEBC container is truncated."); + return end; +} diff --git a/src/runtime/compiler.worker.ts b/src/runtime/compiler.worker.ts index 79616bf..b21f5ca 100644 --- a/src/runtime/compiler.worker.ts +++ b/src/runtime/compiler.worker.ts @@ -1,7 +1,6 @@ /// -import { Runtime, init } from "@wasmer/sdk"; -import wasmerWasmUrl from "@wasmer/sdk/wasm?url"; +import type { Wasmer } from "@wasmer/sdk"; import { WASM_OJ_SCHEMAS } from "../core/contract"; import { sha256Hex } from "../core/hash"; import { @@ -56,20 +55,14 @@ import JavaStageWorkerUrl from "./java-stage.worker?worker&url"; import type { JavaCompileRequest, JavaCompileResult, JavaStageRequest } from "@/src/compiler/java-toolchain"; import { JAVA_COMPILE_TIMEOUT_MS } from "@/src/compiler/java-toolchain"; import { PersistentIsolatedStage } from "./isolated-stage"; -import { - createModuleWorker, - createModuleWorkerBootstrap, - type ModuleWorkerBootstrap, - moduleWorkerBaseUrl, -} from "./module-worker"; -import wasmerThreadWorkerUrl from "./wasmer-thread.worker?worker&url"; +import { createModuleWorker, moduleWorkerBaseUrl } from "./module-worker"; +import { createBrowserWasmer } from "./wasmer-sdk"; const scope: DedicatedWorkerGlobalScope = self as unknown as DedicatedWorkerGlobalScope; const workerBaseUrl = moduleWorkerBaseUrl(); let toolchainSources: readonly BrowserToolchainSource[] | undefined; -let runtime: Runtime | undefined; -let runtimeInitialization: Promise | undefined; -let wasmerThreadWorkerBootstrap: ModuleWorkerBootstrap | undefined; +let wasmer: Wasmer | undefined; +let wasmerInitialization: Promise | undefined; let rustStage: PersistentIsolatedStage | undefined; let goStage: PersistentIsolatedStage | undefined; let javaStage: PersistentIsolatedStage | undefined; @@ -194,9 +187,9 @@ function compileJava(request: JavaCompileRequest): Promise { function configureCompilerHost(): void { configureWasmerCompilerHost({ - getRuntime: () => { - if (!runtime) throw new Error("The compiler Wasmer runtime is not initialized for this language."); - return runtime; + getWasmer: () => { + if (!wasmer) throw new Error("The compiler Wasmer runtime is not initialized for this language."); + return wasmer; }, loadToolchainAsset, loadToolchainFile, @@ -231,29 +224,16 @@ function languageRequiresOuterRuntime(language: string): boolean { } async function ensureOuterRuntime(requestId: string): Promise { - if (runtime) return; - runtimeInitialization ??= (async () => { + if (wasmer) return; + wasmerInitialization ??= (async () => { progress(requestId, "initializing", "Starting Wasmer compiler runtime", 0.1); - const bootstrap = createModuleWorkerBootstrap(new URL(wasmerThreadWorkerUrl, workerBaseUrl)); - wasmerThreadWorkerBootstrap = bootstrap; - try { - await init({ - log: "warn", - module: new URL(wasmerWasmUrl, workerBaseUrl), - workerUrl: bootstrap.url, - }); - runtime = new Runtime({ registry: null }); - } catch (error) { - if (wasmerThreadWorkerBootstrap === bootstrap) wasmerThreadWorkerBootstrap = undefined; - bootstrap.revoke(); - throw error; - } + wasmer = await createBrowserWasmer(); progress(requestId, "initializing", "Wasmer compiler runtime ready", 0.2); })(); try { - await runtimeInitialization; + await wasmerInitialization; } catch (error) { - runtimeInitialization = undefined; + wasmerInitialization = undefined; throw error; } } @@ -293,12 +273,11 @@ async function quiesce(): Promise { await clangBuildGraphPersistence.persistIfDirty(); await disposeSdkDirectClangToolchain(); } finally { + // The Worker closes after quiescing, which ends the SDK's nested Workers; + // closing the client here would terminate them at arbitrary points. clearCompilerHostCaches(); - runtime?.free(); - runtime = undefined; - runtimeInitialization = undefined; - wasmerThreadWorkerBootstrap?.revoke(); - wasmerThreadWorkerBootstrap = undefined; + wasmer = undefined; + wasmerInitialization = undefined; } } } diff --git a/src/runtime/module-worker.test.ts b/src/runtime/module-worker.test.ts index 36be42a..6ea9a37 100644 --- a/src/runtime/module-worker.test.ts +++ b/src/runtime/module-worker.test.ts @@ -82,13 +82,13 @@ describe("module Worker bootstrap", () => { }); it("keeps a reusable bootstrap alive until its owner explicitly revokes it", async () => { - const bootstrap = createModuleWorkerBootstrap("/assets/wasmer-thread.worker.js"); + const bootstrap = createModuleWorkerBootstrap("/assets/wasmer-sdk/dist/browser-worker.js"); expect(bootstrap.url).toBe("blob:https://wasm-oj.example/bootstrap"); expect(URL.revokeObjectURL).not.toHaveBeenCalled(); const source = vi.mocked(URL.createObjectURL).mock.calls[0]?.[0]; expect(await (source as Blob).text()).toContain( - 'await import("https://wasm-oj.example/assets/wasmer-thread.worker.js")', + 'await import("https://wasm-oj.example/assets/wasmer-sdk/dist/browser-worker.js")', ); bootstrap.revoke(); diff --git a/src/runtime/mounted-output-stability.test.ts b/src/runtime/mounted-output-stability.test.ts deleted file mode 100644 index ce0e311..0000000 --- a/src/runtime/mounted-output-stability.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { MountedOutputStabilityObserver } from "./mounted-output-stability"; - -describe("MountedOutputStabilityObserver", () => { - it("returns a later copy only after identical bytes span the full interval", () => { - const observer = new MountedOutputStabilityObserver(75); - const initial = Uint8Array.of(0, 97, 115, 109, 1); - - expect(observer.observe(initial, 100)).toBeUndefined(); - expect(observer.observe(initial.slice(), 174.9)).toBeUndefined(); - const acceptedSnapshot = initial.slice(); - const result = observer.observe(acceptedSnapshot, 175); - - expect(result).toEqual(initial); - expect(result).not.toBe(initial); - expect(result).not.toBe(acceptedSnapshot); - }); - - it("restarts the interval when the mounted bytes change", () => { - const observer = new MountedOutputStabilityObserver(50); - - expect(observer.observe(Uint8Array.of(1, 2), 0)).toBeUndefined(); - expect(observer.observe(Uint8Array.of(1, 2, 3), 50)).toBeUndefined(); - expect(observer.observe(Uint8Array.of(1, 2, 3), 99)).toBeUndefined(); - expect(observer.observe(Uint8Array.of(1, 2, 3), 100)).toEqual(Uint8Array.of(1, 2, 3)); - }); - - it("restarts the interval after a missing or unreadable snapshot", () => { - const observer = new MountedOutputStabilityObserver(25); - - expect(observer.observe(Uint8Array.of(1), 0)).toBeUndefined(); - expect(observer.observe(undefined, 25)).toBeUndefined(); - expect(observer.observe(Uint8Array.of(1), 26)).toBeUndefined(); - expect(observer.observe(Uint8Array.of(1), 51)).toEqual(Uint8Array.of(1)); - }); - - it("rejects invalid intervals and timestamps", () => { - expect(() => new MountedOutputStabilityObserver(0)).toThrow(/positive and finite/); - const observer = new MountedOutputStabilityObserver(); - expect(() => observer.observe(Uint8Array.of(1), Number.NaN)).toThrow(/time must be finite/); - }); -}); diff --git a/src/runtime/mounted-output-stability.ts b/src/runtime/mounted-output-stability.ts deleted file mode 100644 index c251ed6..0000000 --- a/src/runtime/mounted-output-stability.ts +++ /dev/null @@ -1,45 +0,0 @@ -export const MOUNTED_OUTPUT_STABILITY_MS = 75; - -/** - * Accept a mounted output only after byte-identical snapshots have persisted for - * the complete stability interval. This prevents a valid WebAssembly prefix - * from being mistaken for the command's final output. - */ -export class MountedOutputStabilityObserver { - readonly #stabilityMs: number; - #candidate: Uint8Array | undefined; - #candidateSince = 0; - - constructor(stabilityMs = MOUNTED_OUTPUT_STABILITY_MS) { - if (!Number.isFinite(stabilityMs) || stabilityMs <= 0) { - throw new RangeError("Mounted-output stability interval must be positive and finite."); - } - this.#stabilityMs = stabilityMs; - } - - observe(snapshot: Uint8Array | undefined, monotonicMs: number): Uint8Array | undefined { - if (!Number.isFinite(monotonicMs)) { - throw new RangeError("Mounted-output observation time must be finite."); - } - if (!snapshot) { - this.#candidate = undefined; - this.#candidateSince = 0; - return undefined; - } - if (!this.#candidate || !bytesEqual(this.#candidate, snapshot)) { - this.#candidate = snapshot.slice(); - this.#candidateSince = monotonicMs; - return undefined; - } - if (monotonicMs - this.#candidateSince < this.#stabilityMs) return undefined; - return snapshot.slice(); - } -} - -function bytesEqual(left: Uint8Array, right: Uint8Array): boolean { - if (left.byteLength !== right.byteLength) return false; - for (let index = 0; index < left.byteLength; index += 1) { - if (left[index] !== right[index]) return false; - } - return true; -} diff --git a/src/runtime/runner.worker.ts b/src/runtime/runner.worker.ts index 6041f9b..5ba07c0 100644 --- a/src/runtime/runner.worker.ts +++ b/src/runtime/runner.worker.ts @@ -1,10 +1,10 @@ /// -import { Runtime, Wasmer, init } from "@wasmer/sdk"; -import wasmerWasmUrl from "@wasmer/sdk/wasm?url"; +import type { Wasmer } from "@wasmer/sdk"; import { WASM_OJ_STORAGE } from "@/src/core/contract"; import { sha256Hex } from "@/src/core/hash"; import { + PYTHON_COMMAND_SHA256, PYTHON_COMPRESSED_PACKAGE_SHA256, PYTHON_PACKAGE, PYTHON_PACKAGE_ASSET_PATH, @@ -46,34 +46,25 @@ import { openOptionalRuntimeFilesCache, restoreOrExportRuntimeFiles, } from "@/src/runtime/runtime-files-cache"; -import { - PackageHandleCache, - WasmerPackageHandle, - withHandleLease, - withWasmerCommand, -} from "@/src/runner/package-handle-cache"; +import { readWebcAtom } from "@/src/runner/webc"; import initRuntimeCore, { interact_wasm_oj as interactWasmOjCore, run_wasm_oj as runWasmOjCore, } from "@/src/runner/generated/runtime-core.js"; import runtimeCoreWasmUrl from "@/src/runner/generated/runtime-core_bg.wasm?url"; -import { - createModuleWorkerBootstrap, - type ModuleWorkerBootstrap, - moduleWorkerBaseUrl, -} from "./module-worker"; -import wasmerThreadWorkerUrl from "./wasmer-thread.worker?worker&url"; +import { moduleWorkerBaseUrl } from "./module-worker"; +import { createBrowserWasmer } from "./wasmer-sdk"; import { loadBrowserRuntimeDriverPlugins } from "./browser-runtime-plugin"; import type { BrowserRuntimeDriverPlugin } from "@/src/core/types"; const scope: DedicatedWorkerGlobalScope = self as unknown as DedicatedWorkerGlobalScope; const workerBaseUrl = moduleWorkerBaseUrl(); const decoder = new TextDecoder(); -const packages = new PackageHandleCache(); +const RUNTIME_FILES_OUTPUT_LIMIT_BYTES = 256 * 1024 * 1024; const packageFileSystems = new Map>>(); -let sdkRuntime: Runtime | undefined; -let sdkRuntimeInitialization: Promise | undefined; -let wasmerThreadWorkerBootstrap: ModuleWorkerBootstrap | undefined; +let sdkClient: Promise | undefined; +let pythonPackageBytes: Promise | undefined; +let pythonCommand: Promise | undefined; let runtimeDrivers: RuntimeDriverRegistry | undefined; let quickJsBytes: Promise | undefined; let toolchainSources: readonly BrowserToolchainSource[] | undefined; @@ -163,32 +154,15 @@ async function initializeRuntime( progress(requestId, "initializing", "Deterministic Wasmer runner ready", 1); } -async function ensurePackageRuntime(): Promise { - if (sdkRuntime) return sdkRuntime; - sdkRuntimeInitialization ??= (async () => { - const bootstrap = createModuleWorkerBootstrap(new URL(wasmerThreadWorkerUrl, workerBaseUrl)); - wasmerThreadWorkerBootstrap = bootstrap; - try { - await init({ - log: "warn", - module: new URL(wasmerWasmUrl, workerBaseUrl), - workerUrl: bootstrap.url, - }); - const initialized = new Runtime({ registry: null }); - sdkRuntime = initialized; - return initialized; - } catch (error) { - if (wasmerThreadWorkerBootstrap === bootstrap) wasmerThreadWorkerBootstrap = undefined; - bootstrap.revoke(); - throw error; - } - })(); - try { - return await sdkRuntimeInitialization; - } catch (error) { - sdkRuntimeInitialization = undefined; - throw error; - } +function ensureSdkClient(): Promise { + const cached = sdkClient; + if (cached) return cached; + const pending = createBrowserWasmer({ outputBytes: RUNTIME_FILES_OUTPUT_LIMIT_BYTES }); + sdkClient = pending; + void pending.catch(() => { + if (sdkClient === pending) sdkClient = undefined; + }); + return pending; } function toolchainAssetUrl(path: string): URL { @@ -232,22 +206,44 @@ async function loadCompressedAsset( return bytes; } -function acquirePackage(specifier: string) { +function loadPinnedPackage(specifier: string): Promise { if (specifier !== PYTHON_PACKAGE) { throw new Error(`No pinned WASM-OJ runtime package is declared for '${specifier}'.`); } - return packages.acquire(specifier, async () => { - const [bytes, runtime] = await Promise.all([ - loadCompressedAsset( - PYTHON_PACKAGE_ASSET_PATH, - "Python/WASI package", - PYTHON_COMPRESSED_PACKAGE_SHA256, - PYTHON_PACKAGE_SHA256, - ), - ensurePackageRuntime(), - ]); - return new WasmerPackageHandle(await Wasmer.fromFile(bytes, runtime)); + const cached = pythonPackageBytes; + if (cached) return cached; + const pending = loadCompressedAsset( + PYTHON_PACKAGE_ASSET_PATH, + "Python/WASI package", + PYTHON_COMPRESSED_PACKAGE_SHA256, + PYTHON_PACKAGE_SHA256, + ); + pythonPackageBytes = pending; + void pending.catch(() => { + if (pythonPackageBytes === pending) pythonPackageBytes = undefined; + }); + return pending; +} + +function loadPackageCommand(specifier: string, command: string): Promise { + if (specifier !== PYTHON_PACKAGE || command !== "python") { + throw new Error(`No pinned WASM-OJ runtime command is declared for '${specifier}:${command}'.`); + } + const cached = pythonCommand; + if (cached) return cached; + const pending = (async () => { + const bytes = readWebcAtom(await loadPinnedPackage(specifier), command).slice(); + const digest = await sha256Hex(bytes); + if (digest !== PYTHON_COMMAND_SHA256) { + throw new Error(`Pinned ${specifier} command '${command}' has digest ${digest}; expected ${PYTHON_COMMAND_SHA256}.`); + } + return bytes; + })(); + pythonCommand = pending; + void pending.catch(() => { + if (pythonCommand === pending) pythonCommand = undefined; }); + return pending; } function runtimeFilesCacheRequest(request: PackageFileSystemRequest): Request { @@ -274,27 +270,27 @@ async function exportPackageFileSystem( expectedSha256: request.expectedSha256, }, async () => { - const lease = await acquirePackage(request.packageSpecifier); - const output = await withHandleLease( - lease, - (pkg) => withWasmerCommand(pkg, request.command, async (command) => { - const instance = await command.run({ - args: request.args, - env: { - PYTHONHOME: "/usr/local", - PYTHONHASHSEED: "0", - PYTHONDONTWRITEBYTECODE: "1", - }, - }); - return instance.wait(); - }), - ); - if (!output.ok) { + const [packageBytes, wasmer] = await Promise.all([ + loadPinnedPackage(request.packageSpecifier), + ensureSdkClient(), + ]); + const sandbox = await wasmer.sandboxes.create({ + packages: [await wasmer.packages.load(packageBytes)], + env: { + PYTHONHOME: "/usr/local", + PYTHONHASHSEED: "0", + PYTHONDONTWRITEBYTECODE: "1", + }, + }); + const output = await sandbox.command(request.command, request.args).run({ check: false }); + await sandbox.close(); + if (!output.ok || output.stdout.truncated) { throw new Error( - `Unable to export runtime files from ${request.packageSpecifier}: exit ${output.code}: ${output.stderr}`, + `Unable to export runtime files from ${request.packageSpecifier}: ` + + `${output.reason} ${output.exitCode}: ${output.stderr.text()}`, ); } - return output.stdoutBytes.slice(); + return output.stdout.bytes; }, ); })(); @@ -327,20 +323,16 @@ const resolver: RuntimeResolver = { return loadQuickJs(); }, async packageCommand(packageSpecifier, commandName) { - const lease = await acquirePackage(packageSpecifier); - return withHandleLease( - lease, - (pkg) => withWasmerCommand(pkg, commandName, (command) => command.binary()), - ); + return (await loadPackageCommand(packageSpecifier, commandName)).slice(); }, packageFileSystem: exportPackageFileSystem, }; async function clearRuntimeCaches(): Promise { - const packageRetirement = packages.retireAll(); packageFileSystems.clear(); quickJsBytes = undefined; - await packageRetirement.wait(); + pythonPackageBytes = undefined; + pythonCommand = undefined; await caches.delete(WASM_OJ_STORAGE.runtimeFilesCache); } diff --git a/src/runtime/rustc-stage.worker.ts b/src/runtime/rustc-stage.worker.ts index 3fe2381..2658440 100644 --- a/src/runtime/rustc-stage.worker.ts +++ b/src/runtime/rustc-stage.worker.ts @@ -1,11 +1,14 @@ /// -import { Directory, Runtime, Wasmer, init, type Command, type Instance } from "@wasmer/sdk"; -import wasmerWasmUrl from "@wasmer/sdk/wasm?url"; +import type { Package, Wasmer } from "@wasmer/sdk"; import { instantiateRustLinkerArguments, RUST_FINAL_OUTPUT_PATH, RUST_LINKER_COMMAND, RUST_OBJECT_PATH } from "../compiler/rust-linker"; -import { isLlvmBitcode, selectRustAllocatorBitcodeName } from "../compiler/rust-allocator-bitcode"; import { - RUST_COMPILE_TIMEOUT_MS, + RUST_GUEST_ROOT, + isRustArchive, + runRustStage, + type RustStageObservation, +} from "../compiler/rust-sandbox"; +import { RUST_TOOLCHAIN, decodeRustToolchainManifest, deterministicRustCompilerEnvironment, @@ -18,24 +21,17 @@ import { import { parseRustDiagnostics } from "../core/diagnostics"; import { sha256Hex } from "../core/hash"; import { contentAddressedToolchainAssetUrl } from "../core/toolchains"; -import { MountedOutputStabilityObserver } from "./mounted-output-stability"; -import { - createModuleWorkerBootstrap, - type ModuleWorkerBootstrap, - moduleWorkerBaseUrl, -} from "./module-worker"; +import { moduleWorkerBaseUrl } from "./module-worker"; import { OwnedWorkerRegistry, type WorkerConstructorHost } from "./owned-worker-registry"; -import wasmerThreadWorkerUrl from "./wasmer-thread.worker?worker&url"; +import { createBrowserWasmer } from "./wasmer-sdk"; const scope: DedicatedWorkerGlobalScope = self as unknown as DedicatedWorkerGlobalScope; const workerBaseUrl = moduleWorkerBaseUrl(); -const OUTPUT_QUIET_PERIOD_MS = 50; const NESTED_WORKER_RELEASE_GRACE_MS = 1_000; let requestTail = Promise.resolve(); let toolchain: Promise | undefined; let toolchainBaseUrl: string | undefined; let ownedWasmerWorkers: OwnedWorkerRegistry | undefined; -let wasmerThreadWorkerBootstrap: ModuleWorkerBootstrap | undefined; scope.addEventListener("message", (event: MessageEvent) => { requestTail = requestTail.then( @@ -48,33 +44,26 @@ async function compile(message: RustcStageRequest) { if (message.type !== "compile") throw new Error("Invalid rustc stage request."); const baseUrl = new URL(message.assetBaseUrl, workerBaseUrl); if (!baseUrl.pathname.endsWith("/")) baseUrl.pathname += "/"; - const { rustc, linker, manifest } = await loadToolchain(baseUrl); - let work: Directory | undefined; + const { wasmer, pkg, manifest } = await loadToolchain(baseUrl); + const sandbox = await wasmer.sandboxes.create({ + packages: [pkg], + files: Object.fromEntries(message.request.files.map((file) => [`${RUST_GUEST_ROOT}/${file.path}`, file.content])), + }); + const state = { abandoned: false }; try { - work = new Directory(Object.fromEntries( - message.request.files.map((file) => [`/${file.path}`, file.content]), - )); - await work.createDir("/build"); - await work.createDir("/build/deps"); + await sandbox.fs.mkdir(`${RUST_GUEST_ROOT}/build/deps`, { recursive: true }); let dependencyStdout = ""; let dependencyStderr = ""; for (const dependency of message.request.dependencies ?? []) { - const instance = await rustc.run({ + const output = await runRustStage(sandbox, state, { + command: "rustc", args: rustcDependencyArguments(dependency, message.request.optimization), env: deterministicRustCompilerEnvironment(), - mount: { "/work": work }, - cwd: "/work", + outputPath: dependency.outputPath, + stage: `rustc dependency ${dependency.id}`, + outputValidator: isRustArchive, }); - const output = await observeMountedOutput( - instance, - work, - dependency.outputPath, - `rustc dependency ${dependency.id}`, - (stderr) => parseRustDiagnostics(stderr).some((diagnostic) => diagnostic.severity === "error"), - false, - isRustArchive, - ); dependencyStdout += output.stdout; dependencyStderr += output.stderr; if (!output.success) { @@ -86,24 +75,18 @@ async function compile(message: RustcStageRequest) { }; } } - const rustcInstance = await rustc.run({ + const compiled = await runRustStage(sandbox, state, { + command: "rustc", args: rustcObjectArguments( message.request.entry, message.request.optimization, message.request.rootExterns, ), env: deterministicRustCompilerEnvironment(), - mount: { "/work": work }, - cwd: "/work", + outputPath: RUST_OBJECT_PATH, + stage: "rustc", + requiresAllocatorBitcode: true, }); - const compiled = await observeMountedOutput( - rustcInstance, - work, - RUST_OBJECT_PATH, - "rustc", - (stderr) => parseRustDiagnostics(stderr).some((diagnostic) => diagnostic.severity === "error"), - true, - ); const diagnostics = parseRustDiagnostics(`${dependencyStderr}${compiled.stderr}`); if (!compiled.success) { return { @@ -123,19 +106,13 @@ async function compile(message: RustcStageRequest) { if (objectIndex < 0) throw new Error("Pinned Rust linker arguments omit the submission object."); const libraries = [...(message.request.dependencies ?? [])].reverse().map((item) => item.outputPath); if (libraries.length > 0) linkerArguments.splice(objectIndex + 1, 0, ...libraries); - const linkerInstance = await linker.run({ + const linked = await runRustStage(sandbox, state, { + command: RUST_LINKER_COMMAND, args: linkerArguments, env: deterministicRustLinkerEnvironment(), - mount: { "/work": work }, - cwd: "/work", + outputPath: RUST_FINAL_OUTPUT_PATH, + stage: "wasm-ld", }); - const linked = await observeMountedOutput( - linkerInstance, - work, - RUST_FINAL_OUTPUT_PATH, - "wasm-ld", - (stderr) => /(?:wasm-ld|lld): error:/i.test(stderr), - ); return { success: linked.success && Boolean(linked.bytes), wasm: linked.bytes, @@ -144,15 +121,13 @@ async function compile(message: RustcStageRequest) { diagnostics, }; } finally { - work?.free(); + if (!state.abandoned) await sandbox.close(); } } interface RustStageToolchain { - runtime: Runtime; - pkg: Wasmer; - rustc: Command; - linker: Command; + wasmer: Wasmer; + pkg: Package; manifest: Awaited>; } @@ -180,37 +155,18 @@ async function shutdownToolchain(): Promise { toolchainBaseUrl = undefined; if (!pending) { terminateOwnedWasmerWorkers(); - disposeWasmerThreadWorkerBootstrap(); return; } - let loaded: RustStageToolchain; - try { - loaded = await pending; - } catch (error) { - terminateOwnedWasmerWorkers(); - disposeWasmerThreadWorkerBootstrap(); - throw error; - } - // Browser termination of a parent Worker does not run Rust Drop for SDK - // WorkerHandle values. End nested workers first, while their shared Wasmer - // memory is still valid. Chromium releases the backing thread resources - // asynchronously, so keep the owning SDK memory alive across the bounded - // release window before starting another generation. - terminateOwnedWasmerWorkers(); - await new Promise((resolve) => setTimeout(resolve, NESTED_WORKER_RELEASE_GRACE_MS)); try { - loaded.rustc.free(); + await pending; } finally { - try { - loaded.linker.free(); - } finally { - try { - loaded.pkg.free(); - } finally { - loaded.runtime.free(); - disposeWasmerThreadWorkerBootstrap(); - } - } + // Browser termination of a parent Worker does not run Rust Drop for SDK + // WorkerHandle values, and the SDK's own shutdown would terminate them at + // arbitrary points. End nested workers first, then keep the owning SDK + // memory alive across Chromium's asynchronous thread release before + // another generation starts. + terminateOwnedWasmerWorkers(); + await new Promise((resolve) => setTimeout(resolve, NESTED_WORKER_RELEASE_GRACE_MS)); } } @@ -228,48 +184,19 @@ async function initializeToolchain(baseUrl: URL): Promise { const workerRegistry = new OwnedWorkerRegistry(globalThis as unknown as WorkerConstructorHost); workerRegistry.install(); ownedWasmerWorkers = workerRegistry; - const bootstrap = createModuleWorkerBootstrap(new URL(wasmerThreadWorkerUrl, workerBaseUrl)); - wasmerThreadWorkerBootstrap = bootstrap; try { - await init({ - log: "warn", - module: new URL(wasmerWasmUrl, workerBaseUrl), - workerUrl: bootstrap.url, - }); - } catch (error) { - terminateOwnedWasmerWorkers(); - disposeWasmerThreadWorkerBootstrap(); - throw error; - } - let runtime: Runtime; - try { - runtime = new Runtime({ registry: null }); - } catch (error) { - terminateOwnedWasmerWorkers(); - disposeWasmerThreadWorkerBootstrap(); - throw error; - } - let pkg: Wasmer | undefined; - let rustc: Command | undefined; - let linker: Command | undefined; - try { - const [packageBytes, manifest] = await Promise.all([ + const [wasmer, packageBytes, manifest] = await Promise.all([ + createBrowserWasmer(), loadRustPackage(baseUrl), loadRustManifest(baseUrl), ]); - pkg = await Wasmer.fromFile(packageBytes, runtime); - rustc = pkg.commands.rustc; - if (!rustc) throw new Error("The pinned Rust WebC does not expose its rustc command."); - linker = pkg.commands[RUST_LINKER_COMMAND]; - if (!linker) throw new Error(`The pinned Rust WebC does not expose its ${RUST_LINKER_COMMAND} command.`); - return { runtime, pkg, rustc, linker, manifest }; + const pkg = await wasmer.packages.load(packageBytes); + for (const command of ["rustc", RUST_LINKER_COMMAND]) { + if (!pkg.commands.includes(command)) throw new Error(`The pinned Rust WebC does not expose its ${command} command.`); + } + return { wasmer, pkg, manifest }; } catch (error) { - rustc?.free(); - linker?.free(); - pkg?.free(); - runtime.free(); terminateOwnedWasmerWorkers(); - disposeWasmerThreadWorkerBootstrap(); throw error; } } @@ -280,156 +207,13 @@ function terminateOwnedWasmerWorkers(): void { registry?.terminateAll(); } -function disposeWasmerThreadWorkerBootstrap(): void { - wasmerThreadWorkerBootstrap?.revoke(); - wasmerThreadWorkerBootstrap = undefined; -} - -interface StageObservation { - success: boolean; - bytes?: Uint8Array; - allocatorBitcodePath?: string; - stdout: string; - stderr: string; -} - -async function observeMountedOutput( - instance: Instance, - work: Directory, - guestPath: string, - stage: string, - hasTerminalError: (stderr: string) => boolean, - requiresAllocatorBitcode = false, - outputValidator: (bytes: Uint8Array) => boolean = (bytes) => WebAssembly.validate(Uint8Array.from(bytes)), -): Promise { - const stdout = captureReadable(instance.stdout); - const stderr = captureReadable(instance.stderr); - const outputStability = new MountedOutputStabilityObserver(); - let allocatorStability = new MountedOutputStabilityObserver(); - let allocatorCandidatePath: string | undefined; - const deadline = performance.now() + RUST_COMPILE_TIMEOUT_MS; - try { - while (performance.now() < deadline) { - const stderrText = stderr.text(); - const quiet = stdout.quietFor(OUTPUT_QUIET_PERIOD_MS) && stderr.quietFor(OUTPUT_QUIET_PERIOD_MS); - const observedAt = performance.now(); - const bytes = outputStability.observe(await readValidOutput(work, guestPath, outputValidator), observedAt); - const allocator = requiresAllocatorBitcode ? await readRustAllocatorBitcode(work) : undefined; - if (allocator?.path !== allocatorCandidatePath) { - allocatorCandidatePath = allocator?.path; - allocatorStability = new MountedOutputStabilityObserver(); - } - const allocatorBytes = requiresAllocatorBitcode - ? allocatorStability.observe(allocator?.bytes, observedAt) - : undefined; - const allocatorReady = !requiresAllocatorBitcode || Boolean(allocatorBytes && allocatorCandidatePath); - if (bytes && allocatorReady && quiet) { - await new Promise((resolve) => setTimeout(resolve, 0)); - return { - success: true, - bytes, - allocatorBitcodePath: allocatorCandidatePath, - stdout: stdout.text(), - stderr: stderr.text(), - }; - } - if (quiet && hasTerminalError(stderrText)) { - return { success: false, stdout: stdout.text(), stderr: stderrText }; - } - await new Promise((resolve) => setTimeout(resolve, 5)); - } - throw new Error(`${stage} exceeded ${RUST_COMPILE_TIMEOUT_MS} ms.`); - } finally { - await Promise.all([stdout.cancel(), stderr.cancel()]); - instance.free(); - } -} - -function requireAllocatorBitcodePath(observation: StageObservation): string { +function requireAllocatorBitcodePath(observation: RustStageObservation): string { if (!observation.allocatorBitcodePath) { throw new Error("rustc completed without its allocator bitcode module."); } return observation.allocatorBitcodePath; } -function captureReadable(stream: ReadableStream): { - text(): string; - quietFor(milliseconds: number): boolean; - cancel(): Promise; -} { - const reader = stream.getReader(); - const chunks: Uint8Array[] = []; - let updatedAt = performance.now(); - void (async () => { - try { - while (true) { - const result = await reader.read(); - if (result.done) return; - const bytes = result.value instanceof Uint8Array - ? result.value - : new Uint8Array(result.value as ArrayBuffer); - chunks.push(bytes.slice()); - updatedAt = performance.now(); - } - } catch { - // Cancellation is expected once the complete mounted output is observed. - } - })(); - return { - text: () => new TextDecoder().decode(concatenate(chunks)), - quietFor: (milliseconds) => performance.now() - updatedAt >= milliseconds, - cancel: async () => { - try { - await reader.cancel(); - } catch { - // The guest may close the stream concurrently with mounted-output observation. - } - }, - }; -} - -function concatenate(chunks: readonly Uint8Array[]): Uint8Array { - const output = new Uint8Array(chunks.reduce((total, chunk) => total + chunk.byteLength, 0)); - let offset = 0; - for (const chunk of chunks) { - output.set(chunk, offset); - offset += chunk.byteLength; - } - return output; -} - -async function readValidOutput( - work: Directory, - guestPath: string, - validator: (bytes: Uint8Array) => boolean, -): Promise { - const mountRelativePath = guestPath.startsWith("/work/") ? guestPath.slice("/work".length) : guestPath; - try { - const bytes = (await work.readFile(mountRelativePath)).slice(); - return bytes.byteLength > 8 && validator(bytes) ? bytes : undefined; - } catch { - return undefined; - } -} - -function isRustArchive(bytes: Uint8Array): boolean { - return bytes.byteLength > 8 && new TextDecoder().decode(bytes.subarray(0, 8)) === "!\n"; -} - -async function readRustAllocatorBitcode( - work: Directory, -): Promise<{ path: string; bytes: Uint8Array } | undefined> { - try { - const name = selectRustAllocatorBitcodeName((await work.readDir("/build")).map((entry) => entry.name)); - if (!name) return undefined; - const bytes = (await work.readFile(`/build/${name}`)).slice(); - return isLlvmBitcode(bytes) ? { path: `/work/build/${name}`, bytes } : undefined; - } catch (error) { - if (error instanceof Error && error.message.startsWith("rustc emitted multiple")) throw error; - return undefined; - } -} - async function loadRustPackage(baseUrl: URL): Promise { const compressed = await loadVerifiedAssetResponse( baseUrl, diff --git a/src/runtime/wasmer-sdk-thread.d.ts b/src/runtime/wasmer-sdk-thread.d.ts deleted file mode 100644 index e989808..0000000 --- a/src/runtime/wasmer-sdk-thread.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import "@wasmer/sdk"; - -declare module "@wasmer/sdk" { - /** Internal worker primitive exported and used by the pinned official SDK worker entry. */ - export class ThreadPoolWorker { - constructor(id: number); - handle(message: unknown): Promise; - } -} diff --git a/src/runtime/wasmer-sdk.ts b/src/runtime/wasmer-sdk.ts new file mode 100644 index 0000000..2117338 --- /dev/null +++ b/src/runtime/wasmer-sdk.ts @@ -0,0 +1,38 @@ +import type { Wasmer, WasmerOptions } from "@wasmer/sdk"; +import { wasmerSdkBindingUrl, wasmerSdkEntryUrl } from "virtual:wasm-oj/wasmer-sdk"; +import { createModuleWorkerBootstrap, moduleWorkerBaseUrl } from "./module-worker"; + +type WasmerSdk = typeof import("@wasmer/sdk"); + +interface WasmerSdkBinding { + setWorkerUrl(url: string): void; +} + +let runtime: Promise<{ sdk: WasmerSdk; binding: WasmerSdkBinding; workerUrl: string }> | undefined; + +/** + * Create an SDK client from the shipped runtime files. The SDK's thread Workers + * start from a same-origin blob bootstrap, like every WASM-OJ module Worker, so + * hosts that omit COOP/COEP on static assets still run them. + */ +export async function createBrowserWasmer(options: WasmerOptions = {}): Promise { + runtime ??= (async () => { + const baseUrl = moduleWorkerBaseUrl(); + const entryUrl = new URL(wasmerSdkEntryUrl, baseUrl); + const [sdk, binding] = await Promise.all([ + import(/* @vite-ignore */ entryUrl.href) as Promise, + import(/* @vite-ignore */ new URL(wasmerSdkBindingUrl, baseUrl).href) as Promise, + ]); + return { sdk, binding, workerUrl: createModuleWorkerBootstrap(new URL("browser-worker.js", entryUrl)).url }; + })(); + const loaded = await runtime.catch((error: unknown) => { + runtime = undefined; + throw error; + }); + const wasmer = new loaded.sdk.Wasmer({ cache: false, ...options }); + await wasmer.ready(); + // Each client initialization resets the Worker URL to the SDK's own asset; + // Workers start lazily, so replacing it here precedes every spawn. + loaded.binding.setWorkerUrl(loaded.workerUrl); + return wasmer; +} diff --git a/src/runtime/wasmer-thread-policy.test.ts b/src/runtime/wasmer-thread-policy.test.ts deleted file mode 100644 index e0b7a34..0000000 --- a/src/runtime/wasmer-thread-policy.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - isWasmerThreadInitMessage, - WASMER_THREAD_STACK_SIZE_BYTES, -} from "./wasmer-thread-policy"; - -const EMPTY_WASM = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0]); - -describe("Wasmer secondary-worker policy", () => { - it("pins a positive WebAssembly-page-aligned stack", () => { - expect(WASMER_THREAD_STACK_SIZE_BYTES).toBe(1_048_576); - expect(WASMER_THREAD_STACK_SIZE_BYTES % 65_536).toBe(0); - }); - - it("accepts only a complete SDK initialization message", () => { - const message = { - type: "init", - id: 1, - memory: new WebAssembly.Memory({ initial: 1, maximum: 2, shared: true }), - module: new WebAssembly.Module(EMPTY_WASM), - sdkUrl: "https://wasm-oj.example/assets/wasmer-sdk.js", - }; - expect(isWasmerThreadInitMessage(message)).toBe(true); - expect(isWasmerThreadInitMessage({ ...message, id: -1 })).toBe(false); - expect(isWasmerThreadInitMessage({ ...message, id: 1.5 })).toBe(false); - expect(isWasmerThreadInitMessage({ ...message, id: 0x1_0000_0000 })).toBe(false); - expect(isWasmerThreadInitMessage({ ...message, memory: new ArrayBuffer(8) })).toBe(false); - expect(isWasmerThreadInitMessage({ ...message, module: EMPTY_WASM })).toBe(false); - expect(isWasmerThreadInitMessage({ ...message, sdkUrl: "" })).toBe(false); - }); -}); diff --git a/src/runtime/wasmer-thread-policy.ts b/src/runtime/wasmer-thread-policy.ts deleted file mode 100644 index eeaca43..0000000 --- a/src/runtime/wasmer-thread-policy.ts +++ /dev/null @@ -1,26 +0,0 @@ -export interface WasmerThreadInitMessage { - type: "init"; - id: number; - memory: WebAssembly.Memory; - module: WebAssembly.Module; - sdkUrl: string; -} - -// wasm-bindgen 0.2.101 defaults secondary instances to 2 MiB stacks. rustc -// schedules enough blocking WASIX tasks for those concurrent allocations to -// race memory growth in the SDK glue. One MiB is page-aligned and is the -// verified bound for WASM-OJ's pinned compiler/runtime workloads. -export const WASMER_THREAD_STACK_SIZE_BYTES = 1 << 20; - -export function isWasmerThreadInitMessage(value: unknown): value is WasmerThreadInitMessage { - if (typeof value !== "object" || value === null) return false; - const message = value as Record; - return message.type === "init" - && Number.isSafeInteger(message.id) - && (message.id as number) >= 0 - && (message.id as number) <= 0xffff_ffff - && message.memory instanceof WebAssembly.Memory - && message.module instanceof WebAssembly.Module - && typeof message.sdkUrl === "string" - && message.sdkUrl.length > 0; -} diff --git a/src/runtime/wasmer-thread.worker.ts b/src/runtime/wasmer-thread.worker.ts deleted file mode 100644 index f994abd..0000000 --- a/src/runtime/wasmer-thread.worker.ts +++ /dev/null @@ -1,52 +0,0 @@ -/// - -import { ThreadPoolWorker, initSync, setRegistry, setSDKUrl } from "@wasmer/sdk"; -import { - isWasmerThreadInitMessage, - WASMER_THREAD_STACK_SIZE_BYTES, - type WasmerThreadInitMessage, -} from "./wasmer-thread-policy"; - -const scope: DedicatedWorkerGlobalScope = self as unknown as DedicatedWorkerGlobalScope; -const pendingMessages: unknown[] = []; -let worker: ThreadPoolWorker | undefined; -let initialization: Promise | undefined; - -scope.addEventListener("message", (event: MessageEvent) => { - if (isWasmerThreadInitMessage(event.data)) { - if (initialization) { - reportFailure(new Error("A Wasmer thread worker received more than one initialization message.")); - return; - } - initialization = initialize(event.data); - void initialization.catch(reportFailure); - return; - } - if (!worker) { - pendingMessages.push(event.data); - return; - } - void worker.handle(event.data).catch(reportFailure); -}); - -async function initialize(message: WasmerThreadInitMessage): Promise { - setRegistry({}); - initSync({ - module: message.module, - memory: message.memory, - thread_stack_size: WASMER_THREAD_STACK_SIZE_BYTES, - }); - setSDKUrl(message.sdkUrl); - const initializedWorker = new ThreadPoolWorker(message.id); - worker = initializedWorker; - for (const pending of pendingMessages.splice(0)) await initializedWorker.handle(pending); -} - -function reportFailure(reason: unknown): void { - const error = reason instanceof Error ? reason : new Error(String(reason)); - queueMicrotask(() => { - throw error; - }); -} - -export {}; diff --git a/src/server/cancellation.integration.test.ts b/src/server/cancellation.integration.test.ts new file mode 100644 index 0000000..c8ae9d9 --- /dev/null +++ b/src/server/cancellation.integration.test.ts @@ -0,0 +1,127 @@ +import { execFileSync } from "node:child_process"; +import { mkdtemp, rm } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import type { BuildArtifact } from "../core/types"; +import { createEngine, type Engine } from "../sdk/engine"; +import type { CompileInput } from "../sdk/project"; +import { ServerCompiler } from "./server-compiler"; +import { ServerRunner } from "./server-runner"; +import { testToolchains } from "./test-toolchains.test-helper"; + +const enabled = process.env.WASM_OJ_RUN_CANCELLATION_STRESS === "1"; +const rounds = Number(process.env.WASM_OJ_CANCELLATION_ROUNDS ?? "4"); +const SETTLE_LIMIT_MS = 60_000; +const ENTRIES: Readonly> = { c: "main.c", cpp: "main.cpp", rust: "main.rs", python: "main.py" }; + +// Cancels every server path that runs a guest mid-flight: SDK compiler +// children, the SDK Python runtime-preparation child, and native runner +// processes. Cancellation must settle promptly and leave the engine usable. +describe.skipIf(!enabled)("server cancellation stress", () => { + let scratch: string; + let engine: Engine; + let cpuLoop: BuildArtifact; + let outputHeavy: BuildArtifact; + + const createServerEngine = (cacheDirectory: string) => createEngine({ + compiler: new ServerCompiler({ + compilerExecutable: path.resolve("crates/runtime-core/target/release/wasm-oj-compiler"), + toolchains: testToolchains(), + }), + runner: new ServerRunner({ + runtimeExecutable: path.resolve("crates/runtime-core/target/release/wasm-oj-runner"), + toolchains: testToolchains(), + cacheDirectory, + }), + }); + + beforeAll(async () => { + execFileSync("cargo", [ + "build", "--locked", "--manifest-path", "crates/runtime-core/Cargo.toml", "--release", + "--bin", "wasm-oj-runner", "--bin", "wasm-oj-compiler", + ], { stdio: "pipe" }); + scratch = await mkdtemp(path.join(os.tmpdir(), "wasm-oj-cancellation-")); + engine = await createServerEngine(path.join(scratch, "shared")); + cpuLoop = await compile(engine, "c", "int main(void){volatile unsigned long long x=0;for(;;)x++;}\n"); + outputHeavy = await compile( + engine, + "c", + "#include \nint main(void){for(unsigned long i=0;;i++)printf(\"%lu xxxxxxxxxxxxxxxxxxxxxxxx\\n\",i);}\n", + ); + }, 300_000); + + afterAll(async () => { + engine?.dispose(); + if (scratch) await rm(scratch, { recursive: true, force: true }); + }); + + it("settles every cancelled operation and keeps the engine usable", { timeout: 1_800_000 }, async () => { + const scenarios: Array<{ name: string; delay: [number, number]; fresh?: boolean; start(target: Engine): Promise }> = [ + { + name: "C++ compile", + delay: [100, 2_500], + start: (target) => target.compile(input("cpp", "#include \n#include \nint main(){std::map m; m[1]=2; std::cout< target.compile(input("rust", "fn main(){ let v: Vec = (0..64).collect(); println!(\"{}\", v.len()); }\n"), { cache: false }), + }, + { + name: "Python runtime preparation", + delay: [100, 2_000], + fresh: true, + start: async (target) => target.run(await compile(target, "python", "print(41 + 1)\n")), + }, + { name: "CPU-bound run", delay: [50, 1_500], start: (target) => target.run(cpuLoop) }, + { + name: "output-heavy run", + delay: [50, 1_500], + start: (target) => target.run(outputHeavy, { resources: { outputLimitBytes: 64 * 1024 * 1024 } }), + }, + ]; + for (let round = 0; round < rounds; round += 1) { + for (const scenario of scenarios) { + const target = scenario.fresh ? await createServerEngine(path.join(scratch, `fresh-${round}`)) : engine; + try { + const delay = scenario.delay[0] + Math.floor(Math.random() * (scenario.delay[1] - scenario.delay[0])); + const timer = setTimeout(() => target.cancel(), delay); + const settled = await Promise.race([ + scenario.start(target).then(() => "completed", () => "cancelled"), + new Promise((resolve) => setTimeout(() => resolve("hung"), SETTLE_LIMIT_MS)), + ]); + clearTimeout(timer); + expect(settled, `${scenario.name} cancelled after ${delay} ms`).not.toBe("hung"); + + const run = await target.run(await compile(target, "c", "#include \nint main(void){printf(\"%d\\n\", 40 + 2);return 0;}\n")); + expect(run).toMatchObject({ code: 0, stdout: "42\n", termination: "exited" }); + if (scenario.fresh) { + const python = await target.run(await compile(target, "python", "print(40 + 2)\n")); + expect(python).toMatchObject({ code: 0, stdout: "42\n", termination: "exited" }); + } + } finally { + if (target !== engine) target.dispose(); + } + } + } + }); +}); + +function input(language: CompileInput["language"], source: string): CompileInput { + const entry = ENTRIES[language]!; + return { + projectId: `cancellation:${language}:${Math.random()}`, + language, + target: "wasip1", + optimization: "release", + entry, + files: { [entry]: source }, + }; +} + +async function compile(engine: Engine, language: CompileInput["language"], source: string): Promise { + const result = await engine.compile(input(language, source), { cache: false }); + if (!result.success || !result.artifact) throw new Error(`Failed to compile ${language}: ${result.stderr}`); + return result.artifact; +} diff --git a/src/server/reusable-stage.test.ts b/src/server/reusable-stage.test.ts new file mode 100644 index 0000000..05c5d20 --- /dev/null +++ b/src/server/reusable-stage.test.ts @@ -0,0 +1,280 @@ +import { execFileSync, spawn } from "node:child_process"; +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; +import { serialize } from "node:v8"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { createSdkProject } from "../sdk/project"; +import { ReusableStage } from "./reusable-stage"; +import { ServerCompiler } from "./server-compiler"; +import { serializeServerToolchainSources } from "./toolchain-sources"; +import { testToolchains } from "./test-toolchains.test-helper"; + +const FAKE_STAGE = ` +import { writeFileSync } from "node:fs"; +import { createInterface } from "node:readline"; +process.stdin.once("end", () => process.exit(0)); +for await (const line of createInterface({ input: process.stdin })) { + if (line !== "hang") writeFileSync(3, \`\${process.pid}\\n\`); +} +`; + +describe("reusable server stage", () => { + let directory: string; + let script: string; + + beforeAll(async () => { + directory = await mkdtemp(path.join(os.tmpdir(), "wasm-oj-reusable-stage-")); + script = path.join(directory, "stage.mjs"); + await writeFile(script, FAKE_STAGE); + }); + + afterAll(async () => { + if (directory) await rm(directory, { recursive: true, force: true }); + }); + + const stage = (options: { maxExchanges?: number; idleTimeoutMs?: number } = {}) => new ReusableStage({ + script, + label: "test", + lineLimitBytes: 1024, + idleTimeoutMs: options.idleTimeoutMs ?? 60_000, + ...(options.maxExchanges === undefined ? {} : { maxExchanges: options.maxExchanges }), + }); + + it("serves sequential exchanges from one child and replaces it after the exchange limit", async () => { + const target = stage({ maxExchanges: 2 }); + try { + const first = await ask(target); + expect(await ask(target)).toBe(first); + await waitForExit(first); + const third = await ask(target); + expect(third).not.toBe(first); + expect(await ask(target)).toBe(third); + } finally { + target.terminate(); + } + }); + + it("never reuses a child after a failed, timed-out or terminated exchange", async () => { + const target = stage(); + try { + const failed = await ask(target); + await expect(target.run({ + request: "pid", + timeoutMs: 10_000, + timeoutMessage: "The test stage timed out.", + line: () => true, + complete: () => { + throw new Error("Invalid stage response."); + }, + })).rejects.toThrow("Invalid stage response."); + await waitForExit(failed); + + const timedOut = await ask(target); + expect(timedOut).not.toBe(failed); + await expect(ask(target, "hang", 200)).rejects.toThrow("The test stage timed out."); + await waitForExit(timedOut); + + const terminated = await ask(target); + expect(terminated).not.toBe(timedOut); + const pending = ask(target, "hang"); + target.terminate(); + await expect(pending).rejects.toThrow("The test stage exited before it answered."); + await waitForExit(terminated); + expect(await ask(target)).not.toBe(terminated); + } finally { + target.terminate(); + } + }); + + it("retires idle children without keeping the owning process alive", async () => { + const target = stage({ idleTimeoutMs: 100 }); + const idle = await ask(target); + await waitForExit(idle); + + const moduleUrl = pathToFileURL(path.resolve("src/server/reusable-stage.ts")).href; + const owner = spawn(process.execPath, [ + "--experimental-strip-types", + "--disable-warning=ExperimentalWarning", + "--input-type=module", + "--eval", + ` + import { ReusableStage } from ${JSON.stringify(moduleUrl)}; + const stage = new ReusableStage({ script: ${JSON.stringify(script)}, label: "test", lineLimitBytes: 1024, idleTimeoutMs: 60_000 }); + let pid = ""; + await stage.run({ request: "pid", timeoutMs: 10_000, timeoutMessage: "timed out", line: (line) => { pid = line; return true; }, complete: () => undefined }); + process.stdout.write(pid); + `, + ], { stdio: ["ignore", "pipe", "pipe"] }); + let stdout = ""; + owner.stdout.on("data", (chunk: Buffer) => stdout += chunk.toString()); + const started = Date.now(); + const code = await new Promise((resolve) => owner.once("close", resolve)); + expect(code).toBe(0); + expect(Date.now() - started).toBeLessThan(30_000); + await waitForExit(Number(stdout)); + }); +}); + +describe("server stage children", () => { + const rust = (value: number) => createSdkProject({ + language: "rust", + entry: "src/main.rs", + files: { "src/main.rs": `fn main(){ println!("{}", ${value}); }\n` }, + }); + + it("reuses the build child, replaces the rustc child after its build limit and discards cancelled children", { timeout: 300_000 }, async () => { + const existing = new Set(descendants(process.pid).map((child) => child.pid)); + const stages = (name: string) => descendants(process.pid) + .filter((child) => !existing.has(child.pid) && child.command.includes(name)) + .map((child) => child.pid); + const compiler = new ServerCompiler({ compilerExecutable: process.execPath, toolchains: testToolchains() }); + const build = async (value: number) => { + const result = await compiler.build(rust(value), `rust-${value}`); + expect(result.success, result.stderr).toBe(true); + }; + try { + await build(1); + const [buildStage] = stages("server-build-stage.mjs"); + const [rustc] = stages("rustc-stage.mjs"); + expect(buildStage).toBeDefined(); + expect(rustc).toBeDefined(); + + await build(2); + await waitForExit(rustc!); + expect(stages("rustc-stage.mjs")).toEqual([]); + + await build(3); + expect(stages("server-build-stage.mjs")).toEqual([buildStage]); + const [replacement] = stages("rustc-stage.mjs"); + expect(replacement).toBeDefined(); + expect(replacement).not.toBe(rustc); + + const compiling = new Promise((resolve) => { + const remove = compiler.onProgress((progress) => { + if (progress.phase !== "compiling") return; + remove(); + resolve(); + }); + }); + const cancelled = compiler.build(rust(4), "rust-4"); + cancelled.catch(() => undefined); + await compiling; + compiler.cancel(); + await expect(cancelled).rejects.toThrow("cancelled"); + await waitForExit(buildStage!); + await waitForExit(replacement!); + + await build(5); + const [next] = stages("server-build-stage.mjs"); + expect(next).toBeDefined(); + expect(next).not.toBe(buildStage); + } finally { + compiler.dispose(); + } + }); + + it.each(["idle", "busy"])("exits %s build and rustc children when their owner dies", { timeout: 300_000 }, async (state) => { + const transport = await mkdtemp(path.join(os.tmpdir(), "wasm-oj-stage-orphan-")); + try { + const requestPath = path.join(transport, "request.v8"); + await writeFile(requestPath, serialize({ + compilerExecutable: process.execPath, + stageDirectory: path.resolve("src/server"), + toolchains: serializeServerToolchainSources(testToolchains()), + verifiedToolchain: false, + project: rust(42), + cacheKey: "orphan", + })); + const owner = spawn(process.execPath, [ + "--input-type=module", + "--eval", + ` + import { spawn } from "node:child_process"; + const stage = spawn(process.execPath, ["--experimental-strip-types", "--disable-warning=ExperimentalWarning", ${JSON.stringify(path.resolve("src/server/server-build-stage.mjs"))}], { stdio: ["pipe", "ignore", "ignore", "pipe"] }); + stage.stdio[3].on("data", (chunk) => { + if (String(chunk).includes('"complete":true')) process.stdout.write("complete\\n"); + }); + stage.stdin.write(${JSON.stringify(JSON.stringify({ requestPath, responsePath: path.join(transport, "response.v8") }))} + "\\n"); + process.stdout.write(\`\${stage.pid}\\n\`); + setInterval(() => undefined, 60_000); + `, + ], { stdio: ["ignore", "pipe", "inherit"] }); + let stdout = ""; + owner.stdout.on("data", (chunk: Buffer) => stdout += chunk.toString()); + try { + await waitFor(() => (state === "idle" + ? stdout.includes("complete\n") + : descendants(owner.pid!).some((child) => child.command.includes("rustc-stage.mjs")))); + const children = descendants(owner.pid!).filter((child) => child.command.includes("-stage.mjs")); + expect(children.map((child) => path.basename(child.command.split(" ").at(-1)!)).sort()) + .toEqual(["rustc-stage.mjs", "server-build-stage.mjs"]); + owner.kill("SIGKILL"); + for (const child of children) await waitForExit(child.pid); + } finally { + owner.kill("SIGKILL"); + } + } finally { + await rm(transport, { recursive: true, force: true }); + } + }); +}); + +function ask(stage: ReusableStage, request = "pid", timeoutMs = 10_000): Promise { + let pid = 0; + return stage.run({ + request, + timeoutMs, + timeoutMessage: "The test stage timed out.", + line: (line) => { + pid = Number(line); + return true; + }, + complete: () => pid, + }); +} + +function descendants(root: number): Array<{ pid: number; command: string }> { + const processes = execFileSync("ps", ["-A", "-o", "pid=,ppid=,command="], { encoding: "utf8" }) + .split("\n") + .flatMap((row) => { + const match = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(row); + return match ? [{ pid: Number(match[1]), parent: Number(match[2]), command: match[3]! }] : []; + }); + const found: Array<{ pid: number; command: string }> = []; + const parents = [root]; + while (parents.length > 0) { + const parent = parents.pop(); + for (const child of processes.filter((entry) => entry.parent === parent)) { + found.push({ pid: child.pid, command: child.command }); + parents.push(child.pid); + } + } + return found; +} + +function running(pid: number): boolean { + try { + process.kill(pid, 0); + } catch { + return false; + } + try { + return !execFileSync("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8" }).trim().startsWith("Z"); + } catch { + return false; + } +} + +async function waitForExit(pid: number, timeoutMs = 10_000): Promise { + await waitFor(() => !running(pid), timeoutMs, `Process ${pid} did not exit within ${timeoutMs} ms.`); +} + +async function waitFor(condition: () => boolean, timeoutMs = 120_000, message = "Condition was not met."): Promise { + const deadline = Date.now() + timeoutMs; + while (!condition()) { + if (Date.now() > deadline) throw new Error(message); + await new Promise((resolve) => setTimeout(resolve, 50)); + } +} diff --git a/src/server/reusable-stage.ts b/src/server/reusable-stage.ts new file mode 100644 index 0000000..5996c2b --- /dev/null +++ b/src/server/reusable-stage.ts @@ -0,0 +1,245 @@ +import { spawn, type ChildProcess } from "node:child_process"; +import { BoundedByteCollector } from "./bounded-transport.ts"; + +const STAGE_LOG_LIMIT_BYTES = 1024 * 1024; + +export interface ReusableStageOptions { + script: string; + label: string; + /** Upper bound for one fd 3 protocol line. */ + lineLimitBytes: number; + idleTimeoutMs: number; + /** Retire the child after this many exchanges; unlimited when omitted. */ + maxExchanges?: number; + /** SIGTERM grace before SIGKILL when the owner terminates its stages. */ + terminationGraceMs?: number; + env?: NodeJS.ProcessEnv; +} + +export interface StageExchangeOutput { + stdout: string; + stderr: string; +} + +export interface StageExchange { + /** One protocol line written to the child's stdin. */ + request: string; + timeoutMs: number; + timeoutMessage: string; + /** Handles one fd 3 line and returns true once the response has arrived. */ + line(line: string): boolean; + /** Validates the response; a rejection discards the child. */ + complete(output: StageExchangeOutput): T | Promise; +} + +interface PendingExchange { + line(line: string): boolean; + resolve(): void; + reject(error: Error): void; + stdout: BoundedByteCollector; + stderr: BoundedByteCollector; +} + +/** + * Child process that serves sequential line-delimited requests on stdin and + * answers on fd 3. It exits by itself when its stdin reaches EOF, including + * when the owning process dies. + */ +class StageProcess { + readonly child: ChildProcess; + exchanges = 0; + exited = false; + private readonly options: ReusableStageOptions; + private pending: PendingExchange | undefined; + private buffered = ""; + + constructor(options: ReusableStageOptions) { + this.options = options; + this.child = spawn(process.execPath, [ + "--experimental-strip-types", + "--disable-warning=ExperimentalWarning", + options.script, + ], { + stdio: ["pipe", "pipe", "pipe", "pipe"], + ...(options.env === undefined ? {} : { env: options.env }), + }); + this.child.stdout?.on("data", (chunk: Buffer) => this.pending?.stdout.append(chunk)); + this.child.stderr?.on("data", (chunk: Buffer) => this.pending?.stderr.append(chunk)); + this.child.stdin?.on("error", (error) => this.fail(error)); + this.child.on("error", (error) => this.fail(error)); + this.child.on("close", () => { + this.exited = true; + const stderr = this.pending ? safeText(this.pending.stderr) || safeText(this.pending.stdout) : ""; + this.fail(new Error(stderr.trim() || `The ${options.label} stage exited before it answered.`)); + }); + const responses = this.child.stdio[3]; + if (!responses || typeof responses === "number" || !("on" in responses)) { + this.child.kill("SIGKILL"); + throw new Error(`The ${options.label} stage did not expose its response channel.`); + } + responses.on("data", (chunk: Buffer) => this.receive(chunk)); + } + + exchange(request: string, line: (line: string) => boolean): Promise { + if (this.exited || this.pending) { + return Promise.reject(new Error(`The ${this.options.label} stage is not available.`)); + } + this.setReferenced(true); + return new Promise((resolve, reject) => { + const kill = (error: Error) => { + this.child.kill("SIGKILL"); + this.fail(error); + }; + const pending: PendingExchange = { + line, + stdout: new BoundedByteCollector(`The ${this.options.label} stage stdout`, STAGE_LOG_LIMIT_BYTES, kill), + stderr: new BoundedByteCollector(`The ${this.options.label} stage stderr`, STAGE_LOG_LIMIT_BYTES, kill), + resolve: () => { + this.pending = undefined; + resolve({ stdout: safeText(pending.stdout), stderr: safeText(pending.stderr) }); + }, + reject: (error) => { + this.pending = undefined; + reject(error); + }, + }; + this.pending = pending; + this.buffered = ""; + this.child.stdin?.write(`${request}\n`); + }); + } + + idle(): void { + this.setReferenced(false); + } + + retire(): void { + this.setReferenced(false); + this.child.stdin?.end(); + } + + kill(signal: NodeJS.Signals = "SIGKILL"): void { + if (!this.exited) this.child.kill(signal); + } + + private receive(chunk: Buffer): void { + if (!this.pending) return; + this.buffered += chunk.toString(); + const lines = this.buffered.split("\n"); + this.buffered = lines.pop() ?? ""; + if (Buffer.byteLength(this.buffered, "utf8") > this.options.lineLimitBytes) { + this.child.kill("SIGKILL"); + this.fail(new Error( + `The ${this.options.label} stage response exceeded the ${this.options.lineLimitBytes} byte line boundary.`, + )); + return; + } + for (const line of lines) { + const pending = this.pending; + if (!pending) return; + try { + if (line && pending.line(line)) pending.resolve(); + } catch (error) { + this.child.kill("SIGKILL"); + this.fail(error instanceof Error ? error : new Error(String(error))); + } + } + } + + private fail(error: Error): void { + this.pending?.reject(error); + } + + private setReferenced(referenced: boolean): void { + const handles = [this.child, this.child.stdin, this.child.stdout, this.child.stderr, this.child.stdio[3]]; + for (const handle of handles) { + const control = handle as { ref?: () => void; unref?: () => void } | null; + if (referenced) control?.ref?.(); + else control?.unref?.(); + } + } +} + +/** + * Keeps one stage child warm between sequential exchanges. A failed, timed-out + * or terminated exchange discards its child; idle children retire after a + * timeout and never keep the owning process alive. + */ +export class ReusableStage { + private warm: StageProcess | undefined; + private active: StageProcess | undefined; + private idleTimer: ReturnType | undefined; + private readonly options: ReusableStageOptions; + + constructor(options: ReusableStageOptions) { + this.options = options; + } + + async run(exchange: StageExchange): Promise { + if (this.active) throw new Error(`The ${this.options.label} stage serves one request at a time.`); + clearTimeout(this.idleTimer); + const stage = this.warm && !this.warm.exited ? this.warm : new StageProcess(this.options); + this.warm = undefined; + this.active = stage; + let timer: ReturnType | undefined; + try { + const output = await Promise.race([ + stage.exchange(exchange.request, exchange.line), + new Promise((_resolve, reject) => { + timer = setTimeout(() => reject(new Error(exchange.timeoutMessage)), exchange.timeoutMs); + }), + ]); + const value = await exchange.complete(output); + stage.exchanges += 1; + this.release(stage); + return value; + } catch (error) { + stage.kill(); + throw error; + } finally { + clearTimeout(timer); + if (this.active === stage) this.active = undefined; + } + } + + /** Discard every child; the next exchange starts a new one. */ + terminate(): void { + clearTimeout(this.idleTimer); + const stages = [this.active, this.warm].filter((stage): stage is StageProcess => stage !== undefined); + this.active = undefined; + this.warm = undefined; + for (const stage of stages) { + const grace = this.options.terminationGraceMs; + if (!grace) { + stage.kill(); + continue; + } + stage.kill("SIGTERM"); + setTimeout(() => stage.kill(), grace).unref(); + } + } + + private release(stage: StageProcess): void { + if (this.active !== stage || stage.exited) return; + if (this.options.maxExchanges !== undefined && stage.exchanges >= this.options.maxExchanges) { + stage.retire(); + return; + } + stage.idle(); + this.warm = stage; + this.idleTimer = setTimeout(() => { + if (this.warm !== stage) return; + this.warm = undefined; + stage.retire(); + }, this.options.idleTimeoutMs); + this.idleTimer.unref(); + } +} + +function safeText(collector: BoundedByteCollector): string { + try { + return collector.text(); + } catch { + return ""; + } +} diff --git a/src/server/rustc-stage.mjs b/src/server/rustc-stage.mjs index 6b672a1..cd19b53 100644 --- a/src/server/rustc-stage.mjs +++ b/src/server/rustc-stage.mjs @@ -2,17 +2,21 @@ import { createHash } from "node:crypto"; import { writeFileSync } from "node:fs"; import { readFile } from "node:fs/promises"; import path from "node:path"; +import { createInterface } from "node:readline"; import { gunzipSync } from "node:zlib"; -import { Directory, init, Runtime, Wasmer } from "@wasmer/sdk/node"; +import { Wasmer } from "@wasmer/sdk/node"; import { RUST_FINAL_OUTPUT_PATH, RUST_LINKER_COMMAND, RUST_OBJECT_PATH, instantiateRustLinkerArguments, } from "../compiler/rust-linker.ts"; -import { isLlvmBitcode, selectRustAllocatorBitcodeName } from "../compiler/rust-allocator-bitcode.ts"; import { - RUST_COMPILE_TIMEOUT_MS, + RUST_GUEST_ROOT, + isRustArchive, + runRustStage, +} from "../compiler/rust-sandbox.ts"; +import { RUST_TOOLCHAIN, decodeRustToolchainManifest, deterministicRustCompilerEnvironment, @@ -20,95 +24,91 @@ import { rustcDependencyArguments, rustcObjectArguments, } from "../compiler/rust-toolchain.ts"; -import { MountedOutputStabilityObserver } from "../runtime/mounted-output-stability.ts"; +let toolchain; -const OUTPUT_QUIET_PERIOD_MS = 50; -let runtime; -let pkg; -let rustc; -let linker; -let work; -let exitCode = 0; +process.stdin.once("end", () => process.exit(0)); -try { - const encoded = JSON.parse(await readStdin()); - await init({ log: "warn" }); - runtime = new Runtime({ registry: null }); +for await (const line of createInterface({ input: process.stdin, crlfDelay: Infinity })) { + if (!line) continue; + let response; + try { + response = { ok: true, result: await compile(JSON.parse(line)) }; + } catch (error) { + response = { ok: false, error: error instanceof Error ? error.message : String(error) }; + } + writeFileSync(3, `${JSON.stringify(response)}\n`); +} + +async function loadToolchain(encoded) { + const packageAsset = requiredToolchainAsset(encoded, RUST_TOOLCHAIN.packageAsset); + const manifestAsset = requiredToolchainAsset(encoded, RUST_TOOLCHAIN.manifestAsset); + const identity = JSON.stringify([packageAsset, manifestAsset, encoded.verifiedToolchain === true]); + if (toolchain?.identity === identity) return toolchain; const [packageBytes, manifest] = await Promise.all([ - loadRustPackage( - requiredToolchainAsset(encoded, RUST_TOOLCHAIN.packageAsset), - encoded.verifiedToolchain === true, - ), - loadRustManifest( - requiredToolchainAsset(encoded, RUST_TOOLCHAIN.manifestAsset), - encoded.verifiedToolchain === true, - ), + loadRustPackage(packageAsset, encoded.verifiedToolchain === true), + loadRustManifest(manifestAsset, encoded.verifiedToolchain === true), ]); - pkg = await Wasmer.fromFile(packageBytes, runtime); - rustc = pkg.commands.rustc; - if (!rustc) throw new Error("The pinned Rust WebC does not expose its rustc command."); - linker = pkg.commands[RUST_LINKER_COMMAND]; - if (!linker) throw new Error(`The pinned Rust WebC does not expose its ${RUST_LINKER_COMMAND} command.`); + const wasmer = new Wasmer({ cache: false }); + const pkg = await wasmer.packages.load(packageBytes); + for (const command of ["rustc", RUST_LINKER_COMMAND]) { + if (!pkg.commands.includes(command)) throw new Error(`The pinned Rust WebC does not expose its ${command} command.`); + } + toolchain = { identity, wasmer, pkg, manifest }; + return toolchain; +} - work = new Directory(Object.fromEntries( - encoded.request.files.map((file) => [`/${file.path}`, file.content]), - )); - await work.createDir("/build"); - await work.createDir("/build/deps"); - let dependencyStdout = ""; - let dependencyStderr = ""; - let dependencyFailed = false; - for (const dependency of encoded.request.dependencies ?? []) { - const output = await runObservedStage({ - command: rustc, - args: rustcDependencyArguments(dependency, encoded.request.optimization), +async function compile(encoded) { + const { wasmer, pkg, manifest } = await loadToolchain(encoded); + const sandbox = await wasmer.sandboxes.create({ + packages: [pkg], + files: Object.fromEntries(encoded.request.files.map((file) => [`${RUST_GUEST_ROOT}/${file.path}`, file.content])), + }); + const state = { abandoned: false }; + try { + await sandbox.fs.mkdir(`${RUST_GUEST_ROOT}/build/deps`, { recursive: true }); + let dependencyStdout = ""; + let dependencyStderr = ""; + for (const dependency of encoded.request.dependencies ?? []) { + const output = await runRustStage(sandbox, state, { + command: "rustc", + args: rustcDependencyArguments(dependency, encoded.request.optimization), + env: deterministicRustCompilerEnvironment(), + outputPath: dependency.outputPath, + stage: `rustc dependency ${dependency.id}`, + outputValidator: isRustArchive, + }); + dependencyStdout += output.stdout; + dependencyStderr += output.stderr; + if (!output.success) { + return { + success: false, + stdout: dependencyStdout, + stderr: dependencyStderr, + diagnostics: parseRustDiagnostics(dependencyStderr, encoded.request.entry), + }; + } + } + const compiled = await runRustStage(sandbox, state, { + command: "rustc", + args: rustcObjectArguments( + encoded.request.entry, + encoded.request.optimization, + encoded.request.rootExterns, + ), env: deterministicRustCompilerEnvironment(), - work, - outputPath: dependency.outputPath, - stage: `rustc dependency ${dependency.id}`, - hasTerminalError: (stderr) => parseRustDiagnostics(stderr, encoded.request.entry) - .some((diagnostic) => diagnostic.severity === "error"), - outputValidator: isRustArchive, + outputPath: RUST_OBJECT_PATH, + stage: "rustc", + requiresAllocatorBitcode: true, }); - dependencyStdout += output.stdout; - dependencyStderr += output.stderr; - if (!output.success) { - dependencyFailed = true; - break; + const diagnostics = parseRustDiagnostics(`${dependencyStderr}${compiled.stderr}`, encoded.request.entry); + if (!compiled.success) { + return { + success: false, + stdout: `${dependencyStdout}${compiled.stdout}`, + stderr: `${dependencyStderr}${compiled.stderr}`, + diagnostics, + }; } - } - if (dependencyFailed) { - writeResult({ - success: false, - stdout: dependencyStdout, - stderr: dependencyStderr, - diagnostics: parseRustDiagnostics(dependencyStderr, encoded.request.entry), - }); - } else { - const compiled = await runObservedStage({ - command: rustc, - args: rustcObjectArguments( - encoded.request.entry, - encoded.request.optimization, - encoded.request.rootExterns, - ), - env: deterministicRustCompilerEnvironment(), - work, - outputPath: RUST_OBJECT_PATH, - stage: "rustc", - hasTerminalError: (stderr) => parseRustDiagnostics(stderr, encoded.request.entry) - .some((diagnostic) => diagnostic.severity === "error"), - requiresAllocatorBitcode: true, - }); - const diagnostics = parseRustDiagnostics(`${dependencyStderr}${compiled.stderr}`, encoded.request.entry); - if (!compiled.success) { - writeResult({ - success: false, - stdout: `${dependencyStdout}${compiled.stdout}`, - stderr: `${dependencyStderr}${compiled.stderr}`, - diagnostics, - }); - } else { const linkerArguments = instantiateRustLinkerArguments( manifest.linkerArguments, encoded.request.optimization, @@ -118,95 +118,22 @@ try { if (objectIndex < 0) throw new Error("Pinned Rust linker arguments omit the submission object."); const libraries = [...(encoded.request.dependencies ?? [])].reverse().map((item) => item.outputPath); if (libraries.length > 0) linkerArguments.splice(objectIndex + 1, 0, ...libraries); - const linked = await runObservedStage({ - command: linker, + const linked = await runRustStage(sandbox, state, { + command: RUST_LINKER_COMMAND, args: linkerArguments, env: deterministicRustLinkerEnvironment(), - work, outputPath: RUST_FINAL_OUTPUT_PATH, stage: "wasm-ld", - hasTerminalError: (stderr) => /(?:wasm-ld|lld): error:/i.test(stderr), }); - writeResult({ + return { success: linked.success && Boolean(linked.bytes), wasmBase64: linked.bytes ? Buffer.from(linked.bytes).toString("base64") : undefined, stdout: `${dependencyStdout}${compiled.stdout}${linked.stdout}`, stderr: `${dependencyStderr}${compiled.stderr}${linked.stderr}`, diagnostics, - }); - } - } -} catch (error) { - writeFileSync(3, JSON.stringify({ - ok: false, - error: error instanceof Error ? error.message : String(error), - })); - exitCode = 1; -} finally { - work?.free(); - rustc?.free(); - linker?.free(); - pkg?.free(); - runtime?.free(); - setTimeout(() => process.exit(exitCode), 10); -} - -function writeResult(result) { - writeFileSync(3, JSON.stringify({ ok: true, result })); -} - -async function runObservedStage({ - command, - args, - env, - work, - outputPath, - stage, - hasTerminalError, - requiresAllocatorBitcode = false, - outputValidator = (bytes) => WebAssembly.validate(bytes), -}) { - const instance = await command.run({ args, env, mount: { "/work": work }, cwd: "/work" }); - const stdout = captureReadable(instance.stdout); - const stderr = captureReadable(instance.stderr); - const outputStability = new MountedOutputStabilityObserver(); - let allocatorStability = new MountedOutputStabilityObserver(); - let allocatorCandidatePath; - const deadline = performance.now() + RUST_COMPILE_TIMEOUT_MS; - try { - while (performance.now() < deadline) { - const stderrText = stderr.text(); - const quiet = stdout.quietFor(OUTPUT_QUIET_PERIOD_MS) && stderr.quietFor(OUTPUT_QUIET_PERIOD_MS); - const observedAt = performance.now(); - const bytes = outputStability.observe(await readValidOutput(work, outputPath, outputValidator), observedAt); - const allocator = requiresAllocatorBitcode ? await readRustAllocatorBitcode(work) : undefined; - if (allocator?.path !== allocatorCandidatePath) { - allocatorCandidatePath = allocator?.path; - allocatorStability = new MountedOutputStabilityObserver(); - } - const allocatorBytes = requiresAllocatorBitcode - ? allocatorStability.observe(allocator?.bytes, observedAt) - : undefined; - const allocatorReady = !requiresAllocatorBitcode || Boolean(allocatorBytes && allocatorCandidatePath); - if (bytes && allocatorReady && quiet) { - await new Promise((resolve) => setTimeout(resolve, 0)); - return { - success: true, - bytes, - allocatorBitcodePath: allocatorCandidatePath, - stdout: stdout.text(), - stderr: stderr.text(), - }; - } - if (quiet && hasTerminalError(stderrText)) { - return { success: false, stdout: stdout.text(), stderr: stderrText }; - } - await new Promise((resolve) => setTimeout(resolve, 5)); - } - throw new Error(`${stage} exceeded ${RUST_COMPILE_TIMEOUT_MS} ms.`); + }; } finally { - await Promise.all([stdout.cancel(), stderr.cancel()]); - instance.free(); + if (!state.abandoned) await sandbox.close(); } } @@ -217,74 +144,6 @@ function requireAllocatorBitcodePath(observation) { return observation.allocatorBitcodePath; } -function captureReadable(stream) { - const reader = stream.getReader(); - const chunks = []; - let lastUpdate = performance.now(); - void (async () => { - try { - while (true) { - const result = await reader.read(); - if (result.done) return; - const bytes = result.value instanceof Uint8Array - ? result.value - : new Uint8Array(result.value); - chunks.push(bytes.slice()); - lastUpdate = performance.now(); - } - } catch { - // Cancellation is expected once the complete mounted output is observed. - } - })(); - return { - text: () => new TextDecoder().decode(concatenate(chunks)), - quietFor: (milliseconds) => performance.now() - lastUpdate >= milliseconds, - cancel: async () => { - try { - await reader.cancel(); - } catch { - // The guest may close the stream concurrently with mounted-output observation. - } - }, - }; -} - -function concatenate(chunks) { - const output = new Uint8Array(chunks.reduce((total, chunk) => total + chunk.byteLength, 0)); - let offset = 0; - for (const chunk of chunks) { - output.set(chunk, offset); - offset += chunk.byteLength; - } - return output; -} - -async function readValidOutput(work, guestPath, validator) { - const mountRelativePath = guestPath.startsWith("/work/") ? guestPath.slice("/work".length) : guestPath; - try { - const bytes = (await work.readFile(mountRelativePath)).slice(); - return bytes.byteLength > 8 && validator(bytes) ? bytes : undefined; - } catch { - return undefined; - } -} - -function isRustArchive(bytes) { - return bytes.byteLength > 8 && new TextDecoder().decode(bytes.subarray(0, 8)) === "!\n"; -} - -async function readRustAllocatorBitcode(work) { - try { - const name = selectRustAllocatorBitcodeName((await work.readDir("/build")).map((entry) => entry.name)); - if (!name) return undefined; - const bytes = (await work.readFile(`/build/${name}`)).slice(); - return isLlvmBitcode(bytes) ? { path: `/work/build/${name}`, bytes } : undefined; - } catch (error) { - if (error instanceof Error && error.message.startsWith("rustc emitted multiple")) throw error; - return undefined; - } -} - async function loadRustPackage(file, verifiedToolchain) { const compressed = await readFile(file); if (!verifiedToolchain) verifyDigest(file, compressed, RUST_TOOLCHAIN.packageCompressedSha256); @@ -318,12 +177,6 @@ function uint8View(bytes) { return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength); } -async function readStdin() { - const chunks = []; - for await (const chunk of process.stdin) chunks.push(chunk); - return Buffer.concat(chunks).toString("utf8"); -} - function parseRustDiagnostics(output, entry) { const diagnostics = []; for (const line of output.split(/\r?\n/)) { @@ -336,7 +189,7 @@ function parseRustDiagnostics(output, entry) { diagnostics.push({ severity: value.level === "warning" ? "warning" : value.level === "note" ? "info" : "error", message: value.message, - file: String(location?.file_name ?? entry).replace(/^\/work\//, ""), + file: String(location?.file_name ?? entry).replace(/^\/workspace\//, ""), line: Number(location?.line_start ?? 1), column: Number(location?.column_start ?? 1), endLine: location?.line_end, diff --git a/src/server/server-build-stage.mjs b/src/server/server-build-stage.mjs index dfba21e..977b7f8 100644 --- a/src/server/server-build-stage.mjs +++ b/src/server/server-build-stage.mjs @@ -1,47 +1,51 @@ import { writeFileSync } from "node:fs"; +import path from "node:path"; +import { createInterface } from "node:readline"; import { deserialize, serialize } from "node:v8"; -import { buildServerProjectInProcess } from "./server-compiler.ts"; +import { buildServerProjectInProcess, disposeServerBuildStage } from "./server-compiler.ts"; import { deserializeServerToolchainSources } from "./toolchain-sources.ts"; import { readBoundedRegularFile } from "./bounded-transport.ts"; -import { withProcessKeepalive } from "./process-keepalive.mjs"; const SERVER_BUILD_REQUEST_LIMIT_BYTES = 768 * 1024 * 1024; -try { - const responsePath = requiredResponsePath(); - const encoded = deserialize(await readBoundedRegularFile( - requiredRequestPath(), - SERVER_BUILD_REQUEST_LIMIT_BYTES, - )); - const result = await withProcessKeepalive(buildServerProjectInProcess( - { - compilerExecutable: encoded.compilerExecutable, - stageDirectory: encoded.stageDirectory, - toolchains: deserializeServerToolchainSources(encoded.toolchains), - verifiedToolchain: encoded.verifiedToolchain === true, - }, - encoded.project, - encoded.cacheKey, - (progress) => writeFileSync(3, `${JSON.stringify(progress)}\n`), - )); - writeFileSync(responsePath, serialize({ ok: true, result }), { flag: "wx" }); - setTimeout(() => process.exit(0), 10); -} catch (error) { - writeFileSync(requiredResponsePath(), serialize({ - ok: false, - error: error instanceof Error ? error.message : String(error), - }), { flag: "wx" }); - setTimeout(() => process.exit(1), 10); -} +process.stdin.once("end", () => process.exit(0)); +process.once("SIGTERM", () => { + disposeServerBuildStage(); + process.exit(1); +}); -function requiredResponsePath() { - const value = process.env.WASM_OJ_BUILD_RESPONSE; - if (!value) throw new Error("WASM_OJ_BUILD_RESPONSE is required."); - return value; +for await (const line of createInterface({ input: process.stdin, crlfDelay: Infinity })) { + if (!line) continue; + const { requestPath, responsePath } = parseTransport(line); + let response; + try { + const encoded = deserialize(await readBoundedRegularFile(requestPath, SERVER_BUILD_REQUEST_LIMIT_BYTES)); + const result = await buildServerProjectInProcess( + { + compilerExecutable: encoded.compilerExecutable, + stageDirectory: encoded.stageDirectory, + toolchains: deserializeServerToolchainSources(encoded.toolchains), + verifiedToolchain: encoded.verifiedToolchain === true, + }, + encoded.project, + encoded.cacheKey, + (progress) => writeFileSync(3, `${JSON.stringify(progress)}\n`), + ); + response = { ok: true, result }; + } catch (error) { + response = { ok: false, error: error instanceof Error ? error.message : String(error) }; + } + writeFileSync(responsePath, serialize(response), { flag: "wx" }); + writeFileSync(3, `${JSON.stringify({ complete: true })}\n`); } -function requiredRequestPath() { - const value = process.env.WASM_OJ_BUILD_REQUEST; - if (!value) throw new Error("WASM_OJ_BUILD_REQUEST is required."); +function parseTransport(line) { + const value = JSON.parse(line); + if ( + typeof value?.requestPath !== "string" || !path.isAbsolute(value.requestPath) + || typeof value.responsePath !== "string" || !path.isAbsolute(value.responsePath) + ) { + throw new Error("The isolated server compiler received an invalid transport request."); + } return value; } diff --git a/src/server/server-compiler.ts b/src/server/server-compiler.ts index f6862b1..9abb372 100644 --- a/src/server/server-compiler.ts +++ b/src/server/server-compiler.ts @@ -6,7 +6,7 @@ import os from "node:os"; import path from "node:path"; import { deserialize, serialize } from "node:v8"; import { gunzipSync } from "node:zlib"; -import { Runtime } from "@wasmer/sdk/node"; +import { Wasmer } from "@wasmer/sdk/node"; import type { Compiler } from "@wasm-oj/core"; import { assertCompilerCacheKey, @@ -21,7 +21,7 @@ import type { ServerToolchainSource, WorkerProgress, } from "@wasm-oj/contracts"; -import { clearSdkDirectClangCaches } from "../compiler/sdk-direct-clang.ts"; +import { clearSdkDirectClangBuildGraph, clearSdkDirectClangCaches } from "../compiler/sdk-direct-clang.ts"; import { buildProject, clearCompilerHostCaches, @@ -33,17 +33,21 @@ import type { RustCompileResult, } from "../compiler/rust-toolchain.ts"; import { RUST_COMPILE_TIMEOUT_MS, RUST_TOOLCHAIN } from "../compiler/rust-toolchain.ts"; +import { + MAX_OUTPUT_READY_RUST_STAGES_PER_WORKER, + OUTPUT_READY_RUST_STAGES_PER_BUILD, +} from "../compiler/browser-rust-policy.ts"; import type { GoCompileRequest, GoCompileResult } from "../compiler/go-toolchain.ts"; import { GO_COMPILE_TIMEOUT_MS, GO_TOOLCHAIN } from "../compiler/go-toolchain.ts"; import type { JavaCompileRequest, JavaCompileResult } from "../compiler/java-toolchain.ts"; import { JAVA_COMPILE_TIMEOUT_MS } from "../compiler/java-toolchain.ts"; -import { initializeServerWasmerSdk } from "./wasmer-runtime.ts"; import { JAVA_COMPILER_ASSET_PATH, JAVA_COMPILE_CLASSLIB_ASSET_PATH, JAVA_RUNTIME_CLASSLIB_ASSET_PATH, } from "../core/toolchains.ts"; import { BoundedByteCollector, readBoundedRegularFile } from "./bounded-transport.ts"; +import { ReusableStage } from "./reusable-stage.ts"; import { buildControlTimeoutMs } from "../compiler/build-timeout-policy.ts"; import { assertVerifiedToolchainDistribution, @@ -80,6 +84,8 @@ const SERVER_STAGE_PROGRESS_LINE_LIMIT_BYTES = 1024 * 1024; const SERVER_BUILD_RESPONSE_LIMIT_BYTES = 256 * 1024 * 1024; const SERVER_BUILD_REQUEST_LIMIT_BYTES = 768 * 1024 * 1024; const SERVER_COMPILER_STAGE_RESPONSE_LIMIT_BYTES = 256 * 1024 * 1024; +const SERVER_STAGE_IDLE_TIMEOUT_MS = 30_000; +const SERVER_RUST_BUILDS_PER_STAGE = MAX_OUTPUT_READY_RUST_STAGES_PER_WORKER / OUTPUT_READY_RUST_STAGES_PER_BUILD; interface ServerCompilerStageOptions extends ServerCompilerOptions { /** @internal Canonical stage root inherited from the package entry process. */ stageDirectory: string; @@ -107,6 +113,9 @@ export class ServerCompiler implements Compiler { private readonly stageDirectory: string; private readonly activeChildren = new Set>(); private activeOperation: ServerCompilerOperation | undefined; + private buildStage: ReusableStage | undefined; + private rustStage: ReusableStage | undefined; + private wasmer: Wasmer | undefined; constructor(options: ServerCompilerOptions); /** @internal */ @@ -168,9 +177,11 @@ export class ServerCompiler implements Compiler { await this.ready(); this.assertCurrent(operation, "Server compilation was cancelled before initialization completed."); if (!this.inProcess) return await this.buildIsolated(project, cacheKey, operation); - const runtime = new Runtime({ registry: null }); + // The in-process compiler runs only inside a stage child that serves sequential builds, + // so the client is never closed: SDK shutdown terminates workers mid-task. + const wasmer = this.wasmer ??= new Wasmer({ cache: false }); configureWasmerCompilerHost({ - getRuntime: () => runtime, + getWasmer: () => wasmer, loadToolchainAsset: (assetPath) => this.loadToolchainAsset(assetPath), loadToolchainFile: (assetPath) => this.loadToolchainFile(assetPath), compileRust: (request) => this.compileRust(request), @@ -188,8 +199,7 @@ export class ServerCompiler implements Compiler { this.assertCurrent(operation, "Server compilation was cancelled."); return result; } finally { - await clearSdkDirectClangCaches(); - runtime.free(); + clearSdkDirectClangBuildGraph(); } } finally { this.endOperation(operation); @@ -253,7 +263,6 @@ export class ServerCompiler implements Compiler { await Promise.all([ access(this.compilerExecutable, fsConstants.X_OK), ...serverToolchainDirectories(this.toolchains).map((directory) => access(directory, fsConstants.R_OK)), - this.inProcess ? initializeServerWasmerSdk() : Promise.resolve(), ]); } @@ -280,124 +289,62 @@ export class ServerCompiler implements Compiler { throw new Error(`Server compiler request exceeds ${SERVER_BUILD_REQUEST_LIMIT_BYTES} bytes.`); } await writeFile(requestPath, encodedRequest, { flag: "wx", mode: 0o600 }); - return await new Promise((resolve, reject) => { - const script = serverStageScript(this.stageDirectory, "server-build-stage.mjs"); - const child = spawn(process.execPath, [ - "--experimental-strip-types", - "--disable-warning=ExperimentalWarning", - script, - ], { - // Progress on fd 3 is best-effort. The artifact response uses a private - // one-shot file because Wasmer worker transports may claim inherited fds. - stdio: ["pipe", "pipe", "pipe", "pipe"], - env: { - ...process.env, - WASM_OJ_BUILD_REQUEST: requestPath, - WASM_OJ_BUILD_RESPONSE: responsePath, - }, - }); - this.activeChildren.add(child); - let progressBuffer = ""; - let timedOut = false; - let transportError: Error | undefined; - const failTransport = (error: Error) => { - transportError ??= error; - child.kill("SIGKILL"); - }; - const stdout = new BoundedByteCollector( - "Isolated server compiler stdout", - SERVER_STAGE_LOG_LIMIT_BYTES, - failTransport, - ); - const stderr = new BoundedByteCollector( - "Isolated server compiler stderr", - SERVER_STAGE_LOG_LIMIT_BYTES, - failTransport, - ); - child.stdout.on("data", (chunk: Buffer) => stdout.append(chunk)); - child.stderr.on("data", (chunk: Buffer) => stderr.append(chunk)); - child.on("error", (error) => { transportError = error; }); - child.stdin.on("error", (error) => { transportError ??= error; }); - const progressStream = child.stdio[3]; - if (!progressStream || typeof progressStream === "number") { - child.kill("SIGKILL"); - this.activeChildren.delete(child); - reject(new Error("The isolated server compiler did not expose its progress channel.")); - return; - } - progressStream.on("data", (chunk: Buffer) => { - if (Buffer.byteLength(progressBuffer, "utf8") + chunk.byteLength > SERVER_STAGE_PROGRESS_LINE_LIMIT_BYTES) { - failTransport(new Error( - `Isolated server compiler progress exceeded the ${SERVER_STAGE_PROGRESS_LINE_LIMIT_BYTES} byte line boundary.`, - )); - return; + this.buildStage ??= new ReusableStage({ + script: serverStageScript(this.stageDirectory, "server-build-stage.mjs"), + label: "isolated server compiler", + lineLimitBytes: SERVER_STAGE_PROGRESS_LINE_LIMIT_BYTES, + idleTimeoutMs: SERVER_STAGE_IDLE_TIMEOUT_MS, + terminationGraceMs: 1_000, + }); + return await this.buildStage.run({ + request: JSON.stringify({ requestPath, responsePath }), + timeoutMs, + timeoutMessage: `Server compilation exceeded ${timeoutMs} ms.`, + line: (line) => { + let message: { complete?: unknown }; + try { + message = JSON.parse(line) as { complete?: unknown }; + } catch { + // Compiler internals may write non-protocol data to fd 3. + return false; } - progressBuffer += chunk.toString(); - const lines = progressBuffer.split("\n"); - progressBuffer = lines.pop() ?? ""; - for (const line of lines) { - if (!line) continue; - try { - const progress = JSON.parse(line) as WorkerProgress; - if (this.isCurrent(operation)) { - for (const listener of this.progressListeners) listener(progress); - } - } catch { - // Compiler internals may write non-protocol data to fd 3. - } + if (message.complete === true) return true; + if (this.isCurrent(operation)) { + for (const listener of this.progressListeners) listener(message as WorkerProgress); } - }); - const timer = setTimeout(() => { - timedOut = true; - child.kill("SIGKILL"); - }, timeoutMs); - child.on("close", async () => { - clearTimeout(timer); - this.activeChildren.delete(child); + return false; + }, + complete: async ({ stdout, stderr }) => { + this.assertCurrent(operation, "Server compilation was cancelled."); + let encodedResponse: Buffer; try { - this.assertCurrent(operation, "Server compilation was cancelled."); - if (timedOut) throw new Error(`Server compilation exceeded ${timeoutMs} ms.`); - if (transportError) throw transportError; - let encodedResponse: Buffer; - try { - encodedResponse = await readBoundedRegularFile(responsePath, SERVER_BUILD_RESPONSE_LIMIT_BYTES); - } catch (error) { - const stageError = stderr.text().trim() || stdout.text().trim(); - if (stageError) throw new Error(stageError, { cause: error }); - throw error; - } - const response = deserialize(encodedResponse) as { - ok: boolean; - result?: BuildResult; - error?: string; - }; - if (!response.ok || !response.result) { - throw new Error( - response.error - || stderr.text() - || stdout.text() - || "The isolated server compiler failed.", - ); - } - resolve(response.result); + encodedResponse = await readBoundedRegularFile(responsePath, SERVER_BUILD_RESPONSE_LIMIT_BYTES); } catch (error) { - reject(error); + const stageError = stderr.trim() || stdout.trim(); + if (stageError) throw new Error(stageError, { cause: error }); + throw error; } - }); - child.stdin.end(); + const response = deserialize(encodedResponse) as { + ok: boolean; + result?: BuildResult; + error?: string; + }; + if (!response.ok || !response.result) { + throw new Error(response.error || stderr || stdout || "The isolated server compiler failed."); + } + return response.result; + }, }); + } catch (error) { + this.assertCurrent(operation, "Server compilation was cancelled."); + throw error; } finally { await rm(transportDirectory, { recursive: true, force: true }); } } private async compileRust(request: RustCompileRequest): Promise { - const result = await this.runCompilerStage & { wasmBase64?: string }>( - "rustc-stage.mjs", - { request }, - RUST_COMPILE_TIMEOUT_MS, - [RUST_TOOLCHAIN.packageAsset, RUST_TOOLCHAIN.manifestAsset], - ); + const result = await this.runRustStage & { wasmBase64?: string }>({ request }); return { ...result, diagnostics: parseRustDiagnostics(result.stderr), @@ -442,8 +389,45 @@ export class ServerCompiler implements Compiler { }; } + private runRustStage(input: object): Promise { + const operation = this.activeOperation; + if (!operation || operation.kind !== "build") { + return Promise.reject(new Error("Server compilation was cancelled before its compiler stage started.")); + } + this.assertCurrent(operation, "Server compilation was cancelled before its compiler stage started."); + const timeoutMs = RUST_COMPILE_TIMEOUT_MS + 5_000; + this.rustStage ??= new ReusableStage({ + script: serverStageScript(this.stageDirectory, "rustc-stage.mjs"), + label: "isolated compiler stage 'rustc-stage.mjs'", + lineLimitBytes: SERVER_COMPILER_STAGE_RESPONSE_LIMIT_BYTES, + idleTimeoutMs: SERVER_STAGE_IDLE_TIMEOUT_MS, + maxExchanges: SERVER_RUST_BUILDS_PER_STAGE, + }); + let responseLine = ""; + return this.rustStage.run({ + request: JSON.stringify({ + toolchainAssets: serverToolchainAssetFiles(this.toolchains, [RUST_TOOLCHAIN.packageAsset, RUST_TOOLCHAIN.manifestAsset]), + verifiedToolchain: this.verifiedToolchain, + ...input, + }), + timeoutMs, + timeoutMessage: `The isolated compiler stage 'rustc-stage.mjs' exceeded ${timeoutMs} ms.`, + line: (line) => { + responseLine = line; + return true; + }, + complete: ({ stdout, stderr }) => { + const response = JSON.parse(responseLine) as { ok: boolean; result?: T; error?: string }; + if (!response.ok || !response.result) { + throw new Error(response.error || stderr || stdout || "The isolated compiler stage 'rustc-stage.mjs' failed."); + } + return response.result; + }, + }); + } + private runCompilerStage( - scriptName: Exclude, + scriptName: Exclude, input: object, timeoutMs: number, assetPaths: readonly string[], @@ -574,6 +558,8 @@ export class ServerCompiler implements Compiler { } private terminateChildren(): void { + this.buildStage?.terminate(); + this.rustStage?.terminate(); for (const child of this.activeChildren) { if (this.inProcess) { child.kill("SIGKILL"); @@ -595,6 +581,8 @@ function uint8View(bytes: Buffer): Uint8Array { return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength); } +let stageCompiler: { identity: string; compiler: ServerCompiler } | undefined; + /** @internal Entry point used only by the isolated Node compiler process. */ export async function buildServerProjectInProcess( options: ServerCompilerStageOptions, @@ -602,15 +590,22 @@ export async function buildServerProjectInProcess( cacheKey: string, onProgress: (progress: WorkerProgress) => void, ): Promise { - const compiler = new ServerCompiler(options, IN_PROCESS_STAGE); + const identity = JSON.stringify(options); + if (stageCompiler?.identity !== identity) { + disposeServerBuildStage(); + stageCompiler = { identity, compiler: new ServerCompiler(options, IN_PROCESS_STAGE) }; + } + const { compiler } = stageCompiler; const removeProgress = compiler.onProgress(onProgress); - const terminate = () => compiler.dispose(); - process.once("SIGTERM", terminate); try { return await compiler.build(project, cacheKey); } finally { - process.off("SIGTERM", terminate); removeProgress(); - compiler.dispose(); } } + +/** @internal Releases the isolated compiler process's reusable compiler state. */ +export function disposeServerBuildStage(): void { + stageCompiler?.compiler.dispose(); + stageCompiler = undefined; +} diff --git a/src/server/server-runner-stage.mjs b/src/server/server-runner-stage.mjs index a3eb1ff..c7bd023 100644 --- a/src/server/server-runner-stage.mjs +++ b/src/server/server-runner-stage.mjs @@ -4,69 +4,56 @@ import { readFile } from "node:fs/promises"; import path from "node:path"; import { serialize } from "node:v8"; import { gunzipSync } from "node:zlib"; -import { init, Runtime, Wasmer } from "@wasmer/sdk/node"; +import { Wasmer } from "@wasmer/sdk/node"; import { withProcessKeepalive } from "./process-keepalive.mjs"; import { + PYTHON_COMMAND_SHA256, PYTHON_COMPRESSED_PACKAGE_SHA256, PYTHON_PACKAGE, PYTHON_PACKAGE_SHA256, } from "../core/toolchains.ts"; +import { readWebcAtom } from "../runner/webc.ts"; const MAX_INPUT_BYTES = 1024 * 1024; const MAX_RESULT_BYTES = 256 * 1024 * 1024; const MAX_ERROR_CHARACTERS = 64 * 1024; -let runtime; -let pkg; -let commands = []; let response; let exitCode = 0; try { const input = parseInput(JSON.parse(await readStdin())); - await withProcessKeepalive(init({ log: "warn" })); - runtime = new Runtime({ registry: null }); const packagePath = input.toolchainAsset; const compressed = await readFile(packagePath); if (!input.verifiedToolchain) verifyDigest(packagePath, compressed, PYTHON_COMPRESSED_PACKAGE_SHA256); const expanded = uint8View(gunzipSync(compressed)); if (!input.verifiedToolchain) verifyDigest(packagePath, expanded, PYTHON_PACKAGE_SHA256); - pkg = await withProcessKeepalive(Wasmer.fromFile(expanded, runtime)); - - const commandMap = pkg.commands; - commands = uniqueCommands(commandMap); - const command = commandMap[input.request.command]; - if (!command) { - throw new Error( - `Package '${input.request.packageSpecifier}' does not expose '${input.request.command}'.`, - ); - } let bytes; if (input.request.operation === "command-binary") { - bytes = command.binary().slice(); - if (!WebAssembly.validate(bytes)) { - throw new Error( - `Package '${input.request.packageSpecifier}' command '${input.request.command}' returned invalid WebAssembly.`, - ); - } + bytes = readWebcAtom(expanded, input.request.command).slice(); + verifyDigest(`${input.request.packageSpecifier} command '${input.request.command}'`, bytes, PYTHON_COMMAND_SHA256); } else { - const instance = await withProcessKeepalive(command.run({ - args: input.request.args, + const wasmer = new Wasmer({ cache: false, outputBytes: MAX_RESULT_BYTES }); + const pkg = await withProcessKeepalive(wasmer.packages.load(expanded)); + const sandbox = await withProcessKeepalive(wasmer.sandboxes.create({ + packages: [pkg], env: { PYTHONHOME: "/usr/local", PYTHONHASHSEED: "0", PYTHONDONTWRITEBYTECODE: "1", }, })); - const output = await withProcessKeepalive(instance.wait()); - if (!output.ok) { + const output = await withProcessKeepalive( + sandbox.command(input.request.command, input.request.args).run({ check: false }), + ); + if (!output.ok || output.stdout.truncated) { throw new Error( `Unable to export runtime files from ${input.request.packageSpecifier}: ` - + `exit ${output.code}: ${boundedText(output.stderr)}`, + + `${output.reason} ${output.exitCode}: ${boundedText(output.stderr.text())}`, ); } - bytes = output.stdoutBytes.slice(); + bytes = output.stdout.bytes; } if (bytes.byteLength > MAX_RESULT_BYTES) { @@ -82,33 +69,6 @@ try { response = { ok: false, error: boundedText(error instanceof Error ? error.message : String(error)) }; exitCode = 1; } finally { - const cleanupErrors = []; - for (const command of commands) { - try { - command.free(); - } catch (error) { - cleanupErrors.push(error); - } - } - try { - pkg?.free(); - } catch (error) { - cleanupErrors.push(error); - } - try { - runtime?.free(); - } catch (error) { - cleanupErrors.push(error); - } - if (cleanupErrors.length > 0) { - response = { - ok: false, - error: boundedText( - `Unable to release isolated Wasmer resources: ${cleanupErrors.map(errorText).join("; ")}`, - ), - }; - exitCode = 1; - } try { writeFileSync(requiredResponsePath(), serialize(response), { flag: "wx", @@ -169,14 +129,6 @@ function uint8View(bytes) { return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength); } -function uniqueCommands(commandMap) { - const unique = new Set(); - for (const command of Object.values(commandMap)) { - if (command && typeof command.free === "function") unique.add(command); - } - return [...unique]; -} - function requiredResponsePath() { const value = process.env.WASM_OJ_RUNNER_STAGE_RESPONSE; if (!value) throw new Error("WASM_OJ_RUNNER_STAGE_RESPONSE is required."); diff --git a/src/server/wasmer-runtime.ts b/src/server/wasmer-runtime.ts deleted file mode 100644 index f40b828..0000000 --- a/src/server/wasmer-runtime.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { init } from "@wasmer/sdk/node"; - -let initialization: Promise | undefined; - -export async function initializeServerWasmerSdk(): Promise { - initialization ??= init({ log: "error" }).then(() => undefined); - try { - await initialization; - } catch (error) { - initialization = undefined; - throw error; - } -} diff --git a/vite-env.d.ts b/vite-env.d.ts index 11f02fe..b2a4c9c 100644 --- a/vite-env.d.ts +++ b/vite-env.d.ts @@ -1 +1,8 @@ /// + +declare module "virtual:wasm-oj/wasmer-sdk" { + /** URL of the shipped `@wasmer/sdk` browser entry module. */ + export const wasmerSdkEntryUrl: string; + /** URL of the SDK's wasm-bindgen module, which owns its Worker URL setting. */ + export const wasmerSdkBindingUrl: string; +} diff --git a/vite.config.ts b/vite.config.ts index 19dd5cf..5070f7b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,6 +1,7 @@ import vinext from "vinext"; import { defineConfig, type UserConfig } from "vite"; import { sites } from "./build/sites-vite-plugin.ts"; +import { wasmerSdkRuntime } from "./scripts/wasmer-sdk-runtime.mjs"; const isCodexSeatbeltSandbox = process.env.CODEX_SANDBOX === "seatbelt"; @@ -24,10 +25,11 @@ export default defineConfig(async (): Promise => { modulePreload: { polyfill: false }, target: "es2022", }, - worker: { format: "es" as const }, + worker: { format: "es" as const, plugins: () => [wasmerSdkRuntime()] }, plugins: [ vinext(), sites(), + wasmerSdkRuntime(), cloudflare({ viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] }, configPath: "./wrangler.jsonc",