├── .github ├── actions │ ├── build-nix │ │ └── action.yml │ └── install-nix │ │ └── action.yml ├── dependabot.yml └── workflows │ ├── nix.yml │ └── wit-deps.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── ORG_CODE_OF_CONDUCT.md ├── README.md ├── crates └── wit-deps │ ├── Cargo.toml │ └── src │ ├── cache.rs │ ├── digest.rs │ ├── lib.rs │ ├── lock.rs │ └── manifest.rs ├── examples ├── github │ ├── main.rs │ └── wit │ │ ├── deps.lock │ │ ├── deps.toml │ │ ├── deps │ │ ├── cli │ │ │ ├── command.wit │ │ │ ├── environment.wit │ │ │ ├── exit.wit │ │ │ ├── imports.wit │ │ │ ├── run.wit │ │ │ ├── stdio.wit │ │ │ └── terminal.wit │ │ ├── clocks │ │ │ ├── monotonic-clock.wit │ │ │ ├── timezone.wit │ │ │ ├── wall-clock.wit │ │ │ └── world.wit │ │ ├── filesystem │ │ │ ├── preopens.wit │ │ │ ├── types.wit │ │ │ └── world.wit │ │ ├── http │ │ │ ├── handler.wit │ │ │ ├── proxy.wit │ │ │ └── types.wit │ │ ├── io │ │ │ ├── error.wit │ │ │ ├── poll.wit │ │ │ ├── streams.wit │ │ │ └── world.wit │ │ ├── keyvalue │ │ │ ├── atomic.wit │ │ │ ├── batch.wit │ │ │ ├── store.wit │ │ │ ├── watch.wit │ │ │ └── world.wit │ │ ├── random │ │ │ ├── insecure-seed.wit │ │ │ ├── insecure.wit │ │ │ ├── random.wit │ │ │ └── world.wit │ │ └── sockets │ │ │ ├── instance-network.wit │ │ │ ├── ip-name-lookup.wit │ │ │ ├── network.wit │ │ │ ├── tcp-create-socket.wit │ │ │ ├── tcp.wit │ │ │ ├── udp-create-socket.wit │ │ │ ├── udp.wit │ │ │ └── world.wit │ │ └── world.wit └── http │ ├── main.rs │ └── wit │ ├── deps.lock │ ├── deps.toml │ ├── deps │ ├── cli │ │ ├── command.wit │ │ ├── environment.wit │ │ ├── exit.wit │ │ ├── imports.wit │ │ ├── run.wit │ │ ├── stdio.wit │ │ └── terminal.wit │ ├── clocks │ │ ├── monotonic-clock.wit │ │ ├── timezone.wit │ │ ├── wall-clock.wit │ │ └── world.wit │ ├── filesystem │ │ ├── preopens.wit │ │ ├── types.wit │ │ └── world.wit │ ├── http │ │ ├── handler.wit │ │ ├── proxy.wit │ │ └── types.wit │ ├── io │ │ ├── error.wit │ │ ├── poll.wit │ │ ├── streams.wit │ │ └── world.wit │ ├── random │ │ ├── insecure-seed.wit │ │ ├── insecure.wit │ │ ├── random.wit │ │ └── world.wit │ └── sockets │ │ ├── instance-network.wit │ │ ├── ip-name-lookup.wit │ │ ├── network.wit │ │ ├── tcp-create-socket.wit │ │ ├── tcp.wit │ │ ├── udp-create-socket.wit │ │ ├── udp.wit │ │ └── world.wit │ └── world.wit ├── flake.lock ├── flake.nix ├── garnix.yaml ├── src └── bin │ └── wit-deps │ └── main.rs └── tests └── build ├── .gitignore ├── Cargo.toml ├── build.rs ├── src └── lib.rs ├── subcrate ├── Cargo.toml ├── build.rs ├── src │ └── lib.rs └── wit │ ├── deps.lock │ ├── deps.toml │ └── world.wit └── wit ├── build.wit ├── deps.lock └── deps.toml /.github/actions/build-nix/action.yml: -------------------------------------------------------------------------------- 1 | name: build via Nix 2 | 3 | inputs: 4 | package: 5 | description: package specification to build 6 | required: true 7 | install-path: 8 | description: path within resulting output, from which to install (e.g. `/bin/wit-deps`) 9 | 10 | runs: 11 | using: composite 12 | steps: 13 | - run: nix build -L '.#${{ inputs.package }}' 14 | shell: bash 15 | - run: nix run -L --inputs-from . 'nixpkgs#coreutils' -- --coreutils-prog=ginstall -p "./result${{ inputs.install-path }}" '${{ inputs.package }}' 16 | shell: bash 17 | - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 18 | with: 19 | name: ${{ inputs.package }} 20 | path: ${{ inputs.package }} 21 | if-no-files-found: error 22 | -------------------------------------------------------------------------------- /.github/actions/install-nix/action.yml: -------------------------------------------------------------------------------- 1 | name: install Nix 2 | 3 | inputs: 4 | cachixAuthToken: 5 | description: auth token for https://app.cachix.org/cache/bytecodealliance 6 | 7 | runs: 8 | using: composite 9 | steps: 10 | - uses: DeterminateSystems/nix-installer-action@v16 11 | with: 12 | extra-conf: | 13 | accept-flake-config = true 14 | - uses: DeterminateSystems/magic-nix-cache-action@v9 15 | - uses: cachix/cachix-action@v15 16 | continue-on-error: true 17 | with: 18 | name: bytecodealliance 19 | authToken: '${{ inputs.cachixAuthToken }}' 20 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | - package-ecosystem: "github-actions" 12 | directory: "/.github/actions/build-nix" 13 | schedule: 14 | interval: "daily" 15 | - package-ecosystem: "github-actions" 16 | directory: "/.github/actions/install-nix" 17 | schedule: 18 | interval: "daily" 19 | -------------------------------------------------------------------------------- /.github/workflows/nix.yml: -------------------------------------------------------------------------------- 1 | name: nix 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.ref }} 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | fmt: 13 | runs-on: ubuntu-24.04 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: ./.github/actions/install-nix 17 | with: 18 | cachixAuthToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 19 | - run: nix fmt -L $(find . -type f -name '*.nix') 20 | 21 | run: 22 | runs-on: ubuntu-24.04 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: ./.github/actions/install-nix 26 | with: 27 | cachixAuthToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 28 | - run: nix run -L . -- --version 29 | 30 | develop: 31 | runs-on: ubuntu-24.04 32 | steps: 33 | - uses: actions/checkout@v4 34 | - uses: ./.github/actions/install-nix 35 | with: 36 | cachixAuthToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 37 | - run: nix develop -L --ignore-environment -c cargo tree 38 | 39 | check: 40 | runs-on: ubuntu-24.04 41 | steps: 42 | - uses: actions/checkout@v4 43 | - uses: ./.github/actions/install-nix 44 | with: 45 | cachixAuthToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 46 | - run: | 47 | nix build -L \ 48 | .#checks.x86_64-linux.cli \ 49 | .#checks.x86_64-linux.clocks \ 50 | .#checks.x86_64-linux.filesystem \ 51 | .#checks.x86_64-linux.http \ 52 | .#checks.x86_64-linux.io \ 53 | .#checks.x86_64-linux.random \ 54 | .#checks.x86_64-linux.sockets 55 | -------------------------------------------------------------------------------- /.github/workflows/wit-deps.yml: -------------------------------------------------------------------------------- 1 | name: wit-deps 2 | 3 | on: 4 | pull_request: 5 | push: 6 | workflow_dispatch: 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | build-bin: 14 | strategy: 15 | matrix: 16 | config: 17 | - target: aarch64-unknown-linux-musl 18 | install-path: /bin/wit-deps 19 | test-bin: nix shell --inputs-from . 'nixpkgs#qemu' -c qemu-aarch64 ./result/bin/wit-deps --version 20 | test-oci: docker load < ./result 21 | # TODO: Run aarch64 binary within OCI 22 | 23 | - target: aarch64-apple-darwin 24 | install-path: /bin/wit-deps 25 | test-bin: file ./result/bin/wit-deps 26 | test-oci: docker load < ./result 27 | 28 | - target: armv7-unknown-linux-musleabihf 29 | install-path: /bin/wit-deps 30 | test-bin: nix shell --inputs-from . 'nixpkgs#qemu' -c qemu-arm ./result/bin/wit-deps --version 31 | test-oci: docker load < ./result 32 | # TODO: Run armv7 binary within OCI 33 | 34 | - target: x86_64-apple-darwin 35 | install-path: /bin/wit-deps 36 | test-bin: file ./result/bin/wit-deps 37 | test-oci: docker load < ./result 38 | 39 | - target: x86_64-pc-windows-gnu 40 | install-path: /bin/wit-deps.exe 41 | test-bin: nix shell --inputs-from . 'nixpkgs#wine64' -c wine64 ./result/bin/wit-deps.exe --version 42 | test-oci: docker load < ./result 43 | # TODO: Run windows binary within OCI 44 | 45 | - target: x86_64-unknown-linux-musl 46 | install-path: /bin/wit-deps 47 | test-bin: ./result/bin/wit-deps --version 48 | test-oci: | 49 | docker load < ./result 50 | docker run --rm wit-deps:$(nix eval --raw .#wit-deps-x86_64-unknown-linux-musl-oci.imageTag) wit-deps --version 51 | 52 | name: wit-deps-${{ matrix.config.target }} 53 | runs-on: ubuntu-24.04 54 | steps: 55 | - uses: actions/checkout@v4 56 | - uses: ./.github/actions/install-nix 57 | with: 58 | cachixAuthToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 59 | - uses: ./.github/actions/build-nix 60 | with: 61 | package: wit-deps-${{ matrix.config.target }} 62 | install-path: ${{ matrix.config.install-path }} 63 | - run: ${{ matrix.config.test-bin }} 64 | - uses: ./.github/actions/build-nix 65 | with: 66 | package: wit-deps-${{ matrix.config.target }}-oci 67 | - run: ${{ matrix.config.test-oci }} 68 | 69 | build-lipo: 70 | name: wit-deps-universal-darwin 71 | needs: build-bin 72 | runs-on: macos-latest 73 | steps: 74 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 75 | with: 76 | name: wit-deps-aarch64-apple-darwin 77 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 78 | with: 79 | name: wit-deps-x86_64-apple-darwin 80 | - run: chmod +x ./wit-deps-aarch64-apple-darwin 81 | - run: ./wit-deps-aarch64-apple-darwin --version 82 | - run: lipo -create ./wit-deps-aarch64-apple-darwin ./wit-deps-x86_64-apple-darwin -output ./wit-deps-universal-darwin 83 | - run: chmod +x ./wit-deps-universal-darwin 84 | - run: ./wit-deps-universal-darwin --version 85 | - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 86 | with: 87 | name: wit-deps-universal-darwin 88 | path: wit-deps-universal-darwin 89 | if-no-files-found: error 90 | 91 | cargo: 92 | strategy: 93 | matrix: 94 | check: 95 | - audit 96 | - fmt 97 | - clippy 98 | - nextest 99 | 100 | name: cargo ${{ matrix.check }} 101 | runs-on: ubuntu-24.04 102 | steps: 103 | - uses: actions/checkout@v4 104 | - uses: ./.github/actions/install-nix 105 | with: 106 | cachixAuthToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 107 | - run: nix build -L .#checks.x86_64-linux.${{ matrix.check }} 108 | 109 | build-doc: 110 | runs-on: ubuntu-24.04 111 | steps: 112 | - uses: actions/checkout@v4 113 | - uses: ./.github/actions/install-nix 114 | with: 115 | cachixAuthToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 116 | - run: nix build -L .#checks.x86_64-linux.doc 117 | - run: cp --no-preserve=mode -R ./result/share/doc ./doc 118 | - run: rm -f doc/.lock 119 | - name: Create `.nojekyll` 120 | run: touch doc/.nojekyll 121 | - name: Write `index.html` 122 | run: | 123 | cat < doc/index.html 124 | 125 | 126 | Redirecting to wit_deps/index.html 127 | 128 | 129 | EOF 130 | - uses: actions/upload-pages-artifact@v3.0.1 131 | with: 132 | path: doc 133 | 134 | deploy-doc: 135 | runs-on: ubuntu-24.04 136 | needs: build-doc 137 | permissions: 138 | pages: write 139 | id-token: write 140 | environment: 141 | name: github-pages 142 | url: ${{ steps.deployment.outputs.page_url }} 143 | if: ${{ github.ref == 'refs/heads/main' }} 144 | steps: 145 | - uses: actions/deploy-pages@v4.0.5 146 | id: deployment 147 | 148 | oci: 149 | runs-on: ubuntu-22.04 150 | if: ${{ github.event_name != 'pull_request' }} 151 | needs: build-bin 152 | permissions: 153 | packages: write 154 | steps: 155 | - uses: actions/checkout@v4 156 | - uses: ./.github/actions/install-nix 157 | with: 158 | cachixAuthToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 159 | - uses: docker/login-action@v3 160 | with: 161 | registry: ghcr.io 162 | username: ${{ github.repository_owner }} 163 | password: ${{ secrets.GITHUB_TOKEN }} 164 | - run: | 165 | nix run -L .#build-wit-deps-oci wit-deps 166 | nix profile install --inputs-from . 'nixpkgs#buildah' 167 | buildah manifest push --all --format 'v2s2' wit-deps docker://ghcr.io/${{ github.repository_owner }}/wit-deps:${{ github.sha }} 168 | docker run ghcr.io/${{ github.repository_owner }}/wit-deps:${{ github.sha }} wit-deps --version 169 | 170 | release: 171 | if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push' 172 | needs: [ build-bin, build-lipo, build-doc, cargo ] 173 | runs-on: ubuntu-24.04 174 | permissions: 175 | contents: write 176 | steps: 177 | - uses: actions/checkout@v4 178 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 179 | with: 180 | name: wit-deps-aarch64-apple-darwin 181 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 182 | with: 183 | name: wit-deps-aarch64-unknown-linux-musl 184 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 185 | with: 186 | name: wit-deps-armv7-unknown-linux-musleabihf 187 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 188 | with: 189 | name: wit-deps-universal-darwin 190 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 191 | with: 192 | name: wit-deps-x86_64-apple-darwin 193 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 194 | with: 195 | name: wit-deps-x86_64-pc-windows-gnu 196 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 197 | with: 198 | name: wit-deps-x86_64-unknown-linux-musl 199 | 200 | - run: chmod +x ./wit-deps-* 201 | 202 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 203 | with: 204 | name: wit-deps-aarch64-apple-darwin-oci 205 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 206 | with: 207 | name: wit-deps-aarch64-unknown-linux-musl-oci 208 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 209 | with: 210 | name: wit-deps-armv7-unknown-linux-musleabihf-oci 211 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 212 | with: 213 | name: wit-deps-x86_64-pc-windows-gnu-oci 214 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 215 | with: 216 | name: wit-deps-x86_64-apple-darwin-oci 217 | - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 218 | with: 219 | name: wit-deps-x86_64-unknown-linux-musl-oci 220 | 221 | - uses: softprops/action-gh-release@v2 222 | with: 223 | draft: true 224 | prerelease: true 225 | generate_release_notes: true 226 | files: | 227 | wit-deps-aarch64-apple-darwin 228 | wit-deps-aarch64-apple-darwin-oci 229 | wit-deps-aarch64-unknown-linux-musl 230 | wit-deps-aarch64-unknown-linux-musl-oci 231 | wit-deps-armv7-unknown-linux-musleabihf 232 | wit-deps-armv7-unknown-linux-musleabihf-oci 233 | wit-deps-universal-darwin 234 | wit-deps-x86_64-apple-darwin 235 | wit-deps-x86_64-apple-darwin-oci 236 | wit-deps-x86_64-pc-windows-gnu 237 | wit-deps-x86_64-pc-windows-gnu-oci 238 | wit-deps-x86_64-unknown-linux-musl 239 | wit-deps-x86_64-unknown-linux-musl-oci 240 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | release 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.5.0] - 2025-01-16 11 | 12 | ### Added 13 | 14 | - URL dependencies now have support for `subdir` attribute, which overrides the default of `wit`. 15 | This feature is particularly useful for working with wasip3 draft packages 16 | 17 | ## [0.3.0] - 2023-04-11 18 | 19 | ### Added 20 | 21 | - Transitive dependencies will now be pulled in from `wit/deps` of dependencies in the manifest 22 | 23 | ### Fixed 24 | 25 | - Relative manifest path support in `wit-deps` binary 26 | 27 | ### Removed 28 | 29 | - `package` argument to binary and library update and lock functions 30 | 31 | ## [0.2.2] - 2023-04-11 32 | 33 | ### Added 34 | 35 | - `wit-deps update` along with the `wit-deps::update_path` and `wit-deps::update` library API 36 | 37 | ## [0.2.1] - 2023-04-10 38 | 39 | ### Fixed 40 | 41 | - Ensure `path` in `deps.lock` matches the manifest `path` 42 | 43 | ## [0.2.0] - 2023-04-10 44 | 45 | ### Added 46 | 47 | - Functionality to specify a path to `wit` directory in `lock!` 48 | - `lock_sync!` macro executing `lock!` in a multi-threaded Tokio context. This macro is guarded by `sync` feature, which is enabled by default 49 | - Support for path dependencies in `deps.toml` 50 | 51 | ## [0.1.0] - 2023-04-07 52 | 53 | ### Added 54 | 55 | - Initial `wit-deps` library and binary implementations 56 | 57 | [unreleased]: https://github.com/bytecodealliance/wit-deps/compare/v0.5.0...HEAD 58 | [0.5.0]: https://github.com/bytecodealliance/wit-deps/releases/tag/v0.5.0 59 | [0.3.0]: https://github.com/bytecodealliance/wit-deps/releases/tag/v0.3.0 60 | [0.2.2]: https://github.com/bytecodealliance/wit-deps/releases/tag/v0.2.2 61 | [0.2.1]: https://github.com/bytecodealliance/wit-deps/releases/tag/v0.2.1 62 | [0.2.0]: https://github.com/bytecodealliance/wit-deps/releases/tag/v0.2.0 63 | [0.1.0]: https://github.com/bytecodealliance/wit-deps/releases/tag/v0.1.0 64 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | *Note*: this Code of Conduct pertains to individuals' behavior. Please also see the [Organizational Code of Conduct][OCoC]. 4 | 5 | ## Our Pledge 6 | 7 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to creating a positive environment include: 12 | 13 | * Using welcoming and inclusive language 14 | * Being respectful of differing viewpoints and experiences 15 | * Gracefully accepting constructive criticism 16 | * Focusing on what is best for the community 17 | * Showing empathy towards other community members 18 | 19 | Examples of unacceptable behavior by participants include: 20 | 21 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 22 | * Trolling, insulting/derogatory comments, and personal or political attacks 23 | * Public or private harassment 24 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 25 | * Other conduct which could reasonably be considered inappropriate in a professional setting 26 | 27 | ## Our Responsibilities 28 | 29 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 30 | 31 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 32 | 33 | ## Scope 34 | 35 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 36 | 37 | ## Enforcement 38 | 39 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Bytecode Alliance CoC team at [report@bytecodealliance.org](mailto:report@bytecodealliance.org). The CoC team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The CoC team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 40 | 41 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the Bytecode Alliance's leadership. 42 | 43 | ## Attribution 44 | 45 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 46 | 47 | [OCoC]: https://github.com/bytecodealliance/wasmtime/blob/main/ORG_CODE_OF_CONDUCT.md 48 | [homepage]: https://www.contributor-covenant.org 49 | [version]: https://www.contributor-covenant.org/version/1/4/ 50 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wit-deps-cli" 3 | version = "0.5.0" 4 | description = "WIT dependency manager" 5 | 6 | authors.workspace = true 7 | categories.workspace = true 8 | edition.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | 12 | [workspace.package] 13 | authors = ["Roman Volosatovs"] 14 | categories = ["wasm"] 15 | edition = "2021" 16 | license = "Apache-2.0 WITH LLVM-exception" 17 | repository = "https://github.com/bytecodealliance/wit-deps" 18 | 19 | [badges.maintenance] 20 | status = "actively-developed" 21 | 22 | [dependencies] 23 | anyhow = { workspace = true, features = ["std"] } 24 | camino = { workspace = true } 25 | clap = { workspace = true, features = ["std", "color", "help", "usage", "error-context", "suggestions", "derive"] } 26 | wit-deps = { workspace = true } 27 | tokio = { workspace = true, features = ["io-std", "fs", "macros", "rt-multi-thread"] } 28 | tokio-util = { workspace = true, features = ["compat"] } 29 | toml = { workspace = true, features = ["display", "parse"] } 30 | tracing-subscriber = { workspace = true, features = ["ansi", "env-filter", "fmt", "json", "std"] } 31 | 32 | [dev-dependencies] 33 | build-test = { workspace = true } 34 | wit-bindgen = { workspace = true, features = ["default"] } 35 | 36 | [workspace.dependencies] 37 | anyhow = { version = "1", default-features = false } 38 | async-compression = { version = "0.3", default-features = false } 39 | async-tar = { version = "0.5", default-features = false } 40 | async-trait = { version = "0.1", default-features = false } 41 | camino = { version = "1", default-features = false } 42 | clap = { version = "4", default-features = false } 43 | directories = { version = "5", default-features = false } 44 | futures = { version = "0.3", default-features = false } 45 | build-test = { path = "./tests/build" } 46 | hex = { version = "0.4", default-features = false } 47 | reqwest = { version = "0.11", default-features = false } 48 | serde = { version = "1", default-features = false } 49 | sha2 = { version = "0.10", default-features = false } 50 | tokio = { version = "1", default-features = false } 51 | tokio-stream = { version = "0.1", default-features = false } 52 | tokio-util = { version = "0.7", default-features = false } 53 | toml = { version = "0.7", default-features = false } 54 | tracing = { version = "0.1", default-features = false } 55 | tracing-subscriber = { version = "0.3", default-features = false } 56 | url = { version = "2", default-features = false } 57 | urlencoding = { version = "2.1", default-features = false } 58 | wit-bindgen = { version = "0.37", default-features = false } 59 | wit-deps = { path = "./crates/wit-deps", version = "0.5" } 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Romāns Volosatovs 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | --- LLVM Exceptions to the Apache 2.0 License ---- 16 | 17 | As an exception, if, as a result of your compiling your source code, portions 18 | of this Software are embedded into an Object form of such source code, you 19 | may redistribute such embedded portions in such Object form without complying 20 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 21 | 22 | In addition, if you combine or link compiled forms of this Software with 23 | software that is licensed under the GPLv2 ("Combined Software") and if a 24 | court of competent jurisdiction determines that the patent provision (Section 25 | 3), the indemnity provision (Section 9) or other Section of the License 26 | conflicts with the conditions of the GPLv2, you may retroactively and 27 | prospectively choose to deem waived or otherwise exclude such Section(s) of 28 | the License, but only in their entirety and only with respect to the Combined 29 | Software. 30 | -------------------------------------------------------------------------------- /ORG_CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Bytecode Alliance Organizational Code of Conduct (OCoC) 2 | 3 | *Note*: this Code of Conduct pertains to organizations' behavior. Please also see the [Individual Code of Conduct](CODE_OF_CONDUCT.md). 4 | 5 | ## Preamble 6 | 7 | The Bytecode Alliance (BA) welcomes involvement from organizations, 8 | including commercial organizations. This document is an 9 | *organizational* code of conduct, intended particularly to provide 10 | guidance to commercial organizations. It is distinct from the 11 | [Individual Code of Conduct (ICoC)](CODE_OF_CONDUCT.md), and does not 12 | replace the ICoC. This OCoC applies to any group of people acting in 13 | concert as a BA member or as a participant in BA activities, whether 14 | or not that group is formally incorporated in some jurisdiction. 15 | 16 | The code of conduct described below is not a set of rigid rules, and 17 | we did not write it to encompass every conceivable scenario that might 18 | arise. For example, it is theoretically possible there would be times 19 | when asserting patents is in the best interest of the BA community as 20 | a whole. In such instances, consult with the BA, strive for 21 | consensus, and interpret these rules with an intent that is generous 22 | to the community the BA serves. 23 | 24 | While we may revise these guidelines from time to time based on 25 | real-world experience, overall they are based on a simple principle: 26 | 27 | *Bytecode Alliance members should observe the distinction between 28 | public community functions and private functions — especially 29 | commercial ones — and should ensure that the latter support, or at 30 | least do not harm, the former.* 31 | 32 | ## Guidelines 33 | 34 | * **Do not cause confusion about Wasm standards or interoperability.** 35 | 36 | Having an interoperable WebAssembly core is a high priority for 37 | the BA, and members should strive to preserve that core. It is fine 38 | to develop additional non-standard features or APIs, but they 39 | should always be clearly distinguished from the core interoperable 40 | Wasm. 41 | 42 | Treat the WebAssembly name and any BA-associated names with 43 | respect, and follow BA trademark and branding guidelines. If you 44 | distribute a customized version of software originally produced by 45 | the BA, or if you build a product or service using BA-derived 46 | software, use names that clearly distinguish your work from the 47 | original. (You should still provide proper attribution to the 48 | original, of course, wherever such attribution would normally be 49 | given.) 50 | 51 | Further, do not use the WebAssembly name or BA-associated names in 52 | other public namespaces in ways that could cause confusion, e.g., 53 | in company names, names of commercial service offerings, domain 54 | names, publicly-visible social media accounts or online service 55 | accounts, etc. It may sometimes be reasonable, however, to 56 | register such a name in a new namespace and then immediately donate 57 | control of that account to the BA, because that would help the project 58 | maintain its identity. 59 | 60 | For further guidance, see the BA Trademark and Branding Policy 61 | [TODO: create policy, then insert link]. 62 | 63 | * **Do not restrict contributors.** If your company requires 64 | employees or contractors to sign non-compete agreements, those 65 | agreements must not prevent people from participating in the BA or 66 | contributing to related projects. 67 | 68 | This does not mean that all non-compete agreements are incompatible 69 | with this code of conduct. For example, a company may restrict an 70 | employee's ability to solicit the company's customers. However, an 71 | agreement must not block any form of technical or social 72 | participation in BA activities, including but not limited to the 73 | implementation of particular features. 74 | 75 | The accumulation of experience and expertise in individual persons, 76 | who are ultimately free to direct their energy and attention as 77 | they decide, is one of the most important drivers of progress in 78 | open source projects. A company that limits this freedom may hinder 79 | the success of the BA's efforts. 80 | 81 | * **Do not use patents as offensive weapons.** If any BA participant 82 | prevents the adoption or development of BA technologies by 83 | asserting its patents, that undermines the purpose of the 84 | coalition. The collaboration fostered by the BA cannot include 85 | members who act to undermine its work. 86 | 87 | * **Practice responsible disclosure** for security vulnerabilities. 88 | Use designated, non-public reporting channels to disclose technical 89 | vulnerabilities, and give the project a reasonable period to 90 | respond, remediate, and patch. [TODO: optionally include the 91 | security vulnerability reporting URL here.] 92 | 93 | Vulnerability reporters may patch their company's own offerings, as 94 | long as that patching does not significantly delay the reporting of 95 | the vulnerability. Vulnerability information should never be used 96 | for unilateral commercial advantage. Vendors may legitimately 97 | compete on the speed and reliability with which they deploy 98 | security fixes, but withholding vulnerability information damages 99 | everyone in the long run by risking harm to the BA project's 100 | reputation and to the security of all users. 101 | 102 | * **Respect the letter and spirit of open source practice.** While 103 | there is not space to list here all possible aspects of standard 104 | open source practice, some examples will help show what we mean: 105 | 106 | * Abide by all applicable open source license terms. Do not engage 107 | in copyright violation or misattribution of any kind. 108 | 109 | * Do not claim others' ideas or designs as your own. 110 | 111 | * When others engage in publicly visible work (e.g., an upcoming 112 | demo that is coordinated in a public issue tracker), do not 113 | unilaterally announce early releases or early demonstrations of 114 | that work ahead of their schedule in order to secure private 115 | advantage (such as marketplace advantage) for yourself. 116 | 117 | The BA reserves the right to determine what constitutes good open 118 | source practices and to take action as it deems appropriate to 119 | encourage, and if necessary enforce, such practices. 120 | 121 | ## Enforcement 122 | 123 | Instances of organizational behavior in violation of the OCoC may 124 | be reported by contacting the Bytecode Alliance CoC team at 125 | [report@bytecodealliance.org](mailto:report@bytecodealliance.org). The 126 | CoC team will review and investigate all complaints, and will respond 127 | in a way that it deems appropriate to the circumstances. The CoC team 128 | is obligated to maintain confidentiality with regard to the reporter of 129 | an incident. Further details of specific enforcement policies may be 130 | posted separately. 131 | 132 | When the BA deems an organization in violation of this OCoC, the BA 133 | will, at its sole discretion, determine what action to take. The BA 134 | will decide what type, degree, and duration of corrective action is 135 | needed, if any, before a violating organization can be considered for 136 | membership (if it was not already a member) or can have its membership 137 | reinstated (if it was a member and the BA canceled its membership due 138 | to the violation). 139 | 140 | In practice, the BA's first approach will be to start a conversation, 141 | with punitive enforcement used only as a last resort. Violations 142 | often turn out to be unintentional and swiftly correctable with all 143 | parties acting in good faith. 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | `wit-deps` is a simple WIT dependency manager binary and Rust library, which manages your `wit/deps`. It's main objective is to ensure that whatever is located in your `wit/deps` is consistent with your dependency manifest (default: `wit/deps.toml`) and dependency lock (default: `wit/deps.lock`). 4 | 5 | # Manifest 6 | 7 | A dependency manifest is a TOML-encoded table mapping dependency names to their source specifications. In it's simplest form, a source specification is a URL string of a gzipped tarball containing a directory tree with a `wit` subdirectory containing `wit` files. 8 | 9 | Example: 10 | 11 | ```toml 12 | # wit/deps.toml 13 | # Use `wit-deps update` to pull in latest changes from "dynamic" branch references 14 | clocks = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz" 15 | http = "https://github.com/WebAssembly/wasi-http/archive/main.tar.gz" 16 | messaging = "https://github.com/WebAssembly/wasi-messaging/archive/main.tar.gz" 17 | sockets = "https://github.com/WebAssembly/wasi-sockets/archive/main.tar.gz" 18 | sql = "https://github.com/WebAssembly/wasi-sql/archive/main.tar.gz" 19 | 20 | # Pin to a tag 21 | io = "https://github.com/rvolosatovs/wasi-io/archive/v0.1.0.tar.gz" # this fork renames `streams` interface for compatiblity with wasi-snapshot-preview1 22 | 23 | # Pin a dependency to a particular revision and source digests. Each digest is optional 24 | [keyvalue] 25 | url = "https://github.com/WebAssembly/wasi-keyvalue/archive/6f3bd6bca07cb7b25703a13f633e05258d56a2dc.tar.gz" 26 | sha256 = "1755b8f1e9f2e70d0bde06198bf50d12603b454b52bf1f59064c1877baa33dff" 27 | sha512 = "7bc43665a9de73ec7bef075e32f67ed0ebab04a1e47879f9328e8e52edfb35359512c899ab8a52240fecd0c53ff9c036abefe549e5fb99225518a2e0446d66e0" 28 | 29 | ``` 30 | 31 | A source specfication can also be a structure with the following fields: 32 | 33 | - `url` - same format as the URL string 34 | - `sha256` - (optional) hex-encoded sha256 digest of the contents of the URL 35 | - `sha512` (optional) hex-encoded sha512 digest of the contents of the URL 36 | - `path` path to the directory containing the WIT definitions 37 | 38 | Either `url` or `path` must be specified (both support string format) 39 | 40 | Example: 41 | 42 | ```toml 43 | # wit/deps.toml 44 | mywit = "./path/to/my/wit" 45 | 46 | [logging] 47 | url = "https://github.com/WebAssembly/wasi-logging/archive/d106e59b25297d0496e6a5d221ad090e19c3aaa3.tar.gz" 48 | sha256 = "4bb4aeab99e7323b30d107aab78e88b2265c1598cc438bc5fbc0d16bb63e798f" 49 | sha512 = "13b52b59afd98dd4938e3a651fad631d41a2e84ce781df5d8957eded77a8e1ac4277e771a10225cd4a3a9eae369ed7e8fee6e26f9991a2caa7c97c4a758b1ae6" 50 | ``` 51 | 52 | # Usage 53 | 54 | Note, `wit-deps` assumes that it has full control over `wit/deps` and so it may delete and modify contents of `wit/deps` at any time! 55 | 56 | ## Interactive 57 | 58 | Use `wit-deps` or `wit-deps lock` to populate `wit/deps` using `wit/deps.toml` manifest and `wit/deps.lock` (will be created if it does not exist) 59 | 60 | To you it with a proxy, use the below environment variables: 61 | ``` 62 | export PROXY_SERVER={yourproxyaddress}:{port} 63 | export PROXY_USERNAME='{yourproxyusername}' 64 | export PROXY_PASSWORD='{yourproxypassword}' 65 | ``` 66 | 67 | ## Rust 68 | 69 | Use `wit-deps::lock!` macro in `build.rs` of your project to automatically lock your `wit/deps`. 70 | 71 | See crate documentation for more advanced use cases 72 | 73 | # Design decisions 74 | 75 | - `wit-deps` is lazy by default and will only fetch/write when it absolutely has to 76 | - `wit-deps` assumes that result of fetching from a URL is deterministic, that is contents returned by GET of a URL `domain.com` must always return exactly the same contents. Note, that you can use `sha256` or `sha512` fields in your manifest entry to invalidate the cache in this case 77 | -------------------------------------------------------------------------------- /crates/wit-deps/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wit-deps" 3 | version = "0.5.0" 4 | description = "WIT dependency management" 5 | readme = "../../README.md" 6 | 7 | authors.workspace = true 8 | categories.workspace = true 9 | edition.workspace = true 10 | license.workspace = true 11 | repository.workspace = true 12 | 13 | [dependencies] 14 | anyhow = { workspace = true, features = ["std"] } 15 | async-compression = { workspace = true, features = ["futures-io", "gzip"] } 16 | async-tar = { workspace = true } 17 | async-trait = { workspace = true } 18 | directories = { workspace = true } 19 | futures = { workspace = true, features = ["async-await", "std"] } 20 | hex = { workspace = true, features = ["alloc"] } 21 | reqwest = { workspace = true, features = ["rustls-tls", "stream"] } 22 | serde = { workspace = true, features = ["derive"] } 23 | sha2 = { workspace = true } 24 | tokio = { workspace = true, features = ["fs"] } 25 | tokio-stream = { workspace = true, features = ["fs"] } 26 | tokio-util = { workspace = true, features = ["compat"] } 27 | toml = { workspace = true, features = ["display", "parse", "preserve_order"] } 28 | tracing = { workspace = true, features = ["attributes"] } 29 | url = { workspace = true, features = ["serde"] } 30 | urlencoding = { workspace = true } 31 | 32 | [features] 33 | default = ["sync"] 34 | sync = ["tokio/rt-multi-thread"] 35 | -------------------------------------------------------------------------------- /crates/wit-deps/src/cache.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use core::ops::{Deref, DerefMut}; 3 | 4 | use std::ffi::{OsStr, OsString}; 5 | use std::path::{Path, PathBuf}; 6 | 7 | use anyhow::{bail, Context as _}; 8 | use async_trait::async_trait; 9 | use directories::ProjectDirs; 10 | use futures::{io::BufReader, AsyncBufRead, AsyncWrite}; 11 | use tokio::fs::{self, File, OpenOptions}; 12 | use tokio_util::compat::{Compat, TokioAsyncReadCompatExt}; 13 | use url::{Host, Url}; 14 | 15 | /// Resource caching layer 16 | #[async_trait] 17 | pub trait Cache { 18 | /// Type returned by the [Self::get] method 19 | type Read: AsyncBufRead + Unpin; 20 | /// Type returned by the [Self::insert] method 21 | type Write: AsyncWrite + Unpin; 22 | 23 | /// Returns a read handle for the entry from the cache associated with a given URL 24 | async fn get(&self, url: &Url) -> anyhow::Result>; 25 | 26 | /// Returns a write handle for the entry associated with a given URL 27 | async fn insert(&self, url: &Url) -> anyhow::Result; 28 | } 29 | 30 | /// Write-only [Cache] wrapper 31 | pub struct Write(pub T); 32 | 33 | impl From for Write { 34 | fn from(cache: T) -> Self { 35 | Self(cache) 36 | } 37 | } 38 | 39 | impl Deref for Write { 40 | type Target = T; 41 | 42 | fn deref(&self) -> &Self::Target { 43 | &self.0 44 | } 45 | } 46 | 47 | impl DerefMut for Write { 48 | fn deref_mut(&mut self) -> &mut Self::Target { 49 | &mut self.0 50 | } 51 | } 52 | 53 | #[async_trait] 54 | impl Cache for Write { 55 | type Read = T::Read; 56 | type Write = T::Write; 57 | 58 | async fn get(&self, _: &Url) -> anyhow::Result> { 59 | Ok(None) 60 | } 61 | 62 | async fn insert(&self, url: &Url) -> anyhow::Result { 63 | self.0.insert(url).await 64 | } 65 | } 66 | 67 | impl Write { 68 | /// Extracts the inner [Cache] 69 | pub fn into_inner(self) -> T { 70 | self.0 71 | } 72 | } 73 | 74 | /// Local caching layer 75 | #[derive(Clone, Debug)] 76 | pub struct Local(PathBuf); 77 | 78 | impl fmt::Display for Local { 79 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 80 | write!(f, "{}", self.0.display()) 81 | } 82 | } 83 | 84 | impl Deref for Local { 85 | type Target = PathBuf; 86 | 87 | fn deref(&self) -> &Self::Target { 88 | &self.0 89 | } 90 | } 91 | 92 | impl DerefMut for Local { 93 | fn deref_mut(&mut self) -> &mut Self::Target { 94 | &mut self.0 95 | } 96 | } 97 | 98 | impl Local { 99 | /// Returns a [Local] cache located at the default system-specific cache directory if such 100 | /// could be determined. 101 | pub fn cache_dir() -> Option { 102 | ProjectDirs::from("", "", env!("CARGO_PKG_NAME")) 103 | .as_ref() 104 | .map(ProjectDirs::cache_dir) 105 | .map(Self::from) 106 | } 107 | 108 | fn path(&self, url: &Url) -> impl AsRef { 109 | let mut path = self.0.clone(); 110 | match url.host() { 111 | Some(Host::Ipv4(ip)) => { 112 | path.push(ip.to_string()); 113 | } 114 | Some(Host::Ipv6(ip)) => { 115 | path.push(ip.to_string()); 116 | } 117 | Some(Host::Domain(domain)) => { 118 | path.push(domain); 119 | } 120 | _ => {} 121 | } 122 | if let Some(segments) = url.path_segments() { 123 | for seg in segments { 124 | path.push(seg); 125 | } 126 | } 127 | path 128 | } 129 | } 130 | 131 | #[async_trait] 132 | impl Cache for Local { 133 | type Read = BufReader>; 134 | type Write = Compat; 135 | 136 | async fn get(&self, url: &Url) -> anyhow::Result> { 137 | match File::open(self.path(url)).await { 138 | Ok(file) => Ok(Some(BufReader::new(file.compat()))), 139 | Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None), 140 | Err(e) => bail!("failed to lookup `{url}` in cache: {e}"), 141 | } 142 | } 143 | 144 | async fn insert(&self, url: &Url) -> anyhow::Result { 145 | let path = self.path(url); 146 | if let Some(parent) = path.as_ref().parent() { 147 | fs::create_dir_all(parent) 148 | .await 149 | .context("failed to create directory")?; 150 | } 151 | OpenOptions::new() 152 | .create_new(true) 153 | .write(true) 154 | .open(path) 155 | .await 156 | .map(tokio_util::compat::TokioAsyncReadCompatExt::compat) 157 | .context("failed to open file for writing") 158 | } 159 | } 160 | 161 | impl From for Local { 162 | fn from(path: PathBuf) -> Self { 163 | Self(path) 164 | } 165 | } 166 | 167 | impl From for Local { 168 | fn from(path: String) -> Self { 169 | Self(path.into()) 170 | } 171 | } 172 | 173 | impl From for Local { 174 | fn from(path: OsString) -> Self { 175 | Self(path.into()) 176 | } 177 | } 178 | 179 | impl From<&Path> for Local { 180 | fn from(path: &Path) -> Self { 181 | Self(path.into()) 182 | } 183 | } 184 | 185 | impl From<&str> for Local { 186 | fn from(path: &str) -> Self { 187 | Self(path.into()) 188 | } 189 | } 190 | 191 | impl From<&OsStr> for Local { 192 | fn from(path: &OsStr) -> Self { 193 | Self(path.into()) 194 | } 195 | } 196 | 197 | #[cfg(test)] 198 | mod tests { 199 | use super::*; 200 | 201 | #[test] 202 | fn local_path() { 203 | assert_eq!( 204 | Local::from("test") 205 | .path( 206 | &"https://example.com/foo/bar.tar.gz" 207 | .parse() 208 | .expect("failed to parse URL") 209 | ) 210 | .as_ref(), 211 | Path::new("test") 212 | .join("example.com") 213 | .join("foo") 214 | .join("bar.tar.gz") 215 | ); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /crates/wit-deps/src/digest.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use core::pin::Pin; 3 | use core::task::{Context, Poll}; 4 | 5 | use futures::{AsyncRead, AsyncWrite}; 6 | use hex::FromHex; 7 | use serde::ser::SerializeStruct; 8 | use serde::{de, Deserialize, Serialize}; 9 | use sha2::{Digest as _, Sha256, Sha512}; 10 | 11 | /// A resource digest 12 | #[derive(Clone, Debug, Eq, Hash, PartialEq)] 13 | pub struct Digest { 14 | /// Sha256 digest of a resource 15 | pub sha256: [u8; 32], 16 | /// Sha512 digest of a resource 17 | pub sha512: [u8; 64], 18 | } 19 | 20 | impl<'de> Deserialize<'de> for Digest { 21 | fn deserialize(deserializer: D) -> Result 22 | where 23 | D: serde::Deserializer<'de>, 24 | { 25 | const FIELDS: [&str; 2] = ["sha256", "sha512"]; 26 | 27 | struct Visitor; 28 | impl<'de> de::Visitor<'de> for Visitor { 29 | type Value = Digest; 30 | 31 | fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 32 | formatter.write_str("a resource digest") 33 | } 34 | 35 | fn visit_map(self, mut map: V) -> Result 36 | where 37 | V: de::MapAccess<'de>, 38 | { 39 | let mut sha256 = None; 40 | let mut sha512 = None; 41 | while let Some((k, v)) = map.next_entry::()? { 42 | match k.as_ref() { 43 | "sha256" => { 44 | if sha256.is_some() { 45 | return Err(de::Error::duplicate_field("sha256")); 46 | } 47 | sha256 = FromHex::from_hex(v).map(Some).map_err(|e| { 48 | de::Error::custom(format!("invalid `sha256` field value: {e}")) 49 | })?; 50 | } 51 | "sha512" => { 52 | if sha512.is_some() { 53 | return Err(de::Error::duplicate_field("sha512")); 54 | } 55 | sha512 = FromHex::from_hex(v).map(Some).map_err(|e| { 56 | de::Error::custom(format!("invalid `sha512` field value: {e}")) 57 | })?; 58 | } 59 | k => return Err(de::Error::unknown_field(k, &FIELDS)), 60 | } 61 | } 62 | let sha256 = sha256.ok_or_else(|| de::Error::missing_field("sha256"))?; 63 | let sha512 = sha512.ok_or_else(|| de::Error::missing_field("sha512"))?; 64 | Ok(Digest { sha256, sha512 }) 65 | } 66 | } 67 | deserializer.deserialize_struct("Entry", &FIELDS, Visitor) 68 | } 69 | } 70 | 71 | impl Serialize for Digest { 72 | fn serialize(&self, serializer: S) -> Result 73 | where 74 | S: serde::Serializer, 75 | { 76 | let mut state = serializer.serialize_struct("Digest", 2)?; 77 | state.serialize_field("sha256", &hex::encode(self.sha256))?; 78 | state.serialize_field("sha512", &hex::encode(self.sha512))?; 79 | state.end() 80 | } 81 | } 82 | 83 | /// A reader wrapper, which hashes the bytes read 84 | pub struct Reader { 85 | reader: T, 86 | sha256: Sha256, 87 | sha512: Sha512, 88 | } 89 | 90 | impl AsyncRead for Reader { 91 | fn poll_read( 92 | mut self: Pin<&mut Self>, 93 | cx: &mut Context<'_>, 94 | buf: &mut [u8], 95 | ) -> Poll> { 96 | Pin::new(&mut self.reader).poll_read(cx, buf).map_ok(|n| { 97 | self.sha256.update(&buf[..n]); 98 | self.sha512.update(&buf[..n]); 99 | n 100 | }) 101 | } 102 | } 103 | 104 | impl From for Reader { 105 | fn from(reader: T) -> Self { 106 | Self { 107 | reader, 108 | sha256: Sha256::new(), 109 | sha512: Sha512::new(), 110 | } 111 | } 112 | } 113 | 114 | impl From> for Digest { 115 | fn from(hashed: Reader) -> Self { 116 | let sha256 = hashed.sha256.finalize().into(); 117 | let sha512 = hashed.sha512.finalize().into(); 118 | Self { sha256, sha512 } 119 | } 120 | } 121 | 122 | /// A writer wrapper, which hashes the bytes written 123 | pub struct Writer { 124 | writer: T, 125 | sha256: Sha256, 126 | sha512: Sha512, 127 | } 128 | 129 | impl AsyncWrite for Writer { 130 | fn poll_write( 131 | mut self: Pin<&mut Self>, 132 | cx: &mut Context<'_>, 133 | buf: &[u8], 134 | ) -> Poll> { 135 | Pin::new(&mut self.writer).poll_write(cx, buf).map_ok(|n| { 136 | self.sha256.update(&buf[..n]); 137 | self.sha512.update(&buf[..n]); 138 | n 139 | }) 140 | } 141 | 142 | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 143 | Pin::new(&mut self.writer).poll_flush(cx) 144 | } 145 | 146 | fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 147 | Pin::new(&mut self.writer).poll_close(cx) 148 | } 149 | } 150 | 151 | impl From for Writer { 152 | fn from(writer: T) -> Self { 153 | Self { 154 | writer, 155 | sha256: Sha256::new(), 156 | sha512: Sha512::new(), 157 | } 158 | } 159 | } 160 | 161 | impl From> for Digest { 162 | fn from(hashed: Writer) -> Self { 163 | let sha256 = hashed.sha256.finalize().into(); 164 | let sha512 = hashed.sha512.finalize().into(); 165 | Self { sha256, sha512 } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /crates/wit-deps/src/lock.rs: -------------------------------------------------------------------------------- 1 | use crate::{tar, Digest, DigestWriter, Identifier}; 2 | 3 | use core::ops::{Deref, DerefMut}; 4 | 5 | use std::collections::{BTreeMap, BTreeSet}; 6 | use std::path::{Path, PathBuf}; 7 | 8 | use anyhow::Context; 9 | use futures::io::sink; 10 | use serde::{Deserialize, Serialize}; 11 | use url::Url; 12 | 13 | fn default_subdir() -> Box { 14 | "wit".into() 15 | } 16 | 17 | fn is_default_subdir(s: &str) -> bool { 18 | s == "wit" 19 | } 20 | 21 | /// Source of this dependency 22 | #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] 23 | #[serde(untagged)] 24 | pub enum EntrySource { 25 | /// URL 26 | Url { 27 | /// URL 28 | url: Url, 29 | /// Subdirectory containing WIT definitions within the tarball 30 | #[serde(default = "default_subdir", skip_serializing_if = "is_default_subdir")] 31 | subdir: Box, 32 | }, 33 | /// Local path 34 | Path { 35 | /// Local path 36 | path: PathBuf, 37 | }, 38 | } 39 | 40 | /// WIT dependency [Lock] entry 41 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 42 | pub struct Entry { 43 | /// Resource source, [None] if the dependency is transitive 44 | #[serde(flatten)] 45 | pub source: Option, 46 | /// Resource digest 47 | #[serde(flatten)] 48 | pub digest: Digest, 49 | /// Transitive dependency identifiers 50 | #[serde(default, skip_serializing_if = "BTreeSet::is_empty")] 51 | pub deps: BTreeSet, 52 | } 53 | 54 | impl Entry { 55 | /// Create a new entry given a dependency source and path containing it 56 | #[must_use] 57 | pub fn new(source: Option, digest: Digest, deps: BTreeSet) -> Self { 58 | Self { 59 | source, 60 | digest, 61 | deps, 62 | } 63 | } 64 | 65 | /// Create a new entry given a dependency url and path containing the unpacked contents of it 66 | /// 67 | /// # Errors 68 | /// 69 | /// Returns an error if [`Self::digest`] of `path` fails 70 | pub async fn from_url( 71 | url: Url, 72 | path: impl AsRef, 73 | deps: BTreeSet, 74 | subdir: impl Into>, 75 | ) -> anyhow::Result { 76 | let digest = Self::digest(path) 77 | .await 78 | .context("failed to compute digest")?; 79 | Ok(Self::new( 80 | Some(EntrySource::Url { 81 | url, 82 | subdir: subdir.into(), 83 | }), 84 | digest, 85 | deps, 86 | )) 87 | } 88 | 89 | /// Create a new entry given a dependency path 90 | /// 91 | /// # Errors 92 | /// 93 | /// Returns an error if [`Self::digest`] of `path` fails 94 | pub async fn from_path( 95 | src: PathBuf, 96 | dst: impl AsRef, 97 | deps: BTreeSet, 98 | ) -> anyhow::Result { 99 | let digest = Self::digest(dst) 100 | .await 101 | .context("failed to compute digest")?; 102 | Ok(Self::new( 103 | Some(EntrySource::Path { path: src }), 104 | digest, 105 | deps, 106 | )) 107 | } 108 | 109 | /// Create a new entry given a transitive dependency path 110 | /// 111 | /// # Errors 112 | /// 113 | /// Returns an error if [`Self::digest`] of `path` fails 114 | pub async fn from_transitive_path(dst: impl AsRef) -> anyhow::Result { 115 | let digest = Self::digest(dst) 116 | .await 117 | .context("failed to compute digest")?; 118 | Ok(Self::new(None, digest, BTreeSet::default())) 119 | } 120 | 121 | /// Compute the digest of an entry from path 122 | /// 123 | /// # Errors 124 | /// 125 | /// Returns an error if tar-encoding the path fails 126 | pub async fn digest(path: impl AsRef) -> std::io::Result { 127 | tar(path, DigestWriter::from(sink())).await.map(Into::into) 128 | } 129 | } 130 | 131 | /// WIT dependency lock mapping [Identifiers](Identifier) to [Entries](Entry) 132 | #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] 133 | pub struct Lock(BTreeMap); 134 | 135 | impl Deref for Lock { 136 | type Target = BTreeMap; 137 | 138 | fn deref(&self) -> &Self::Target { 139 | &self.0 140 | } 141 | } 142 | 143 | impl DerefMut for Lock { 144 | fn deref_mut(&mut self) -> &mut Self::Target { 145 | &mut self.0 146 | } 147 | } 148 | 149 | impl FromIterator<(Identifier, Entry)> for Lock { 150 | fn from_iter>(iter: T) -> Self { 151 | Self(BTreeMap::from_iter(iter)) 152 | } 153 | } 154 | 155 | impl Extend<(Identifier, Entry)> for Lock { 156 | fn extend>(&mut self, iter: T) { 157 | self.0.extend(iter); 158 | } 159 | } 160 | 161 | impl From<[(Identifier, Entry); N]> for Lock { 162 | fn from(entries: [(Identifier, Entry); N]) -> Self { 163 | Self::from_iter(entries) 164 | } 165 | } 166 | 167 | #[cfg(test)] 168 | mod tests { 169 | use super::*; 170 | 171 | use anyhow::{ensure, Context}; 172 | use hex::FromHex; 173 | 174 | const FOO_URL: &str = "https://example.com/baz"; 175 | const FOO_SHA256: &str = "9f86d081884c7d658a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"; 176 | const FOO_SHA512: &str = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff"; 177 | 178 | #[test] 179 | fn decode() -> anyhow::Result<()> { 180 | fn assert_lock(lock: Lock) -> anyhow::Result { 181 | ensure!( 182 | lock == Lock::from([( 183 | "foo".parse().expect("failed to `foo` parse identifier"), 184 | Entry { 185 | source: Some(EntrySource::Url { 186 | url: FOO_URL.parse().expect("failed to parse `foo` URL"), 187 | subdir: "wit".into(), 188 | }), 189 | digest: Digest { 190 | sha256: FromHex::from_hex(FOO_SHA256) 191 | .expect("failed to decode `foo` sha256"), 192 | sha512: FromHex::from_hex(FOO_SHA512) 193 | .expect("failed to decode `foo` sha512"), 194 | }, 195 | deps: BTreeSet::default(), 196 | } 197 | )]) 198 | ); 199 | Ok(lock) 200 | } 201 | 202 | let lock = toml::from_str(&format!( 203 | r#" 204 | foo = {{ url = "{FOO_URL}", sha256 = "{FOO_SHA256}", sha512 = "{FOO_SHA512}" }} 205 | "# 206 | )) 207 | .context("failed to decode lock") 208 | .and_then(assert_lock)?; 209 | 210 | let lock = toml::to_string(&lock).context("failed to encode lock")?; 211 | toml::from_str(&lock) 212 | .context("failed to decode lock") 213 | .and_then(assert_lock)?; 214 | 215 | Ok(()) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /examples/github/main.rs: -------------------------------------------------------------------------------- 1 | wit_bindgen::generate!({ 2 | path: "examples/github/wit", 3 | world: "examples:github/github", 4 | generate_all, 5 | }); 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /examples/github/wit/deps.lock: -------------------------------------------------------------------------------- 1 | [cli] 2 | sha256 = "4dadd13d55aaf626833d1f4b9c34a17b0f04e993babd09552b785cda3b95ea76" 3 | sha512 = "898dcc4e8c15d18acc6b88dbe232336fa4d19019430a910dbc9e7aeaace3077a164af3be9f002de6e7e65ef693df340801ac0c7e421e9a746bf1b6d698a90835" 4 | 5 | [clocks] 6 | url = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz" 7 | sha256 = "93a701968a7dd3c5d69031bc0601681c468972fdf7e28a93bb6150a67d6ebe8b" 8 | sha512 = "98fca567c7a01887b0fb38981f1772169b6ea8de475b546508f8b86738d84e44ba95cae81def40ac34e8809f5f60e85224077ab8cb6d6d5d6296acc1df73c159" 9 | deps = ["io"] 10 | 11 | [filesystem] 12 | sha256 = "69d42fb10a04a33545b17e055f13db9b1e10e82ba0ed5bdb52334e40dc07c679" 13 | sha512 = "612effbac6f4804fe0c29dae20b78bbba59e52cb754c15402f5fe229c3153a221e0fbdff1d9d00ceaa3fe049c6a95523a5b99f772f1c16d972eade2c88326a30" 14 | 15 | [http] 16 | url = "https://github.com/WebAssembly/wasi-http/archive/main.tar.gz" 17 | sha256 = "b5f254849300c7d6ec1d65dff9c8b3d24c6c8a9ee8bf29013fb17b3728820a34" 18 | sha512 = "e421df67df7722a8fb9673bb8c455581e6b52be90073518569d22ae70368043f095adfd03e0655678cd9622965be3d73f60054bbd3a81ea21a4e58ff68e1b461" 19 | deps = ["cli", "filesystem", "io", "random"] 20 | 21 | [io] 22 | sha256 = "1cccbfe4122686ea57a25cd368e8cdfc408cbcad089f47fb6685b6f92e96f050" 23 | sha512 = "7a95f964c13da52611141acd89bc8876226497f128e99dd176a4270c5b5efbd8cc847b5fbd1a91258d028c646db99e0424d72590cf1caf20f9f3a3343fad5017" 24 | 25 | [keyvalue] 26 | url = "https://github.com/WebAssembly/wasi-keyvalue/archive/v0.2.0-draft.tar.gz" 27 | sha256 = "d2de617fe31ec0abc6072f75f97dd22bf95b3231d5b3111471d73871df9081cd" 28 | sha512 = "6f0b4e44c684d760c54552e2bde9bc976e0a4f6525fc1d47acb98625e030847276436242f42a41f4da1bb9169fb2968c53d659d61af9b2f709f4eb6f9880e2c7" 29 | 30 | [random] 31 | sha256 = "dd0c91e7125172eb8fd4568e15ad9fc7305643015e6ece4396c3cc5e8c2bf79a" 32 | sha512 = "d1ca2e7b0616a94a3b39d1b9450bb3fb595b01fd94a8626ad75433038dde40988ecb41ab93a374d569ab72163af3b30038d7bfc3499b9c07193181f4f1d9292a" 33 | 34 | [sockets] 35 | url = "https://github.com/WebAssembly/wasi-sockets/archive/main.tar.gz" 36 | sha256 = "2bc0f65a8046207ee3330ad7d63f6fafeafd4cc0ea4084f081bd5e4f7b177e74" 37 | sha512 = "3e5490e41547dffa78d52631825d93da8d60f4af0246cbaf97e1ecb879285953a86d5f1f390b10c32f91dd7eaec6f43e625a26b1c92c32a0c86fde428aedaaab" 38 | deps = ["io"] 39 | -------------------------------------------------------------------------------- /examples/github/wit/deps.toml: -------------------------------------------------------------------------------- 1 | # Use `wit-deps update` to pull in latest changes from "dynamic" branch references 2 | clocks = "https://github.com/WebAssembly/wasi-clocks/archive/main.tar.gz" 3 | http = "https://github.com/WebAssembly/wasi-http/archive/main.tar.gz" 4 | sockets = "https://github.com/WebAssembly/wasi-sockets/archive/main.tar.gz" 5 | 6 | # Pin a dependency to a particular revision and source digests. Each digest is optional 7 | [keyvalue] 8 | url = "https://github.com/WebAssembly/wasi-keyvalue/archive/v0.2.0-draft.tar.gz" 9 | sha256 = "8698bc1d8b4764674b753bb880ea584f453bdadb79fc676ced9eebd1c3d91fd9" 10 | sha512 = "38b9033c581f6e6c5ee1cbceff8fb2167847b46726e8b04b10ae66c97ae592fb2eb585f722efd9d07419651ec21d96fbcc313f156d20734f2fc3e273ba2815ac" 11 | -------------------------------------------------------------------------------- /examples/github/wit/deps/cli/command.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world command { 5 | @since(version = 0.2.0) 6 | include imports; 7 | 8 | @since(version = 0.2.0) 9 | export run; 10 | } 11 | -------------------------------------------------------------------------------- /examples/github/wit/deps/cli/environment.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface environment { 3 | /// Get the POSIX-style environment variables. 4 | /// 5 | /// Each environment variable is provided as a pair of string variable names 6 | /// and string value. 7 | /// 8 | /// Morally, these are a value import, but until value imports are available 9 | /// in the component model, this import function should return the same 10 | /// values each time it is called. 11 | @since(version = 0.2.0) 12 | get-environment: func() -> list>; 13 | 14 | /// Get the POSIX-style arguments to the program. 15 | @since(version = 0.2.0) 16 | get-arguments: func() -> list; 17 | 18 | /// Return a path that programs should use as their initial current working 19 | /// directory, interpreting `.` as shorthand for this. 20 | @since(version = 0.2.0) 21 | initial-cwd: func() -> option; 22 | } 23 | -------------------------------------------------------------------------------- /examples/github/wit/deps/cli/exit.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface exit { 3 | /// Exit the current instance and any linked instances. 4 | @since(version = 0.2.0) 5 | exit: func(status: result); 6 | 7 | /// Exit the current instance and any linked instances, reporting the 8 | /// specified status code to the host. 9 | /// 10 | /// The meaning of the code depends on the context, with 0 usually meaning 11 | /// "success", and other values indicating various types of failure. 12 | /// 13 | /// This function does not return; the effect is analogous to a trap, but 14 | /// without the connotation that something bad has happened. 15 | @unstable(feature = cli-exit-with-code) 16 | exit-with-code: func(status-code: u8); 17 | } 18 | -------------------------------------------------------------------------------- /examples/github/wit/deps/cli/imports.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | include wasi:clocks/imports@0.2.3; 7 | @since(version = 0.2.0) 8 | include wasi:filesystem/imports@0.2.3; 9 | @since(version = 0.2.0) 10 | include wasi:sockets/imports@0.2.3; 11 | @since(version = 0.2.0) 12 | include wasi:random/imports@0.2.3; 13 | @since(version = 0.2.0) 14 | include wasi:io/imports@0.2.3; 15 | 16 | @since(version = 0.2.0) 17 | import environment; 18 | @since(version = 0.2.0) 19 | import exit; 20 | @since(version = 0.2.0) 21 | import stdin; 22 | @since(version = 0.2.0) 23 | import stdout; 24 | @since(version = 0.2.0) 25 | import stderr; 26 | @since(version = 0.2.0) 27 | import terminal-input; 28 | @since(version = 0.2.0) 29 | import terminal-output; 30 | @since(version = 0.2.0) 31 | import terminal-stdin; 32 | @since(version = 0.2.0) 33 | import terminal-stdout; 34 | @since(version = 0.2.0) 35 | import terminal-stderr; 36 | } 37 | -------------------------------------------------------------------------------- /examples/github/wit/deps/cli/run.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface run { 3 | /// Run the program. 4 | @since(version = 0.2.0) 5 | run: func() -> result; 6 | } 7 | -------------------------------------------------------------------------------- /examples/github/wit/deps/cli/stdio.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface stdin { 3 | @since(version = 0.2.0) 4 | use wasi:io/streams@0.2.3.{input-stream}; 5 | 6 | @since(version = 0.2.0) 7 | get-stdin: func() -> input-stream; 8 | } 9 | 10 | @since(version = 0.2.0) 11 | interface stdout { 12 | @since(version = 0.2.0) 13 | use wasi:io/streams@0.2.3.{output-stream}; 14 | 15 | @since(version = 0.2.0) 16 | get-stdout: func() -> output-stream; 17 | } 18 | 19 | @since(version = 0.2.0) 20 | interface stderr { 21 | @since(version = 0.2.0) 22 | use wasi:io/streams@0.2.3.{output-stream}; 23 | 24 | @since(version = 0.2.0) 25 | get-stderr: func() -> output-stream; 26 | } 27 | -------------------------------------------------------------------------------- /examples/github/wit/deps/cli/terminal.wit: -------------------------------------------------------------------------------- 1 | /// Terminal input. 2 | /// 3 | /// In the future, this may include functions for disabling echoing, 4 | /// disabling input buffering so that keyboard events are sent through 5 | /// immediately, querying supported features, and so on. 6 | @since(version = 0.2.0) 7 | interface terminal-input { 8 | /// The input side of a terminal. 9 | @since(version = 0.2.0) 10 | resource terminal-input; 11 | } 12 | 13 | /// Terminal output. 14 | /// 15 | /// In the future, this may include functions for querying the terminal 16 | /// size, being notified of terminal size changes, querying supported 17 | /// features, and so on. 18 | @since(version = 0.2.0) 19 | interface terminal-output { 20 | /// The output side of a terminal. 21 | @since(version = 0.2.0) 22 | resource terminal-output; 23 | } 24 | 25 | /// An interface providing an optional `terminal-input` for stdin as a 26 | /// link-time authority. 27 | @since(version = 0.2.0) 28 | interface terminal-stdin { 29 | @since(version = 0.2.0) 30 | use terminal-input.{terminal-input}; 31 | 32 | /// If stdin is connected to a terminal, return a `terminal-input` handle 33 | /// allowing further interaction with it. 34 | @since(version = 0.2.0) 35 | get-terminal-stdin: func() -> option; 36 | } 37 | 38 | /// An interface providing an optional `terminal-output` for stdout as a 39 | /// link-time authority. 40 | @since(version = 0.2.0) 41 | interface terminal-stdout { 42 | @since(version = 0.2.0) 43 | use terminal-output.{terminal-output}; 44 | 45 | /// If stdout is connected to a terminal, return a `terminal-output` handle 46 | /// allowing further interaction with it. 47 | @since(version = 0.2.0) 48 | get-terminal-stdout: func() -> option; 49 | } 50 | 51 | /// An interface providing an optional `terminal-output` for stderr as a 52 | /// link-time authority. 53 | @since(version = 0.2.0) 54 | interface terminal-stderr { 55 | @since(version = 0.2.0) 56 | use terminal-output.{terminal-output}; 57 | 58 | /// If stderr is connected to a terminal, return a `terminal-output` handle 59 | /// allowing further interaction with it. 60 | @since(version = 0.2.0) 61 | get-terminal-stderr: func() -> option; 62 | } 63 | -------------------------------------------------------------------------------- /examples/github/wit/deps/clocks/monotonic-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed 3 | /// time. 4 | /// 5 | /// It is intended to be portable at least between Unix-family platforms and 6 | /// Windows. 7 | /// 8 | /// A monotonic clock is a clock which has an unspecified initial value, and 9 | /// successive reads of the clock will produce non-decreasing values. 10 | @since(version = 0.2.0) 11 | interface monotonic-clock { 12 | @since(version = 0.2.0) 13 | use wasi:io/poll@0.2.3.{pollable}; 14 | 15 | /// An instant in time, in nanoseconds. An instant is relative to an 16 | /// unspecified initial value, and can only be compared to instances from 17 | /// the same monotonic-clock. 18 | @since(version = 0.2.0) 19 | type instant = u64; 20 | 21 | /// A duration of time, in nanoseconds. 22 | @since(version = 0.2.0) 23 | type duration = u64; 24 | 25 | /// Read the current value of the clock. 26 | /// 27 | /// The clock is monotonic, therefore calling this function repeatedly will 28 | /// produce a sequence of non-decreasing values. 29 | @since(version = 0.2.0) 30 | now: func() -> instant; 31 | 32 | /// Query the resolution of the clock. Returns the duration of time 33 | /// corresponding to a clock tick. 34 | @since(version = 0.2.0) 35 | resolution: func() -> duration; 36 | 37 | /// Create a `pollable` which will resolve once the specified instant 38 | /// has occurred. 39 | @since(version = 0.2.0) 40 | subscribe-instant: func( 41 | when: instant, 42 | ) -> pollable; 43 | 44 | /// Create a `pollable` that will resolve after the specified duration has 45 | /// elapsed from the time this function is invoked. 46 | @since(version = 0.2.0) 47 | subscribe-duration: func( 48 | when: duration, 49 | ) -> pollable; 50 | } 51 | -------------------------------------------------------------------------------- /examples/github/wit/deps/clocks/timezone.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | 3 | @unstable(feature = clocks-timezone) 4 | interface timezone { 5 | @unstable(feature = clocks-timezone) 6 | use wall-clock.{datetime}; 7 | 8 | /// Return information needed to display the given `datetime`. This includes 9 | /// the UTC offset, the time zone name, and a flag indicating whether 10 | /// daylight saving time is active. 11 | /// 12 | /// If the timezone cannot be determined for the given `datetime`, return a 13 | /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight 14 | /// saving time. 15 | @unstable(feature = clocks-timezone) 16 | display: func(when: datetime) -> timezone-display; 17 | 18 | /// The same as `display`, but only return the UTC offset. 19 | @unstable(feature = clocks-timezone) 20 | utc-offset: func(when: datetime) -> s32; 21 | 22 | /// Information useful for displaying the timezone of a specific `datetime`. 23 | /// 24 | /// This information may vary within a single `timezone` to reflect daylight 25 | /// saving time adjustments. 26 | @unstable(feature = clocks-timezone) 27 | record timezone-display { 28 | /// The number of seconds difference between UTC time and the local 29 | /// time of the timezone. 30 | /// 31 | /// The returned value will always be less than 86400 which is the 32 | /// number of seconds in a day (24*60*60). 33 | /// 34 | /// In implementations that do not expose an actual time zone, this 35 | /// should return 0. 36 | utc-offset: s32, 37 | 38 | /// The abbreviated name of the timezone to display to a user. The name 39 | /// `UTC` indicates Coordinated Universal Time. Otherwise, this should 40 | /// reference local standards for the name of the time zone. 41 | /// 42 | /// In implementations that do not expose an actual time zone, this 43 | /// should be the string `UTC`. 44 | /// 45 | /// In time zones that do not have an applicable name, a formatted 46 | /// representation of the UTC offset may be returned, such as `-04:00`. 47 | name: string, 48 | 49 | /// Whether daylight saving time is active. 50 | /// 51 | /// In implementations that do not expose an actual time zone, this 52 | /// should return false. 53 | in-daylight-saving-time: bool, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/github/wit/deps/clocks/wall-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | /// WASI Wall Clock is a clock API intended to let users query the current 3 | /// time. The name "wall" makes an analogy to a "clock on the wall", which 4 | /// is not necessarily monotonic as it may be reset. 5 | /// 6 | /// It is intended to be portable at least between Unix-family platforms and 7 | /// Windows. 8 | /// 9 | /// A wall clock is a clock which measures the date and time according to 10 | /// some external reference. 11 | /// 12 | /// External references may be reset, so this clock is not necessarily 13 | /// monotonic, making it unsuitable for measuring elapsed time. 14 | /// 15 | /// It is intended for reporting the current date and time for humans. 16 | @since(version = 0.2.0) 17 | interface wall-clock { 18 | /// A time and date in seconds plus nanoseconds. 19 | @since(version = 0.2.0) 20 | record datetime { 21 | seconds: u64, 22 | nanoseconds: u32, 23 | } 24 | 25 | /// Read the current value of the clock. 26 | /// 27 | /// This clock is not monotonic, therefore calling this function repeatedly 28 | /// will not necessarily produce a sequence of non-decreasing values. 29 | /// 30 | /// The returned timestamps represent the number of seconds since 31 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], 32 | /// also known as [Unix Time]. 33 | /// 34 | /// The nanoseconds field of the output is always less than 1000000000. 35 | /// 36 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 37 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time 38 | @since(version = 0.2.0) 39 | now: func() -> datetime; 40 | 41 | /// Query the resolution of the clock. 42 | /// 43 | /// The nanoseconds field of the output is always less than 1000000000. 44 | @since(version = 0.2.0) 45 | resolution: func() -> datetime; 46 | } 47 | -------------------------------------------------------------------------------- /examples/github/wit/deps/clocks/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import monotonic-clock; 7 | @since(version = 0.2.0) 8 | import wall-clock; 9 | @unstable(feature = clocks-timezone) 10 | import timezone; 11 | } 12 | -------------------------------------------------------------------------------- /examples/github/wit/deps/filesystem/preopens.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | interface preopens { 5 | @since(version = 0.2.0) 6 | use types.{descriptor}; 7 | 8 | /// Return the set of preopened directories, and their paths. 9 | @since(version = 0.2.0) 10 | get-directories: func() -> list>; 11 | } 12 | -------------------------------------------------------------------------------- /examples/github/wit/deps/filesystem/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import types; 7 | @since(version = 0.2.0) 8 | import preopens; 9 | } 10 | -------------------------------------------------------------------------------- /examples/github/wit/deps/http/handler.wit: -------------------------------------------------------------------------------- 1 | /// This interface defines a handler of incoming HTTP Requests. It should 2 | /// be exported by components which can respond to HTTP Requests. 3 | @since(version = 0.2.0) 4 | interface incoming-handler { 5 | @since(version = 0.2.0) 6 | use types.{incoming-request, response-outparam}; 7 | 8 | /// This function is invoked with an incoming HTTP Request, and a resource 9 | /// `response-outparam` which provides the capability to reply with an HTTP 10 | /// Response. The response is sent by calling the `response-outparam.set` 11 | /// method, which allows execution to continue after the response has been 12 | /// sent. This enables both streaming to the response body, and performing other 13 | /// work. 14 | /// 15 | /// The implementor of this function must write a response to the 16 | /// `response-outparam` before returning, or else the caller will respond 17 | /// with an error on its behalf. 18 | @since(version = 0.2.0) 19 | handle: func( 20 | request: incoming-request, 21 | response-out: response-outparam 22 | ); 23 | } 24 | 25 | /// This interface defines a handler of outgoing HTTP Requests. It should be 26 | /// imported by components which wish to make HTTP Requests. 27 | @since(version = 0.2.0) 28 | interface outgoing-handler { 29 | @since(version = 0.2.0) 30 | use types.{ 31 | outgoing-request, request-options, future-incoming-response, error-code 32 | }; 33 | 34 | /// This function is invoked with an outgoing HTTP Request, and it returns 35 | /// a resource `future-incoming-response` which represents an HTTP Response 36 | /// which may arrive in the future. 37 | /// 38 | /// The `options` argument accepts optional parameters for the HTTP 39 | /// protocol's transport layer. 40 | /// 41 | /// This function may return an error if the `outgoing-request` is invalid 42 | /// or not allowed to be made. Otherwise, protocol errors are reported 43 | /// through the `future-incoming-response`. 44 | @since(version = 0.2.0) 45 | handle: func( 46 | request: outgoing-request, 47 | options: option 48 | ) -> result; 49 | } 50 | -------------------------------------------------------------------------------- /examples/github/wit/deps/http/proxy.wit: -------------------------------------------------------------------------------- 1 | package wasi:http@0.2.3; 2 | 3 | /// The `wasi:http/imports` world imports all the APIs for HTTP proxies. 4 | /// It is intended to be `include`d in other worlds. 5 | @since(version = 0.2.0) 6 | world imports { 7 | /// HTTP proxies have access to time and randomness. 8 | @since(version = 0.2.0) 9 | import wasi:clocks/monotonic-clock@0.2.3; 10 | @since(version = 0.2.0) 11 | import wasi:clocks/wall-clock@0.2.3; 12 | @since(version = 0.2.0) 13 | import wasi:random/random@0.2.3; 14 | 15 | /// Proxies have standard output and error streams which are expected to 16 | /// terminate in a developer-facing console provided by the host. 17 | @since(version = 0.2.0) 18 | import wasi:cli/stdout@0.2.3; 19 | @since(version = 0.2.0) 20 | import wasi:cli/stderr@0.2.3; 21 | 22 | /// TODO: this is a temporary workaround until component tooling is able to 23 | /// gracefully handle the absence of stdin. Hosts must return an eof stream 24 | /// for this import, which is what wasi-libc + tooling will do automatically 25 | /// when this import is properly removed. 26 | @since(version = 0.2.0) 27 | import wasi:cli/stdin@0.2.3; 28 | 29 | /// This is the default handler to use when user code simply wants to make an 30 | /// HTTP request (e.g., via `fetch()`). 31 | @since(version = 0.2.0) 32 | import outgoing-handler; 33 | } 34 | 35 | /// The `wasi:http/proxy` world captures a widely-implementable intersection of 36 | /// hosts that includes HTTP forward and reverse proxies. Components targeting 37 | /// this world may concurrently stream in and out any number of incoming and 38 | /// outgoing HTTP requests. 39 | @since(version = 0.2.0) 40 | world proxy { 41 | @since(version = 0.2.0) 42 | include imports; 43 | 44 | /// The host delivers incoming HTTP requests to a component by calling the 45 | /// `handle` function of this exported interface. A host may arbitrarily reuse 46 | /// or not reuse component instance when delivering incoming HTTP requests and 47 | /// thus a component must be able to handle 0..N calls to `handle`. 48 | @since(version = 0.2.0) 49 | export incoming-handler; 50 | } 51 | -------------------------------------------------------------------------------- /examples/github/wit/deps/io/error.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | interface error { 5 | /// A resource which represents some error information. 6 | /// 7 | /// The only method provided by this resource is `to-debug-string`, 8 | /// which provides some human-readable information about the error. 9 | /// 10 | /// In the `wasi:io` package, this resource is returned through the 11 | /// `wasi:io/streams/stream-error` type. 12 | /// 13 | /// To provide more specific error information, other interfaces may 14 | /// offer functions to "downcast" this error into more specific types. For example, 15 | /// errors returned from streams derived from filesystem types can be described using 16 | /// the filesystem's own error-code type. This is done using the function 17 | /// `wasi:filesystem/types/filesystem-error-code`, which takes a `borrow` 18 | /// parameter and returns an `option`. 19 | /// 20 | /// The set of functions which can "downcast" an `error` into a more 21 | /// concrete type is open. 22 | @since(version = 0.2.0) 23 | resource error { 24 | /// Returns a string that is suitable to assist humans in debugging 25 | /// this error. 26 | /// 27 | /// WARNING: The returned string should not be consumed mechanically! 28 | /// It may change across platforms, hosts, or other implementation 29 | /// details. Parsing this string is a major platform-compatibility 30 | /// hazard. 31 | @since(version = 0.2.0) 32 | to-debug-string: func() -> string; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/github/wit/deps/io/poll.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | /// A poll API intended to let users wait for I/O events on multiple handles 4 | /// at once. 5 | @since(version = 0.2.0) 6 | interface poll { 7 | /// `pollable` represents a single I/O event which may be ready, or not. 8 | @since(version = 0.2.0) 9 | resource pollable { 10 | 11 | /// Return the readiness of a pollable. This function never blocks. 12 | /// 13 | /// Returns `true` when the pollable is ready, and `false` otherwise. 14 | @since(version = 0.2.0) 15 | ready: func() -> bool; 16 | 17 | /// `block` returns immediately if the pollable is ready, and otherwise 18 | /// blocks until ready. 19 | /// 20 | /// This function is equivalent to calling `poll.poll` on a list 21 | /// containing only this pollable. 22 | @since(version = 0.2.0) 23 | block: func(); 24 | } 25 | 26 | /// Poll for completion on a set of pollables. 27 | /// 28 | /// This function takes a list of pollables, which identify I/O sources of 29 | /// interest, and waits until one or more of the events is ready for I/O. 30 | /// 31 | /// The result `list` contains one or more indices of handles in the 32 | /// argument list that is ready for I/O. 33 | /// 34 | /// This function traps if either: 35 | /// - the list is empty, or: 36 | /// - the list contains more elements than can be indexed with a `u32` value. 37 | /// 38 | /// A timeout can be implemented by adding a pollable from the 39 | /// wasi-clocks API to the list. 40 | /// 41 | /// This function does not return a `result`; polling in itself does not 42 | /// do any I/O so it doesn't fail. If any of the I/O sources identified by 43 | /// the pollables has an error, it is indicated by marking the source as 44 | /// being ready for I/O. 45 | @since(version = 0.2.0) 46 | poll: func(in: list>) -> list; 47 | } 48 | -------------------------------------------------------------------------------- /examples/github/wit/deps/io/streams.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | /// WASI I/O is an I/O abstraction API which is currently focused on providing 4 | /// stream types. 5 | /// 6 | /// In the future, the component model is expected to add built-in stream types; 7 | /// when it does, they are expected to subsume this API. 8 | @since(version = 0.2.0) 9 | interface streams { 10 | @since(version = 0.2.0) 11 | use error.{error}; 12 | @since(version = 0.2.0) 13 | use poll.{pollable}; 14 | 15 | /// An error for input-stream and output-stream operations. 16 | @since(version = 0.2.0) 17 | variant stream-error { 18 | /// The last operation (a write or flush) failed before completion. 19 | /// 20 | /// More information is available in the `error` payload. 21 | /// 22 | /// After this, the stream will be closed. All future operations return 23 | /// `stream-error::closed`. 24 | last-operation-failed(error), 25 | /// The stream is closed: no more input will be accepted by the 26 | /// stream. A closed output-stream will return this error on all 27 | /// future operations. 28 | closed 29 | } 30 | 31 | /// An input bytestream. 32 | /// 33 | /// `input-stream`s are *non-blocking* to the extent practical on underlying 34 | /// platforms. I/O operations always return promptly; if fewer bytes are 35 | /// promptly available than requested, they return the number of bytes promptly 36 | /// available, which could even be zero. To wait for data to be available, 37 | /// use the `subscribe` function to obtain a `pollable` which can be polled 38 | /// for using `wasi:io/poll`. 39 | @since(version = 0.2.0) 40 | resource input-stream { 41 | /// Perform a non-blocking read from the stream. 42 | /// 43 | /// When the source of a `read` is binary data, the bytes from the source 44 | /// are returned verbatim. When the source of a `read` is known to the 45 | /// implementation to be text, bytes containing the UTF-8 encoding of the 46 | /// text are returned. 47 | /// 48 | /// This function returns a list of bytes containing the read data, 49 | /// when successful. The returned list will contain up to `len` bytes; 50 | /// it may return fewer than requested, but not more. The list is 51 | /// empty when no bytes are available for reading at this time. The 52 | /// pollable given by `subscribe` will be ready when more bytes are 53 | /// available. 54 | /// 55 | /// This function fails with a `stream-error` when the operation 56 | /// encounters an error, giving `last-operation-failed`, or when the 57 | /// stream is closed, giving `closed`. 58 | /// 59 | /// When the caller gives a `len` of 0, it represents a request to 60 | /// read 0 bytes. If the stream is still open, this call should 61 | /// succeed and return an empty list, or otherwise fail with `closed`. 62 | /// 63 | /// The `len` parameter is a `u64`, which could represent a list of u8 which 64 | /// is not possible to allocate in wasm32, or not desirable to allocate as 65 | /// as a return value by the callee. The callee may return a list of bytes 66 | /// less than `len` in size while more bytes are available for reading. 67 | @since(version = 0.2.0) 68 | read: func( 69 | /// The maximum number of bytes to read 70 | len: u64 71 | ) -> result, stream-error>; 72 | 73 | /// Read bytes from a stream, after blocking until at least one byte can 74 | /// be read. Except for blocking, behavior is identical to `read`. 75 | @since(version = 0.2.0) 76 | blocking-read: func( 77 | /// The maximum number of bytes to read 78 | len: u64 79 | ) -> result, stream-error>; 80 | 81 | /// Skip bytes from a stream. Returns number of bytes skipped. 82 | /// 83 | /// Behaves identical to `read`, except instead of returning a list 84 | /// of bytes, returns the number of bytes consumed from the stream. 85 | @since(version = 0.2.0) 86 | skip: func( 87 | /// The maximum number of bytes to skip. 88 | len: u64, 89 | ) -> result; 90 | 91 | /// Skip bytes from a stream, after blocking until at least one byte 92 | /// can be skipped. Except for blocking behavior, identical to `skip`. 93 | @since(version = 0.2.0) 94 | blocking-skip: func( 95 | /// The maximum number of bytes to skip. 96 | len: u64, 97 | ) -> result; 98 | 99 | /// Create a `pollable` which will resolve once either the specified stream 100 | /// has bytes available to read or the other end of the stream has been 101 | /// closed. 102 | /// The created `pollable` is a child resource of the `input-stream`. 103 | /// Implementations may trap if the `input-stream` is dropped before 104 | /// all derived `pollable`s created with this function are dropped. 105 | @since(version = 0.2.0) 106 | subscribe: func() -> pollable; 107 | } 108 | 109 | 110 | /// An output bytestream. 111 | /// 112 | /// `output-stream`s are *non-blocking* to the extent practical on 113 | /// underlying platforms. Except where specified otherwise, I/O operations also 114 | /// always return promptly, after the number of bytes that can be written 115 | /// promptly, which could even be zero. To wait for the stream to be ready to 116 | /// accept data, the `subscribe` function to obtain a `pollable` which can be 117 | /// polled for using `wasi:io/poll`. 118 | /// 119 | /// Dropping an `output-stream` while there's still an active write in 120 | /// progress may result in the data being lost. Before dropping the stream, 121 | /// be sure to fully flush your writes. 122 | @since(version = 0.2.0) 123 | resource output-stream { 124 | /// Check readiness for writing. This function never blocks. 125 | /// 126 | /// Returns the number of bytes permitted for the next call to `write`, 127 | /// or an error. Calling `write` with more bytes than this function has 128 | /// permitted will trap. 129 | /// 130 | /// When this function returns 0 bytes, the `subscribe` pollable will 131 | /// become ready when this function will report at least 1 byte, or an 132 | /// error. 133 | @since(version = 0.2.0) 134 | check-write: func() -> result; 135 | 136 | /// Perform a write. This function never blocks. 137 | /// 138 | /// When the destination of a `write` is binary data, the bytes from 139 | /// `contents` are written verbatim. When the destination of a `write` is 140 | /// known to the implementation to be text, the bytes of `contents` are 141 | /// transcoded from UTF-8 into the encoding of the destination and then 142 | /// written. 143 | /// 144 | /// Precondition: check-write gave permit of Ok(n) and contents has a 145 | /// length of less than or equal to n. Otherwise, this function will trap. 146 | /// 147 | /// returns Err(closed) without writing if the stream has closed since 148 | /// the last call to check-write provided a permit. 149 | @since(version = 0.2.0) 150 | write: func( 151 | contents: list 152 | ) -> result<_, stream-error>; 153 | 154 | /// Perform a write of up to 4096 bytes, and then flush the stream. Block 155 | /// until all of these operations are complete, or an error occurs. 156 | /// 157 | /// This is a convenience wrapper around the use of `check-write`, 158 | /// `subscribe`, `write`, and `flush`, and is implemented with the 159 | /// following pseudo-code: 160 | /// 161 | /// ```text 162 | /// let pollable = this.subscribe(); 163 | /// while !contents.is_empty() { 164 | /// // Wait for the stream to become writable 165 | /// pollable.block(); 166 | /// let Ok(n) = this.check-write(); // eliding error handling 167 | /// let len = min(n, contents.len()); 168 | /// let (chunk, rest) = contents.split_at(len); 169 | /// this.write(chunk ); // eliding error handling 170 | /// contents = rest; 171 | /// } 172 | /// this.flush(); 173 | /// // Wait for completion of `flush` 174 | /// pollable.block(); 175 | /// // Check for any errors that arose during `flush` 176 | /// let _ = this.check-write(); // eliding error handling 177 | /// ``` 178 | @since(version = 0.2.0) 179 | blocking-write-and-flush: func( 180 | contents: list 181 | ) -> result<_, stream-error>; 182 | 183 | /// Request to flush buffered output. This function never blocks. 184 | /// 185 | /// This tells the output-stream that the caller intends any buffered 186 | /// output to be flushed. the output which is expected to be flushed 187 | /// is all that has been passed to `write` prior to this call. 188 | /// 189 | /// Upon calling this function, the `output-stream` will not accept any 190 | /// writes (`check-write` will return `ok(0)`) until the flush has 191 | /// completed. The `subscribe` pollable will become ready when the 192 | /// flush has completed and the stream can accept more writes. 193 | @since(version = 0.2.0) 194 | flush: func() -> result<_, stream-error>; 195 | 196 | /// Request to flush buffered output, and block until flush completes 197 | /// and stream is ready for writing again. 198 | @since(version = 0.2.0) 199 | blocking-flush: func() -> result<_, stream-error>; 200 | 201 | /// Create a `pollable` which will resolve once the output-stream 202 | /// is ready for more writing, or an error has occurred. When this 203 | /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an 204 | /// error. 205 | /// 206 | /// If the stream is closed, this pollable is always ready immediately. 207 | /// 208 | /// The created `pollable` is a child resource of the `output-stream`. 209 | /// Implementations may trap if the `output-stream` is dropped before 210 | /// all derived `pollable`s created with this function are dropped. 211 | @since(version = 0.2.0) 212 | subscribe: func() -> pollable; 213 | 214 | /// Write zeroes to a stream. 215 | /// 216 | /// This should be used precisely like `write` with the exact same 217 | /// preconditions (must use check-write first), but instead of 218 | /// passing a list of bytes, you simply pass the number of zero-bytes 219 | /// that should be written. 220 | @since(version = 0.2.0) 221 | write-zeroes: func( 222 | /// The number of zero-bytes to write 223 | len: u64 224 | ) -> result<_, stream-error>; 225 | 226 | /// Perform a write of up to 4096 zeroes, and then flush the stream. 227 | /// Block until all of these operations are complete, or an error 228 | /// occurs. 229 | /// 230 | /// This is a convenience wrapper around the use of `check-write`, 231 | /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with 232 | /// the following pseudo-code: 233 | /// 234 | /// ```text 235 | /// let pollable = this.subscribe(); 236 | /// while num_zeroes != 0 { 237 | /// // Wait for the stream to become writable 238 | /// pollable.block(); 239 | /// let Ok(n) = this.check-write(); // eliding error handling 240 | /// let len = min(n, num_zeroes); 241 | /// this.write-zeroes(len); // eliding error handling 242 | /// num_zeroes -= len; 243 | /// } 244 | /// this.flush(); 245 | /// // Wait for completion of `flush` 246 | /// pollable.block(); 247 | /// // Check for any errors that arose during `flush` 248 | /// let _ = this.check-write(); // eliding error handling 249 | /// ``` 250 | @since(version = 0.2.0) 251 | blocking-write-zeroes-and-flush: func( 252 | /// The number of zero-bytes to write 253 | len: u64 254 | ) -> result<_, stream-error>; 255 | 256 | /// Read from one stream and write to another. 257 | /// 258 | /// The behavior of splice is equivalent to: 259 | /// 1. calling `check-write` on the `output-stream` 260 | /// 2. calling `read` on the `input-stream` with the smaller of the 261 | /// `check-write` permitted length and the `len` provided to `splice` 262 | /// 3. calling `write` on the `output-stream` with that read data. 263 | /// 264 | /// Any error reported by the call to `check-write`, `read`, or 265 | /// `write` ends the splice and reports that error. 266 | /// 267 | /// This function returns the number of bytes transferred; it may be less 268 | /// than `len`. 269 | @since(version = 0.2.0) 270 | splice: func( 271 | /// The stream to read from 272 | src: borrow, 273 | /// The number of bytes to splice 274 | len: u64, 275 | ) -> result; 276 | 277 | /// Read from one stream and write to another, with blocking. 278 | /// 279 | /// This is similar to `splice`, except that it blocks until the 280 | /// `output-stream` is ready for writing, and the `input-stream` 281 | /// is ready for reading, before performing the `splice`. 282 | @since(version = 0.2.0) 283 | blocking-splice: func( 284 | /// The stream to read from 285 | src: borrow, 286 | /// The number of bytes to splice 287 | len: u64, 288 | ) -> result; 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /examples/github/wit/deps/io/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import streams; 7 | 8 | @since(version = 0.2.0) 9 | import poll; 10 | } 11 | -------------------------------------------------------------------------------- /examples/github/wit/deps/keyvalue/atomic.wit: -------------------------------------------------------------------------------- 1 | /// A keyvalue interface that provides atomic operations. 2 | /// 3 | /// Atomic operations are single, indivisible operations. When a fault causes an atomic operation to 4 | /// fail, it will appear to the invoker of the atomic operation that the action either completed 5 | /// successfully or did nothing at all. 6 | /// 7 | /// Please note that this interface is bare functions that take a reference to a bucket. This is to 8 | /// get around the current lack of a way to "extend" a resource with additional methods inside of 9 | /// wit. Future version of the interface will instead extend these methods on the base `bucket` 10 | /// resource. 11 | interface atomics { 12 | use store.{bucket, error}; 13 | 14 | /// Atomically increment the value associated with the key in the store by the given delta. It 15 | /// returns the new value. 16 | /// 17 | /// If the key does not exist in the store, it creates a new key-value pair with the value set 18 | /// to the given delta. 19 | /// 20 | /// If any other error occurs, it returns an `Err(error)`. 21 | increment: func(bucket: borrow, key: string, delta: u64) -> result; 22 | } -------------------------------------------------------------------------------- /examples/github/wit/deps/keyvalue/batch.wit: -------------------------------------------------------------------------------- 1 | /// A keyvalue interface that provides batch operations. 2 | /// 3 | /// A batch operation is an operation that operates on multiple keys at once. 4 | /// 5 | /// Batch operations are useful for reducing network round-trip time. For example, if you want to 6 | /// get the values associated with 100 keys, you can either do 100 get operations or you can do 1 7 | /// batch get operation. The batch operation is faster because it only needs to make 1 network call 8 | /// instead of 100. 9 | /// 10 | /// A batch operation does not guarantee atomicity, meaning that if the batch operation fails, some 11 | /// of the keys may have been modified and some may not. 12 | /// 13 | /// This interface does has the same consistency guarantees as the `store` interface, meaning that 14 | /// you should be able to "read your writes." 15 | /// 16 | /// Please note that this interface is bare functions that take a reference to a bucket. This is to 17 | /// get around the current lack of a way to "extend" a resource with additional methods inside of 18 | /// wit. Future version of the interface will instead extend these methods on the base `bucket` 19 | /// resource. 20 | interface batch { 21 | use store.{bucket, error}; 22 | 23 | /// Get the key-value pairs associated with the keys in the store. It returns a list of 24 | /// key-value pairs. 25 | /// 26 | /// If any of the keys do not exist in the store, it returns a `none` value for that pair in the 27 | /// list. 28 | /// 29 | /// MAY show an out-of-date value if there are concurrent writes to the store. 30 | /// 31 | /// If any other error occurs, it returns an `Err(error)`. 32 | get-many: func(bucket: borrow, keys: list) -> result>>>, error>; 33 | 34 | /// Set the values associated with the keys in the store. If the key already exists in the 35 | /// store, it overwrites the value. 36 | /// 37 | /// Note that the key-value pairs are not guaranteed to be set in the order they are provided. 38 | /// 39 | /// If any of the keys do not exist in the store, it creates a new key-value pair. 40 | /// 41 | /// If any other error occurs, it returns an `Err(error)`. When an error occurs, it does not 42 | /// rollback the key-value pairs that were already set. Thus, this batch operation does not 43 | /// guarantee atomicity, implying that some key-value pairs could be set while others might 44 | /// fail. 45 | /// 46 | /// Other concurrent operations may also be able to see the partial results. 47 | set-many: func(bucket: borrow, key-values: list>>) -> result<_, error>; 48 | 49 | /// Delete the key-value pairs associated with the keys in the store. 50 | /// 51 | /// Note that the key-value pairs are not guaranteed to be deleted in the order they are 52 | /// provided. 53 | /// 54 | /// If any of the keys do not exist in the store, it skips the key. 55 | /// 56 | /// If any other error occurs, it returns an `Err(error)`. When an error occurs, it does not 57 | /// rollback the key-value pairs that were already deleted. Thus, this batch operation does not 58 | /// guarantee atomicity, implying that some key-value pairs could be deleted while others might 59 | /// fail. 60 | /// 61 | /// Other concurrent operations may also be able to see the partial results. 62 | delete-many: func(bucket: borrow, keys: list) -> result<_, error>; 63 | } 64 | -------------------------------------------------------------------------------- /examples/github/wit/deps/keyvalue/store.wit: -------------------------------------------------------------------------------- 1 | /// A keyvalue interface that provides eventually consistent key-value operations. 2 | /// 3 | /// Each of these operations acts on a single key-value pair. 4 | /// 5 | /// The value in the key-value pair is defined as a `u8` byte array and the intention is that it is 6 | /// the common denominator for all data types defined by different key-value stores to handle data, 7 | /// ensuring compatibility between different key-value stores. Note: the clients will be expecting 8 | /// serialization/deserialization overhead to be handled by the key-value store. The value could be 9 | /// a serialized object from JSON, HTML or vendor-specific data types like AWS S3 objects. 10 | /// 11 | /// Data consistency in a key value store refers to the guarantee that once a write operation 12 | /// completes, all subsequent read operations will return the value that was written. 13 | /// 14 | /// Any implementation of this interface must have enough consistency to guarantee "reading your 15 | /// writes." In particular, this means that the client should never get a value that is older than 16 | /// the one it wrote, but it MAY get a newer value if one was written around the same time. These 17 | /// guarantees only apply to the same client (which will likely be provided by the host or an 18 | /// external capability of some kind). In this context a "client" is referring to the caller or 19 | /// guest that is consuming this interface. Once a write request is committed by a specific client, 20 | /// all subsequent read requests by the same client will reflect that write or any subsequent 21 | /// writes. Another client running in a different context may or may not immediately see the result 22 | /// due to the replication lag. As an example of all of this, if a value at a given key is A, and 23 | /// the client writes B, then immediately reads, it should get B. If something else writes C in 24 | /// quick succession, then the client may get C. However, a client running in a separate context may 25 | /// still see A or B 26 | interface store { 27 | /// The set of errors which may be raised by functions in this package 28 | variant error { 29 | /// The host does not recognize the store identifier requested. 30 | no-such-store, 31 | 32 | /// The requesting component does not have access to the specified store 33 | /// (which may or may not exist). 34 | access-denied, 35 | 36 | /// Some implementation-specific error has occurred (e.g. I/O) 37 | other(string) 38 | } 39 | 40 | /// A response to a `list-keys` operation. 41 | record key-response { 42 | /// The list of keys returned by the query. 43 | keys: list, 44 | /// The continuation token to use to fetch the next page of keys. If this is `null`, then 45 | /// there are no more keys to fetch. 46 | cursor: option 47 | } 48 | 49 | /// Get the bucket with the specified identifier. 50 | /// 51 | /// `identifier` must refer to a bucket provided by the host. 52 | /// 53 | /// `error::no-such-store` will be raised if the `identifier` is not recognized. 54 | open: func(identifier: string) -> result; 55 | 56 | /// A bucket is a collection of key-value pairs. Each key-value pair is stored as a entry in the 57 | /// bucket, and the bucket itself acts as a collection of all these entries. 58 | /// 59 | /// It is worth noting that the exact terminology for bucket in key-value stores can very 60 | /// depending on the specific implementation. For example: 61 | /// 62 | /// 1. Amazon DynamoDB calls a collection of key-value pairs a table 63 | /// 2. Redis has hashes, sets, and sorted sets as different types of collections 64 | /// 3. Cassandra calls a collection of key-value pairs a column family 65 | /// 4. MongoDB calls a collection of key-value pairs a collection 66 | /// 5. Riak calls a collection of key-value pairs a bucket 67 | /// 6. Memcached calls a collection of key-value pairs a slab 68 | /// 7. Azure Cosmos DB calls a collection of key-value pairs a container 69 | /// 70 | /// In this interface, we use the term `bucket` to refer to a collection of key-value pairs 71 | resource bucket { 72 | /// Get the value associated with the specified `key` 73 | /// 74 | /// The value is returned as an option. If the key-value pair exists in the 75 | /// store, it returns `Ok(value)`. If the key does not exist in the 76 | /// store, it returns `Ok(none)`. 77 | /// 78 | /// If any other error occurs, it returns an `Err(error)`. 79 | get: func(key: string) -> result>, error>; 80 | 81 | /// Set the value associated with the key in the store. If the key already 82 | /// exists in the store, it overwrites the value. 83 | /// 84 | /// If the key does not exist in the store, it creates a new key-value pair. 85 | /// 86 | /// If any other error occurs, it returns an `Err(error)`. 87 | set: func(key: string, value: list) -> result<_, error>; 88 | 89 | /// Delete the key-value pair associated with the key in the store. 90 | /// 91 | /// If the key does not exist in the store, it does nothing. 92 | /// 93 | /// If any other error occurs, it returns an `Err(error)`. 94 | delete: func(key: string) -> result<_, error>; 95 | 96 | /// Check if the key exists in the store. 97 | /// 98 | /// If the key exists in the store, it returns `Ok(true)`. If the key does 99 | /// not exist in the store, it returns `Ok(false)`. 100 | /// 101 | /// If any other error occurs, it returns an `Err(error)`. 102 | exists: func(key: string) -> result; 103 | 104 | /// Get all the keys in the store with an optional cursor (for use in pagination). It 105 | /// returns a list of keys. Please note that for most KeyValue implementations, this is a 106 | /// can be a very expensive operation and so it should be used judiciously. Implementations 107 | /// can return any number of keys in a single response, but they should never attempt to 108 | /// send more data than is reasonable (i.e. on a small edge device, this may only be a few 109 | /// KB, while on a large machine this could be several MB). Any response should also return 110 | /// a cursor that can be used to fetch the next page of keys. See the `key-response` record 111 | /// for more information. 112 | /// 113 | /// Note that the keys are not guaranteed to be returned in any particular order. 114 | /// 115 | /// If the store is empty, it returns an empty list. 116 | /// 117 | /// MAY show an out-of-date list of keys if there are concurrent writes to the store. 118 | /// 119 | /// If any error occurs, it returns an `Err(error)`. 120 | list-keys: func(cursor: option) -> result; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /examples/github/wit/deps/keyvalue/watch.wit: -------------------------------------------------------------------------------- 1 | /// A keyvalue interface that provides watch operations. 2 | /// 3 | /// This interface is used to provide event-driven mechanisms to handle 4 | /// keyvalue changes. 5 | interface watcher { 6 | /// A keyvalue interface that provides handle-watch operations. 7 | use store.{bucket}; 8 | 9 | /// Handle the `set` event for the given bucket and key. It includes a reference to the `bucket` 10 | /// that can be used to interact with the store. 11 | on-set: func(bucket: bucket, key: string, value: list); 12 | 13 | /// Handle the `delete` event for the given bucket and key. It includes a reference to the 14 | /// `bucket` that can be used to interact with the store. 15 | on-delete: func(bucket: bucket, key: string); 16 | } -------------------------------------------------------------------------------- /examples/github/wit/deps/keyvalue/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:keyvalue@0.2.0-draft; 2 | 3 | /// The `wasi:keyvalue/imports` world provides common APIs for interacting with key-value stores. 4 | /// Components targeting this world will be able to do: 5 | /// 6 | /// 1. CRUD (create, read, update, delete) operations on key-value stores. 7 | /// 2. Atomic `increment` and CAS (compare-and-swap) operations. 8 | /// 3. Batch operations that can reduce the number of round trips to the network. 9 | world imports { 10 | /// The `store` capability allows the component to perform eventually consistent operations on 11 | /// the key-value store. 12 | import store; 13 | 14 | /// The `atomic` capability allows the component to perform atomic / `increment` and CAS 15 | /// (compare-and-swap) operations. 16 | import atomics; 17 | 18 | /// The `batch` capability allows the component to perform eventually consistent batch 19 | /// operations that can reduce the number of round trips to the network. 20 | import batch; 21 | } 22 | 23 | world watch-service { 24 | include imports; 25 | export watcher; 26 | } -------------------------------------------------------------------------------- /examples/github/wit/deps/random/insecure-seed.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | /// The insecure-seed interface for seeding hash-map DoS resistance. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | @since(version = 0.2.0) 7 | interface insecure-seed { 8 | /// Return a 128-bit value that may contain a pseudo-random value. 9 | /// 10 | /// The returned value is not required to be computed from a CSPRNG, and may 11 | /// even be entirely deterministic. Host implementations are encouraged to 12 | /// provide pseudo-random values to any program exposed to 13 | /// attacker-controlled content, to enable DoS protection built into many 14 | /// languages' hash-map implementations. 15 | /// 16 | /// This function is intended to only be called once, by a source language 17 | /// to initialize Denial Of Service (DoS) protection in its hash-map 18 | /// implementation. 19 | /// 20 | /// # Expected future evolution 21 | /// 22 | /// This will likely be changed to a value import, to prevent it from being 23 | /// called multiple times and potentially used for purposes other than DoS 24 | /// protection. 25 | @since(version = 0.2.0) 26 | insecure-seed: func() -> tuple; 27 | } 28 | -------------------------------------------------------------------------------- /examples/github/wit/deps/random/insecure.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | /// The insecure interface for insecure pseudo-random numbers. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | @since(version = 0.2.0) 7 | interface insecure { 8 | /// Return `len` insecure pseudo-random bytes. 9 | /// 10 | /// This function is not cryptographically secure. Do not use it for 11 | /// anything related to security. 12 | /// 13 | /// There are no requirements on the values of the returned bytes, however 14 | /// implementations are encouraged to return evenly distributed values with 15 | /// a long period. 16 | @since(version = 0.2.0) 17 | get-insecure-random-bytes: func(len: u64) -> list; 18 | 19 | /// Return an insecure pseudo-random `u64` value. 20 | /// 21 | /// This function returns the same type of pseudo-random data as 22 | /// `get-insecure-random-bytes`, represented as a `u64`. 23 | @since(version = 0.2.0) 24 | get-insecure-random-u64: func() -> u64; 25 | } 26 | -------------------------------------------------------------------------------- /examples/github/wit/deps/random/random.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | /// WASI Random is a random data API. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | @since(version = 0.2.0) 7 | interface random { 8 | /// Return `len` cryptographically-secure random or pseudo-random bytes. 9 | /// 10 | /// This function must produce data at least as cryptographically secure and 11 | /// fast as an adequately seeded cryptographically-secure pseudo-random 12 | /// number generator (CSPRNG). It must not block, from the perspective of 13 | /// the calling program, under any circumstances, including on the first 14 | /// request and on requests for numbers of bytes. The returned data must 15 | /// always be unpredictable. 16 | /// 17 | /// This function must always return fresh data. Deterministic environments 18 | /// must omit this function, rather than implementing it with deterministic 19 | /// data. 20 | @since(version = 0.2.0) 21 | get-random-bytes: func(len: u64) -> list; 22 | 23 | /// Return a cryptographically-secure random or pseudo-random `u64` value. 24 | /// 25 | /// This function returns the same type of data as `get-random-bytes`, 26 | /// represented as a `u64`. 27 | @since(version = 0.2.0) 28 | get-random-u64: func() -> u64; 29 | } 30 | -------------------------------------------------------------------------------- /examples/github/wit/deps/random/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import random; 7 | 8 | @since(version = 0.2.0) 9 | import insecure; 10 | 11 | @since(version = 0.2.0) 12 | import insecure-seed; 13 | } 14 | -------------------------------------------------------------------------------- /examples/github/wit/deps/sockets/instance-network.wit: -------------------------------------------------------------------------------- 1 | 2 | /// This interface provides a value-export of the default network handle.. 3 | @since(version = 0.2.0) 4 | interface instance-network { 5 | @since(version = 0.2.0) 6 | use network.{network}; 7 | 8 | /// Get a handle to the default network. 9 | @since(version = 0.2.0) 10 | instance-network: func() -> network; 11 | } 12 | -------------------------------------------------------------------------------- /examples/github/wit/deps/sockets/ip-name-lookup.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface ip-name-lookup { 3 | @since(version = 0.2.0) 4 | use wasi:io/poll@0.2.3.{pollable}; 5 | @since(version = 0.2.0) 6 | use network.{network, error-code, ip-address}; 7 | 8 | /// Resolve an internet host name to a list of IP addresses. 9 | /// 10 | /// Unicode domain names are automatically converted to ASCII using IDNA encoding. 11 | /// If the input is an IP address string, the address is parsed and returned 12 | /// as-is without making any external requests. 13 | /// 14 | /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. 15 | /// 16 | /// This function never blocks. It either immediately fails or immediately 17 | /// returns successfully with a `resolve-address-stream` that can be used 18 | /// to (asynchronously) fetch the results. 19 | /// 20 | /// # Typical errors 21 | /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. 22 | /// 23 | /// # References: 24 | /// - 25 | /// - 26 | /// - 27 | /// - 28 | @since(version = 0.2.0) 29 | resolve-addresses: func(network: borrow, name: string) -> result; 30 | 31 | @since(version = 0.2.0) 32 | resource resolve-address-stream { 33 | /// Returns the next address from the resolver. 34 | /// 35 | /// This function should be called multiple times. On each call, it will 36 | /// return the next address in connection order preference. If all 37 | /// addresses have been exhausted, this function returns `none`. 38 | /// 39 | /// This function never returns IPv4-mapped IPv6 addresses. 40 | /// 41 | /// # Typical errors 42 | /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) 43 | /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) 44 | /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) 45 | /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) 46 | @since(version = 0.2.0) 47 | resolve-next-address: func() -> result, error-code>; 48 | 49 | /// Create a `pollable` which will resolve once the stream is ready for I/O. 50 | /// 51 | /// Note: this function is here for WASI 0.2 only. 52 | /// It's planned to be removed when `future` is natively supported in Preview3. 53 | @since(version = 0.2.0) 54 | subscribe: func() -> pollable; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/github/wit/deps/sockets/network.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface network { 3 | @unstable(feature = network-error-code) 4 | use wasi:io/error@0.2.3.{error}; 5 | 6 | /// An opaque resource that represents access to (a subset of) the network. 7 | /// This enables context-based security for networking. 8 | /// There is no need for this to map 1:1 to a physical network interface. 9 | @since(version = 0.2.0) 10 | resource network; 11 | 12 | /// Error codes. 13 | /// 14 | /// In theory, every API can return any error code. 15 | /// In practice, API's typically only return the errors documented per API 16 | /// combined with a couple of errors that are always possible: 17 | /// - `unknown` 18 | /// - `access-denied` 19 | /// - `not-supported` 20 | /// - `out-of-memory` 21 | /// - `concurrency-conflict` 22 | /// 23 | /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. 24 | @since(version = 0.2.0) 25 | enum error-code { 26 | /// Unknown error 27 | unknown, 28 | 29 | /// Access denied. 30 | /// 31 | /// POSIX equivalent: EACCES, EPERM 32 | access-denied, 33 | 34 | /// The operation is not supported. 35 | /// 36 | /// POSIX equivalent: EOPNOTSUPP 37 | not-supported, 38 | 39 | /// One of the arguments is invalid. 40 | /// 41 | /// POSIX equivalent: EINVAL 42 | invalid-argument, 43 | 44 | /// Not enough memory to complete the operation. 45 | /// 46 | /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY 47 | out-of-memory, 48 | 49 | /// The operation timed out before it could finish completely. 50 | timeout, 51 | 52 | /// This operation is incompatible with another asynchronous operation that is already in progress. 53 | /// 54 | /// POSIX equivalent: EALREADY 55 | concurrency-conflict, 56 | 57 | /// Trying to finish an asynchronous operation that: 58 | /// - has not been started yet, or: 59 | /// - was already finished by a previous `finish-*` call. 60 | /// 61 | /// Note: this is scheduled to be removed when `future`s are natively supported. 62 | not-in-progress, 63 | 64 | /// The operation has been aborted because it could not be completed immediately. 65 | /// 66 | /// Note: this is scheduled to be removed when `future`s are natively supported. 67 | would-block, 68 | 69 | 70 | /// The operation is not valid in the socket's current state. 71 | invalid-state, 72 | 73 | /// A new socket resource could not be created because of a system limit. 74 | new-socket-limit, 75 | 76 | /// A bind operation failed because the provided address is not an address that the `network` can bind to. 77 | address-not-bindable, 78 | 79 | /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. 80 | address-in-use, 81 | 82 | /// The remote address is not reachable 83 | remote-unreachable, 84 | 85 | 86 | /// The TCP connection was forcefully rejected 87 | connection-refused, 88 | 89 | /// The TCP connection was reset. 90 | connection-reset, 91 | 92 | /// A TCP connection was aborted. 93 | connection-aborted, 94 | 95 | 96 | /// The size of a datagram sent to a UDP socket exceeded the maximum 97 | /// supported size. 98 | datagram-too-large, 99 | 100 | 101 | /// Name does not exist or has no suitable associated IP addresses. 102 | name-unresolvable, 103 | 104 | /// A temporary failure in name resolution occurred. 105 | temporary-resolver-failure, 106 | 107 | /// A permanent failure in name resolution occurred. 108 | permanent-resolver-failure, 109 | } 110 | 111 | /// Attempts to extract a network-related `error-code` from the stream 112 | /// `error` provided. 113 | /// 114 | /// Stream operations which return `stream-error::last-operation-failed` 115 | /// have a payload with more information about the operation that failed. 116 | /// This payload can be passed through to this function to see if there's 117 | /// network-related information about the error to return. 118 | /// 119 | /// Note that this function is fallible because not all stream-related 120 | /// errors are network-related errors. 121 | @unstable(feature = network-error-code) 122 | network-error-code: func(err: borrow) -> option; 123 | 124 | @since(version = 0.2.0) 125 | enum ip-address-family { 126 | /// Similar to `AF_INET` in POSIX. 127 | ipv4, 128 | 129 | /// Similar to `AF_INET6` in POSIX. 130 | ipv6, 131 | } 132 | 133 | @since(version = 0.2.0) 134 | type ipv4-address = tuple; 135 | @since(version = 0.2.0) 136 | type ipv6-address = tuple; 137 | 138 | @since(version = 0.2.0) 139 | variant ip-address { 140 | ipv4(ipv4-address), 141 | ipv6(ipv6-address), 142 | } 143 | 144 | @since(version = 0.2.0) 145 | record ipv4-socket-address { 146 | /// sin_port 147 | port: u16, 148 | /// sin_addr 149 | address: ipv4-address, 150 | } 151 | 152 | @since(version = 0.2.0) 153 | record ipv6-socket-address { 154 | /// sin6_port 155 | port: u16, 156 | /// sin6_flowinfo 157 | flow-info: u32, 158 | /// sin6_addr 159 | address: ipv6-address, 160 | /// sin6_scope_id 161 | scope-id: u32, 162 | } 163 | 164 | @since(version = 0.2.0) 165 | variant ip-socket-address { 166 | ipv4(ipv4-socket-address), 167 | ipv6(ipv6-socket-address), 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /examples/github/wit/deps/sockets/tcp-create-socket.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface tcp-create-socket { 3 | @since(version = 0.2.0) 4 | use network.{network, error-code, ip-address-family}; 5 | @since(version = 0.2.0) 6 | use tcp.{tcp-socket}; 7 | 8 | /// Create a new TCP socket. 9 | /// 10 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. 11 | /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 12 | /// 13 | /// This function does not require a network capability handle. This is considered to be safe because 14 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` 15 | /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 16 | /// 17 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 18 | /// 19 | /// # Typical errors 20 | /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 21 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 22 | /// 23 | /// # References 24 | /// - 25 | /// - 26 | /// - 27 | /// - 28 | @since(version = 0.2.0) 29 | create-tcp-socket: func(address-family: ip-address-family) -> result; 30 | } 31 | -------------------------------------------------------------------------------- /examples/github/wit/deps/sockets/udp-create-socket.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface udp-create-socket { 3 | @since(version = 0.2.0) 4 | use network.{network, error-code, ip-address-family}; 5 | @since(version = 0.2.0) 6 | use udp.{udp-socket}; 7 | 8 | /// Create a new UDP socket. 9 | /// 10 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. 11 | /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 12 | /// 13 | /// This function does not require a network capability handle. This is considered to be safe because 14 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, 15 | /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 16 | /// 17 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 18 | /// 19 | /// # Typical errors 20 | /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 21 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 22 | /// 23 | /// # References: 24 | /// - 25 | /// - 26 | /// - 27 | /// - 28 | @since(version = 0.2.0) 29 | create-udp-socket: func(address-family: ip-address-family) -> result; 30 | } 31 | -------------------------------------------------------------------------------- /examples/github/wit/deps/sockets/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:sockets@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import instance-network; 7 | @since(version = 0.2.0) 8 | import network; 9 | @since(version = 0.2.0) 10 | import udp; 11 | @since(version = 0.2.0) 12 | import udp-create-socket; 13 | @since(version = 0.2.0) 14 | import tcp; 15 | @since(version = 0.2.0) 16 | import tcp-create-socket; 17 | @since(version = 0.2.0) 18 | import ip-name-lookup; 19 | } 20 | -------------------------------------------------------------------------------- /examples/github/wit/world.wit: -------------------------------------------------------------------------------- 1 | package examples:github; 2 | 3 | world github { 4 | import wasi:clocks/monotonic-clock@0.2.3; 5 | import wasi:http/incoming-handler@0.2.3; 6 | import wasi:sockets/network@0.2.3; 7 | import wasi:keyvalue/store@0.2.0-draft; 8 | } 9 | -------------------------------------------------------------------------------- /examples/http/main.rs: -------------------------------------------------------------------------------- 1 | wit_bindgen::generate!({ 2 | path: "examples/http/wit", 3 | world: "examples:http/http", 4 | generate_all, 5 | }); 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /examples/http/wit/deps.lock: -------------------------------------------------------------------------------- 1 | [cli] 2 | sha256 = "4dadd13d55aaf626833d1f4b9c34a17b0f04e993babd09552b785cda3b95ea76" 3 | sha512 = "898dcc4e8c15d18acc6b88dbe232336fa4d19019430a910dbc9e7aeaace3077a164af3be9f002de6e7e65ef693df340801ac0c7e421e9a746bf1b6d698a90835" 4 | 5 | [clocks] 6 | sha256 = "93a701968a7dd3c5d69031bc0601681c468972fdf7e28a93bb6150a67d6ebe8b" 7 | sha512 = "98fca567c7a01887b0fb38981f1772169b6ea8de475b546508f8b86738d84e44ba95cae81def40ac34e8809f5f60e85224077ab8cb6d6d5d6296acc1df73c159" 8 | 9 | [filesystem] 10 | sha256 = "69d42fb10a04a33545b17e055f13db9b1e10e82ba0ed5bdb52334e40dc07c679" 11 | sha512 = "612effbac6f4804fe0c29dae20b78bbba59e52cb754c15402f5fe229c3153a221e0fbdff1d9d00ceaa3fe049c6a95523a5b99f772f1c16d972eade2c88326a30" 12 | 13 | [http] 14 | url = "https://github.com/WebAssembly/wasi-http/archive/v0.2.3.tar.gz" 15 | sha256 = "72d3a00dbf39eed40a134e8b1dee85834961153f9d205ee4dd56657270c084ce" 16 | sha512 = "636150c464c0eb3d60bd212fc5d4012638c8cd4f89b583b87a38154ef99de828aac4296ac13c5cface10ee61e164fcfc43a5c104f916229dfdf49c0d11047677" 17 | deps = ["cli", "clocks", "filesystem", "io", "random", "sockets"] 18 | 19 | [io] 20 | sha256 = "1cccbfe4122686ea57a25cd368e8cdfc408cbcad089f47fb6685b6f92e96f050" 21 | sha512 = "7a95f964c13da52611141acd89bc8876226497f128e99dd176a4270c5b5efbd8cc847b5fbd1a91258d028c646db99e0424d72590cf1caf20f9f3a3343fad5017" 22 | 23 | [random] 24 | sha256 = "dd0c91e7125172eb8fd4568e15ad9fc7305643015e6ece4396c3cc5e8c2bf79a" 25 | sha512 = "d1ca2e7b0616a94a3b39d1b9450bb3fb595b01fd94a8626ad75433038dde40988ecb41ab93a374d569ab72163af3b30038d7bfc3499b9c07193181f4f1d9292a" 26 | 27 | [sockets] 28 | sha256 = "2bc0f65a8046207ee3330ad7d63f6fafeafd4cc0ea4084f081bd5e4f7b177e74" 29 | sha512 = "3e5490e41547dffa78d52631825d93da8d60f4af0246cbaf97e1ecb879285953a86d5f1f390b10c32f91dd7eaec6f43e625a26b1c92c32a0c86fde428aedaaab" 30 | -------------------------------------------------------------------------------- /examples/http/wit/deps.toml: -------------------------------------------------------------------------------- 1 | http = "https://github.com/WebAssembly/wasi-http/archive/v0.2.3.tar.gz" 2 | -------------------------------------------------------------------------------- /examples/http/wit/deps/cli/command.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world command { 5 | @since(version = 0.2.0) 6 | include imports; 7 | 8 | @since(version = 0.2.0) 9 | export run; 10 | } 11 | -------------------------------------------------------------------------------- /examples/http/wit/deps/cli/environment.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface environment { 3 | /// Get the POSIX-style environment variables. 4 | /// 5 | /// Each environment variable is provided as a pair of string variable names 6 | /// and string value. 7 | /// 8 | /// Morally, these are a value import, but until value imports are available 9 | /// in the component model, this import function should return the same 10 | /// values each time it is called. 11 | @since(version = 0.2.0) 12 | get-environment: func() -> list>; 13 | 14 | /// Get the POSIX-style arguments to the program. 15 | @since(version = 0.2.0) 16 | get-arguments: func() -> list; 17 | 18 | /// Return a path that programs should use as their initial current working 19 | /// directory, interpreting `.` as shorthand for this. 20 | @since(version = 0.2.0) 21 | initial-cwd: func() -> option; 22 | } 23 | -------------------------------------------------------------------------------- /examples/http/wit/deps/cli/exit.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface exit { 3 | /// Exit the current instance and any linked instances. 4 | @since(version = 0.2.0) 5 | exit: func(status: result); 6 | 7 | /// Exit the current instance and any linked instances, reporting the 8 | /// specified status code to the host. 9 | /// 10 | /// The meaning of the code depends on the context, with 0 usually meaning 11 | /// "success", and other values indicating various types of failure. 12 | /// 13 | /// This function does not return; the effect is analogous to a trap, but 14 | /// without the connotation that something bad has happened. 15 | @unstable(feature = cli-exit-with-code) 16 | exit-with-code: func(status-code: u8); 17 | } 18 | -------------------------------------------------------------------------------- /examples/http/wit/deps/cli/imports.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | include wasi:clocks/imports@0.2.3; 7 | @since(version = 0.2.0) 8 | include wasi:filesystem/imports@0.2.3; 9 | @since(version = 0.2.0) 10 | include wasi:sockets/imports@0.2.3; 11 | @since(version = 0.2.0) 12 | include wasi:random/imports@0.2.3; 13 | @since(version = 0.2.0) 14 | include wasi:io/imports@0.2.3; 15 | 16 | @since(version = 0.2.0) 17 | import environment; 18 | @since(version = 0.2.0) 19 | import exit; 20 | @since(version = 0.2.0) 21 | import stdin; 22 | @since(version = 0.2.0) 23 | import stdout; 24 | @since(version = 0.2.0) 25 | import stderr; 26 | @since(version = 0.2.0) 27 | import terminal-input; 28 | @since(version = 0.2.0) 29 | import terminal-output; 30 | @since(version = 0.2.0) 31 | import terminal-stdin; 32 | @since(version = 0.2.0) 33 | import terminal-stdout; 34 | @since(version = 0.2.0) 35 | import terminal-stderr; 36 | } 37 | -------------------------------------------------------------------------------- /examples/http/wit/deps/cli/run.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface run { 3 | /// Run the program. 4 | @since(version = 0.2.0) 5 | run: func() -> result; 6 | } 7 | -------------------------------------------------------------------------------- /examples/http/wit/deps/cli/stdio.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface stdin { 3 | @since(version = 0.2.0) 4 | use wasi:io/streams@0.2.3.{input-stream}; 5 | 6 | @since(version = 0.2.0) 7 | get-stdin: func() -> input-stream; 8 | } 9 | 10 | @since(version = 0.2.0) 11 | interface stdout { 12 | @since(version = 0.2.0) 13 | use wasi:io/streams@0.2.3.{output-stream}; 14 | 15 | @since(version = 0.2.0) 16 | get-stdout: func() -> output-stream; 17 | } 18 | 19 | @since(version = 0.2.0) 20 | interface stderr { 21 | @since(version = 0.2.0) 22 | use wasi:io/streams@0.2.3.{output-stream}; 23 | 24 | @since(version = 0.2.0) 25 | get-stderr: func() -> output-stream; 26 | } 27 | -------------------------------------------------------------------------------- /examples/http/wit/deps/cli/terminal.wit: -------------------------------------------------------------------------------- 1 | /// Terminal input. 2 | /// 3 | /// In the future, this may include functions for disabling echoing, 4 | /// disabling input buffering so that keyboard events are sent through 5 | /// immediately, querying supported features, and so on. 6 | @since(version = 0.2.0) 7 | interface terminal-input { 8 | /// The input side of a terminal. 9 | @since(version = 0.2.0) 10 | resource terminal-input; 11 | } 12 | 13 | /// Terminal output. 14 | /// 15 | /// In the future, this may include functions for querying the terminal 16 | /// size, being notified of terminal size changes, querying supported 17 | /// features, and so on. 18 | @since(version = 0.2.0) 19 | interface terminal-output { 20 | /// The output side of a terminal. 21 | @since(version = 0.2.0) 22 | resource terminal-output; 23 | } 24 | 25 | /// An interface providing an optional `terminal-input` for stdin as a 26 | /// link-time authority. 27 | @since(version = 0.2.0) 28 | interface terminal-stdin { 29 | @since(version = 0.2.0) 30 | use terminal-input.{terminal-input}; 31 | 32 | /// If stdin is connected to a terminal, return a `terminal-input` handle 33 | /// allowing further interaction with it. 34 | @since(version = 0.2.0) 35 | get-terminal-stdin: func() -> option; 36 | } 37 | 38 | /// An interface providing an optional `terminal-output` for stdout as a 39 | /// link-time authority. 40 | @since(version = 0.2.0) 41 | interface terminal-stdout { 42 | @since(version = 0.2.0) 43 | use terminal-output.{terminal-output}; 44 | 45 | /// If stdout is connected to a terminal, return a `terminal-output` handle 46 | /// allowing further interaction with it. 47 | @since(version = 0.2.0) 48 | get-terminal-stdout: func() -> option; 49 | } 50 | 51 | /// An interface providing an optional `terminal-output` for stderr as a 52 | /// link-time authority. 53 | @since(version = 0.2.0) 54 | interface terminal-stderr { 55 | @since(version = 0.2.0) 56 | use terminal-output.{terminal-output}; 57 | 58 | /// If stderr is connected to a terminal, return a `terminal-output` handle 59 | /// allowing further interaction with it. 60 | @since(version = 0.2.0) 61 | get-terminal-stderr: func() -> option; 62 | } 63 | -------------------------------------------------------------------------------- /examples/http/wit/deps/clocks/monotonic-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed 3 | /// time. 4 | /// 5 | /// It is intended to be portable at least between Unix-family platforms and 6 | /// Windows. 7 | /// 8 | /// A monotonic clock is a clock which has an unspecified initial value, and 9 | /// successive reads of the clock will produce non-decreasing values. 10 | @since(version = 0.2.0) 11 | interface monotonic-clock { 12 | @since(version = 0.2.0) 13 | use wasi:io/poll@0.2.3.{pollable}; 14 | 15 | /// An instant in time, in nanoseconds. An instant is relative to an 16 | /// unspecified initial value, and can only be compared to instances from 17 | /// the same monotonic-clock. 18 | @since(version = 0.2.0) 19 | type instant = u64; 20 | 21 | /// A duration of time, in nanoseconds. 22 | @since(version = 0.2.0) 23 | type duration = u64; 24 | 25 | /// Read the current value of the clock. 26 | /// 27 | /// The clock is monotonic, therefore calling this function repeatedly will 28 | /// produce a sequence of non-decreasing values. 29 | @since(version = 0.2.0) 30 | now: func() -> instant; 31 | 32 | /// Query the resolution of the clock. Returns the duration of time 33 | /// corresponding to a clock tick. 34 | @since(version = 0.2.0) 35 | resolution: func() -> duration; 36 | 37 | /// Create a `pollable` which will resolve once the specified instant 38 | /// has occurred. 39 | @since(version = 0.2.0) 40 | subscribe-instant: func( 41 | when: instant, 42 | ) -> pollable; 43 | 44 | /// Create a `pollable` that will resolve after the specified duration has 45 | /// elapsed from the time this function is invoked. 46 | @since(version = 0.2.0) 47 | subscribe-duration: func( 48 | when: duration, 49 | ) -> pollable; 50 | } 51 | -------------------------------------------------------------------------------- /examples/http/wit/deps/clocks/timezone.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | 3 | @unstable(feature = clocks-timezone) 4 | interface timezone { 5 | @unstable(feature = clocks-timezone) 6 | use wall-clock.{datetime}; 7 | 8 | /// Return information needed to display the given `datetime`. This includes 9 | /// the UTC offset, the time zone name, and a flag indicating whether 10 | /// daylight saving time is active. 11 | /// 12 | /// If the timezone cannot be determined for the given `datetime`, return a 13 | /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight 14 | /// saving time. 15 | @unstable(feature = clocks-timezone) 16 | display: func(when: datetime) -> timezone-display; 17 | 18 | /// The same as `display`, but only return the UTC offset. 19 | @unstable(feature = clocks-timezone) 20 | utc-offset: func(when: datetime) -> s32; 21 | 22 | /// Information useful for displaying the timezone of a specific `datetime`. 23 | /// 24 | /// This information may vary within a single `timezone` to reflect daylight 25 | /// saving time adjustments. 26 | @unstable(feature = clocks-timezone) 27 | record timezone-display { 28 | /// The number of seconds difference between UTC time and the local 29 | /// time of the timezone. 30 | /// 31 | /// The returned value will always be less than 86400 which is the 32 | /// number of seconds in a day (24*60*60). 33 | /// 34 | /// In implementations that do not expose an actual time zone, this 35 | /// should return 0. 36 | utc-offset: s32, 37 | 38 | /// The abbreviated name of the timezone to display to a user. The name 39 | /// `UTC` indicates Coordinated Universal Time. Otherwise, this should 40 | /// reference local standards for the name of the time zone. 41 | /// 42 | /// In implementations that do not expose an actual time zone, this 43 | /// should be the string `UTC`. 44 | /// 45 | /// In time zones that do not have an applicable name, a formatted 46 | /// representation of the UTC offset may be returned, such as `-04:00`. 47 | name: string, 48 | 49 | /// Whether daylight saving time is active. 50 | /// 51 | /// In implementations that do not expose an actual time zone, this 52 | /// should return false. 53 | in-daylight-saving-time: bool, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/http/wit/deps/clocks/wall-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | /// WASI Wall Clock is a clock API intended to let users query the current 3 | /// time. The name "wall" makes an analogy to a "clock on the wall", which 4 | /// is not necessarily monotonic as it may be reset. 5 | /// 6 | /// It is intended to be portable at least between Unix-family platforms and 7 | /// Windows. 8 | /// 9 | /// A wall clock is a clock which measures the date and time according to 10 | /// some external reference. 11 | /// 12 | /// External references may be reset, so this clock is not necessarily 13 | /// monotonic, making it unsuitable for measuring elapsed time. 14 | /// 15 | /// It is intended for reporting the current date and time for humans. 16 | @since(version = 0.2.0) 17 | interface wall-clock { 18 | /// A time and date in seconds plus nanoseconds. 19 | @since(version = 0.2.0) 20 | record datetime { 21 | seconds: u64, 22 | nanoseconds: u32, 23 | } 24 | 25 | /// Read the current value of the clock. 26 | /// 27 | /// This clock is not monotonic, therefore calling this function repeatedly 28 | /// will not necessarily produce a sequence of non-decreasing values. 29 | /// 30 | /// The returned timestamps represent the number of seconds since 31 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], 32 | /// also known as [Unix Time]. 33 | /// 34 | /// The nanoseconds field of the output is always less than 1000000000. 35 | /// 36 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 37 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time 38 | @since(version = 0.2.0) 39 | now: func() -> datetime; 40 | 41 | /// Query the resolution of the clock. 42 | /// 43 | /// The nanoseconds field of the output is always less than 1000000000. 44 | @since(version = 0.2.0) 45 | resolution: func() -> datetime; 46 | } 47 | -------------------------------------------------------------------------------- /examples/http/wit/deps/clocks/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import monotonic-clock; 7 | @since(version = 0.2.0) 8 | import wall-clock; 9 | @unstable(feature = clocks-timezone) 10 | import timezone; 11 | } 12 | -------------------------------------------------------------------------------- /examples/http/wit/deps/filesystem/preopens.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | interface preopens { 5 | @since(version = 0.2.0) 6 | use types.{descriptor}; 7 | 8 | /// Return the set of preopened directories, and their paths. 9 | @since(version = 0.2.0) 10 | get-directories: func() -> list>; 11 | } 12 | -------------------------------------------------------------------------------- /examples/http/wit/deps/filesystem/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import types; 7 | @since(version = 0.2.0) 8 | import preopens; 9 | } 10 | -------------------------------------------------------------------------------- /examples/http/wit/deps/http/handler.wit: -------------------------------------------------------------------------------- 1 | /// This interface defines a handler of incoming HTTP Requests. It should 2 | /// be exported by components which can respond to HTTP Requests. 3 | @since(version = 0.2.0) 4 | interface incoming-handler { 5 | @since(version = 0.2.0) 6 | use types.{incoming-request, response-outparam}; 7 | 8 | /// This function is invoked with an incoming HTTP Request, and a resource 9 | /// `response-outparam` which provides the capability to reply with an HTTP 10 | /// Response. The response is sent by calling the `response-outparam.set` 11 | /// method, which allows execution to continue after the response has been 12 | /// sent. This enables both streaming to the response body, and performing other 13 | /// work. 14 | /// 15 | /// The implementor of this function must write a response to the 16 | /// `response-outparam` before returning, or else the caller will respond 17 | /// with an error on its behalf. 18 | @since(version = 0.2.0) 19 | handle: func( 20 | request: incoming-request, 21 | response-out: response-outparam 22 | ); 23 | } 24 | 25 | /// This interface defines a handler of outgoing HTTP Requests. It should be 26 | /// imported by components which wish to make HTTP Requests. 27 | @since(version = 0.2.0) 28 | interface outgoing-handler { 29 | @since(version = 0.2.0) 30 | use types.{ 31 | outgoing-request, request-options, future-incoming-response, error-code 32 | }; 33 | 34 | /// This function is invoked with an outgoing HTTP Request, and it returns 35 | /// a resource `future-incoming-response` which represents an HTTP Response 36 | /// which may arrive in the future. 37 | /// 38 | /// The `options` argument accepts optional parameters for the HTTP 39 | /// protocol's transport layer. 40 | /// 41 | /// This function may return an error if the `outgoing-request` is invalid 42 | /// or not allowed to be made. Otherwise, protocol errors are reported 43 | /// through the `future-incoming-response`. 44 | @since(version = 0.2.0) 45 | handle: func( 46 | request: outgoing-request, 47 | options: option 48 | ) -> result; 49 | } 50 | -------------------------------------------------------------------------------- /examples/http/wit/deps/http/proxy.wit: -------------------------------------------------------------------------------- 1 | package wasi:http@0.2.3; 2 | 3 | /// The `wasi:http/imports` world imports all the APIs for HTTP proxies. 4 | /// It is intended to be `include`d in other worlds. 5 | @since(version = 0.2.0) 6 | world imports { 7 | /// HTTP proxies have access to time and randomness. 8 | @since(version = 0.2.0) 9 | import wasi:clocks/monotonic-clock@0.2.3; 10 | @since(version = 0.2.0) 11 | import wasi:clocks/wall-clock@0.2.3; 12 | @since(version = 0.2.0) 13 | import wasi:random/random@0.2.3; 14 | 15 | /// Proxies have standard output and error streams which are expected to 16 | /// terminate in a developer-facing console provided by the host. 17 | @since(version = 0.2.0) 18 | import wasi:cli/stdout@0.2.3; 19 | @since(version = 0.2.0) 20 | import wasi:cli/stderr@0.2.3; 21 | 22 | /// TODO: this is a temporary workaround until component tooling is able to 23 | /// gracefully handle the absence of stdin. Hosts must return an eof stream 24 | /// for this import, which is what wasi-libc + tooling will do automatically 25 | /// when this import is properly removed. 26 | @since(version = 0.2.0) 27 | import wasi:cli/stdin@0.2.3; 28 | 29 | /// This is the default handler to use when user code simply wants to make an 30 | /// HTTP request (e.g., via `fetch()`). 31 | @since(version = 0.2.0) 32 | import outgoing-handler; 33 | } 34 | 35 | /// The `wasi:http/proxy` world captures a widely-implementable intersection of 36 | /// hosts that includes HTTP forward and reverse proxies. Components targeting 37 | /// this world may concurrently stream in and out any number of incoming and 38 | /// outgoing HTTP requests. 39 | @since(version = 0.2.0) 40 | world proxy { 41 | @since(version = 0.2.0) 42 | include imports; 43 | 44 | /// The host delivers incoming HTTP requests to a component by calling the 45 | /// `handle` function of this exported interface. A host may arbitrarily reuse 46 | /// or not reuse component instance when delivering incoming HTTP requests and 47 | /// thus a component must be able to handle 0..N calls to `handle`. 48 | @since(version = 0.2.0) 49 | export incoming-handler; 50 | } 51 | -------------------------------------------------------------------------------- /examples/http/wit/deps/io/error.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | interface error { 5 | /// A resource which represents some error information. 6 | /// 7 | /// The only method provided by this resource is `to-debug-string`, 8 | /// which provides some human-readable information about the error. 9 | /// 10 | /// In the `wasi:io` package, this resource is returned through the 11 | /// `wasi:io/streams/stream-error` type. 12 | /// 13 | /// To provide more specific error information, other interfaces may 14 | /// offer functions to "downcast" this error into more specific types. For example, 15 | /// errors returned from streams derived from filesystem types can be described using 16 | /// the filesystem's own error-code type. This is done using the function 17 | /// `wasi:filesystem/types/filesystem-error-code`, which takes a `borrow` 18 | /// parameter and returns an `option`. 19 | /// 20 | /// The set of functions which can "downcast" an `error` into a more 21 | /// concrete type is open. 22 | @since(version = 0.2.0) 23 | resource error { 24 | /// Returns a string that is suitable to assist humans in debugging 25 | /// this error. 26 | /// 27 | /// WARNING: The returned string should not be consumed mechanically! 28 | /// It may change across platforms, hosts, or other implementation 29 | /// details. Parsing this string is a major platform-compatibility 30 | /// hazard. 31 | @since(version = 0.2.0) 32 | to-debug-string: func() -> string; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/http/wit/deps/io/poll.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | /// A poll API intended to let users wait for I/O events on multiple handles 4 | /// at once. 5 | @since(version = 0.2.0) 6 | interface poll { 7 | /// `pollable` represents a single I/O event which may be ready, or not. 8 | @since(version = 0.2.0) 9 | resource pollable { 10 | 11 | /// Return the readiness of a pollable. This function never blocks. 12 | /// 13 | /// Returns `true` when the pollable is ready, and `false` otherwise. 14 | @since(version = 0.2.0) 15 | ready: func() -> bool; 16 | 17 | /// `block` returns immediately if the pollable is ready, and otherwise 18 | /// blocks until ready. 19 | /// 20 | /// This function is equivalent to calling `poll.poll` on a list 21 | /// containing only this pollable. 22 | @since(version = 0.2.0) 23 | block: func(); 24 | } 25 | 26 | /// Poll for completion on a set of pollables. 27 | /// 28 | /// This function takes a list of pollables, which identify I/O sources of 29 | /// interest, and waits until one or more of the events is ready for I/O. 30 | /// 31 | /// The result `list` contains one or more indices of handles in the 32 | /// argument list that is ready for I/O. 33 | /// 34 | /// This function traps if either: 35 | /// - the list is empty, or: 36 | /// - the list contains more elements than can be indexed with a `u32` value. 37 | /// 38 | /// A timeout can be implemented by adding a pollable from the 39 | /// wasi-clocks API to the list. 40 | /// 41 | /// This function does not return a `result`; polling in itself does not 42 | /// do any I/O so it doesn't fail. If any of the I/O sources identified by 43 | /// the pollables has an error, it is indicated by marking the source as 44 | /// being ready for I/O. 45 | @since(version = 0.2.0) 46 | poll: func(in: list>) -> list; 47 | } 48 | -------------------------------------------------------------------------------- /examples/http/wit/deps/io/streams.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | /// WASI I/O is an I/O abstraction API which is currently focused on providing 4 | /// stream types. 5 | /// 6 | /// In the future, the component model is expected to add built-in stream types; 7 | /// when it does, they are expected to subsume this API. 8 | @since(version = 0.2.0) 9 | interface streams { 10 | @since(version = 0.2.0) 11 | use error.{error}; 12 | @since(version = 0.2.0) 13 | use poll.{pollable}; 14 | 15 | /// An error for input-stream and output-stream operations. 16 | @since(version = 0.2.0) 17 | variant stream-error { 18 | /// The last operation (a write or flush) failed before completion. 19 | /// 20 | /// More information is available in the `error` payload. 21 | /// 22 | /// After this, the stream will be closed. All future operations return 23 | /// `stream-error::closed`. 24 | last-operation-failed(error), 25 | /// The stream is closed: no more input will be accepted by the 26 | /// stream. A closed output-stream will return this error on all 27 | /// future operations. 28 | closed 29 | } 30 | 31 | /// An input bytestream. 32 | /// 33 | /// `input-stream`s are *non-blocking* to the extent practical on underlying 34 | /// platforms. I/O operations always return promptly; if fewer bytes are 35 | /// promptly available than requested, they return the number of bytes promptly 36 | /// available, which could even be zero. To wait for data to be available, 37 | /// use the `subscribe` function to obtain a `pollable` which can be polled 38 | /// for using `wasi:io/poll`. 39 | @since(version = 0.2.0) 40 | resource input-stream { 41 | /// Perform a non-blocking read from the stream. 42 | /// 43 | /// When the source of a `read` is binary data, the bytes from the source 44 | /// are returned verbatim. When the source of a `read` is known to the 45 | /// implementation to be text, bytes containing the UTF-8 encoding of the 46 | /// text are returned. 47 | /// 48 | /// This function returns a list of bytes containing the read data, 49 | /// when successful. The returned list will contain up to `len` bytes; 50 | /// it may return fewer than requested, but not more. The list is 51 | /// empty when no bytes are available for reading at this time. The 52 | /// pollable given by `subscribe` will be ready when more bytes are 53 | /// available. 54 | /// 55 | /// This function fails with a `stream-error` when the operation 56 | /// encounters an error, giving `last-operation-failed`, or when the 57 | /// stream is closed, giving `closed`. 58 | /// 59 | /// When the caller gives a `len` of 0, it represents a request to 60 | /// read 0 bytes. If the stream is still open, this call should 61 | /// succeed and return an empty list, or otherwise fail with `closed`. 62 | /// 63 | /// The `len` parameter is a `u64`, which could represent a list of u8 which 64 | /// is not possible to allocate in wasm32, or not desirable to allocate as 65 | /// as a return value by the callee. The callee may return a list of bytes 66 | /// less than `len` in size while more bytes are available for reading. 67 | @since(version = 0.2.0) 68 | read: func( 69 | /// The maximum number of bytes to read 70 | len: u64 71 | ) -> result, stream-error>; 72 | 73 | /// Read bytes from a stream, after blocking until at least one byte can 74 | /// be read. Except for blocking, behavior is identical to `read`. 75 | @since(version = 0.2.0) 76 | blocking-read: func( 77 | /// The maximum number of bytes to read 78 | len: u64 79 | ) -> result, stream-error>; 80 | 81 | /// Skip bytes from a stream. Returns number of bytes skipped. 82 | /// 83 | /// Behaves identical to `read`, except instead of returning a list 84 | /// of bytes, returns the number of bytes consumed from the stream. 85 | @since(version = 0.2.0) 86 | skip: func( 87 | /// The maximum number of bytes to skip. 88 | len: u64, 89 | ) -> result; 90 | 91 | /// Skip bytes from a stream, after blocking until at least one byte 92 | /// can be skipped. Except for blocking behavior, identical to `skip`. 93 | @since(version = 0.2.0) 94 | blocking-skip: func( 95 | /// The maximum number of bytes to skip. 96 | len: u64, 97 | ) -> result; 98 | 99 | /// Create a `pollable` which will resolve once either the specified stream 100 | /// has bytes available to read or the other end of the stream has been 101 | /// closed. 102 | /// The created `pollable` is a child resource of the `input-stream`. 103 | /// Implementations may trap if the `input-stream` is dropped before 104 | /// all derived `pollable`s created with this function are dropped. 105 | @since(version = 0.2.0) 106 | subscribe: func() -> pollable; 107 | } 108 | 109 | 110 | /// An output bytestream. 111 | /// 112 | /// `output-stream`s are *non-blocking* to the extent practical on 113 | /// underlying platforms. Except where specified otherwise, I/O operations also 114 | /// always return promptly, after the number of bytes that can be written 115 | /// promptly, which could even be zero. To wait for the stream to be ready to 116 | /// accept data, the `subscribe` function to obtain a `pollable` which can be 117 | /// polled for using `wasi:io/poll`. 118 | /// 119 | /// Dropping an `output-stream` while there's still an active write in 120 | /// progress may result in the data being lost. Before dropping the stream, 121 | /// be sure to fully flush your writes. 122 | @since(version = 0.2.0) 123 | resource output-stream { 124 | /// Check readiness for writing. This function never blocks. 125 | /// 126 | /// Returns the number of bytes permitted for the next call to `write`, 127 | /// or an error. Calling `write` with more bytes than this function has 128 | /// permitted will trap. 129 | /// 130 | /// When this function returns 0 bytes, the `subscribe` pollable will 131 | /// become ready when this function will report at least 1 byte, or an 132 | /// error. 133 | @since(version = 0.2.0) 134 | check-write: func() -> result; 135 | 136 | /// Perform a write. This function never blocks. 137 | /// 138 | /// When the destination of a `write` is binary data, the bytes from 139 | /// `contents` are written verbatim. When the destination of a `write` is 140 | /// known to the implementation to be text, the bytes of `contents` are 141 | /// transcoded from UTF-8 into the encoding of the destination and then 142 | /// written. 143 | /// 144 | /// Precondition: check-write gave permit of Ok(n) and contents has a 145 | /// length of less than or equal to n. Otherwise, this function will trap. 146 | /// 147 | /// returns Err(closed) without writing if the stream has closed since 148 | /// the last call to check-write provided a permit. 149 | @since(version = 0.2.0) 150 | write: func( 151 | contents: list 152 | ) -> result<_, stream-error>; 153 | 154 | /// Perform a write of up to 4096 bytes, and then flush the stream. Block 155 | /// until all of these operations are complete, or an error occurs. 156 | /// 157 | /// This is a convenience wrapper around the use of `check-write`, 158 | /// `subscribe`, `write`, and `flush`, and is implemented with the 159 | /// following pseudo-code: 160 | /// 161 | /// ```text 162 | /// let pollable = this.subscribe(); 163 | /// while !contents.is_empty() { 164 | /// // Wait for the stream to become writable 165 | /// pollable.block(); 166 | /// let Ok(n) = this.check-write(); // eliding error handling 167 | /// let len = min(n, contents.len()); 168 | /// let (chunk, rest) = contents.split_at(len); 169 | /// this.write(chunk ); // eliding error handling 170 | /// contents = rest; 171 | /// } 172 | /// this.flush(); 173 | /// // Wait for completion of `flush` 174 | /// pollable.block(); 175 | /// // Check for any errors that arose during `flush` 176 | /// let _ = this.check-write(); // eliding error handling 177 | /// ``` 178 | @since(version = 0.2.0) 179 | blocking-write-and-flush: func( 180 | contents: list 181 | ) -> result<_, stream-error>; 182 | 183 | /// Request to flush buffered output. This function never blocks. 184 | /// 185 | /// This tells the output-stream that the caller intends any buffered 186 | /// output to be flushed. the output which is expected to be flushed 187 | /// is all that has been passed to `write` prior to this call. 188 | /// 189 | /// Upon calling this function, the `output-stream` will not accept any 190 | /// writes (`check-write` will return `ok(0)`) until the flush has 191 | /// completed. The `subscribe` pollable will become ready when the 192 | /// flush has completed and the stream can accept more writes. 193 | @since(version = 0.2.0) 194 | flush: func() -> result<_, stream-error>; 195 | 196 | /// Request to flush buffered output, and block until flush completes 197 | /// and stream is ready for writing again. 198 | @since(version = 0.2.0) 199 | blocking-flush: func() -> result<_, stream-error>; 200 | 201 | /// Create a `pollable` which will resolve once the output-stream 202 | /// is ready for more writing, or an error has occurred. When this 203 | /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an 204 | /// error. 205 | /// 206 | /// If the stream is closed, this pollable is always ready immediately. 207 | /// 208 | /// The created `pollable` is a child resource of the `output-stream`. 209 | /// Implementations may trap if the `output-stream` is dropped before 210 | /// all derived `pollable`s created with this function are dropped. 211 | @since(version = 0.2.0) 212 | subscribe: func() -> pollable; 213 | 214 | /// Write zeroes to a stream. 215 | /// 216 | /// This should be used precisely like `write` with the exact same 217 | /// preconditions (must use check-write first), but instead of 218 | /// passing a list of bytes, you simply pass the number of zero-bytes 219 | /// that should be written. 220 | @since(version = 0.2.0) 221 | write-zeroes: func( 222 | /// The number of zero-bytes to write 223 | len: u64 224 | ) -> result<_, stream-error>; 225 | 226 | /// Perform a write of up to 4096 zeroes, and then flush the stream. 227 | /// Block until all of these operations are complete, or an error 228 | /// occurs. 229 | /// 230 | /// This is a convenience wrapper around the use of `check-write`, 231 | /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with 232 | /// the following pseudo-code: 233 | /// 234 | /// ```text 235 | /// let pollable = this.subscribe(); 236 | /// while num_zeroes != 0 { 237 | /// // Wait for the stream to become writable 238 | /// pollable.block(); 239 | /// let Ok(n) = this.check-write(); // eliding error handling 240 | /// let len = min(n, num_zeroes); 241 | /// this.write-zeroes(len); // eliding error handling 242 | /// num_zeroes -= len; 243 | /// } 244 | /// this.flush(); 245 | /// // Wait for completion of `flush` 246 | /// pollable.block(); 247 | /// // Check for any errors that arose during `flush` 248 | /// let _ = this.check-write(); // eliding error handling 249 | /// ``` 250 | @since(version = 0.2.0) 251 | blocking-write-zeroes-and-flush: func( 252 | /// The number of zero-bytes to write 253 | len: u64 254 | ) -> result<_, stream-error>; 255 | 256 | /// Read from one stream and write to another. 257 | /// 258 | /// The behavior of splice is equivalent to: 259 | /// 1. calling `check-write` on the `output-stream` 260 | /// 2. calling `read` on the `input-stream` with the smaller of the 261 | /// `check-write` permitted length and the `len` provided to `splice` 262 | /// 3. calling `write` on the `output-stream` with that read data. 263 | /// 264 | /// Any error reported by the call to `check-write`, `read`, or 265 | /// `write` ends the splice and reports that error. 266 | /// 267 | /// This function returns the number of bytes transferred; it may be less 268 | /// than `len`. 269 | @since(version = 0.2.0) 270 | splice: func( 271 | /// The stream to read from 272 | src: borrow, 273 | /// The number of bytes to splice 274 | len: u64, 275 | ) -> result; 276 | 277 | /// Read from one stream and write to another, with blocking. 278 | /// 279 | /// This is similar to `splice`, except that it blocks until the 280 | /// `output-stream` is ready for writing, and the `input-stream` 281 | /// is ready for reading, before performing the `splice`. 282 | @since(version = 0.2.0) 283 | blocking-splice: func( 284 | /// The stream to read from 285 | src: borrow, 286 | /// The number of bytes to splice 287 | len: u64, 288 | ) -> result; 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /examples/http/wit/deps/io/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import streams; 7 | 8 | @since(version = 0.2.0) 9 | import poll; 10 | } 11 | -------------------------------------------------------------------------------- /examples/http/wit/deps/random/insecure-seed.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | /// The insecure-seed interface for seeding hash-map DoS resistance. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | @since(version = 0.2.0) 7 | interface insecure-seed { 8 | /// Return a 128-bit value that may contain a pseudo-random value. 9 | /// 10 | /// The returned value is not required to be computed from a CSPRNG, and may 11 | /// even be entirely deterministic. Host implementations are encouraged to 12 | /// provide pseudo-random values to any program exposed to 13 | /// attacker-controlled content, to enable DoS protection built into many 14 | /// languages' hash-map implementations. 15 | /// 16 | /// This function is intended to only be called once, by a source language 17 | /// to initialize Denial Of Service (DoS) protection in its hash-map 18 | /// implementation. 19 | /// 20 | /// # Expected future evolution 21 | /// 22 | /// This will likely be changed to a value import, to prevent it from being 23 | /// called multiple times and potentially used for purposes other than DoS 24 | /// protection. 25 | @since(version = 0.2.0) 26 | insecure-seed: func() -> tuple; 27 | } 28 | -------------------------------------------------------------------------------- /examples/http/wit/deps/random/insecure.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | /// The insecure interface for insecure pseudo-random numbers. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | @since(version = 0.2.0) 7 | interface insecure { 8 | /// Return `len` insecure pseudo-random bytes. 9 | /// 10 | /// This function is not cryptographically secure. Do not use it for 11 | /// anything related to security. 12 | /// 13 | /// There are no requirements on the values of the returned bytes, however 14 | /// implementations are encouraged to return evenly distributed values with 15 | /// a long period. 16 | @since(version = 0.2.0) 17 | get-insecure-random-bytes: func(len: u64) -> list; 18 | 19 | /// Return an insecure pseudo-random `u64` value. 20 | /// 21 | /// This function returns the same type of pseudo-random data as 22 | /// `get-insecure-random-bytes`, represented as a `u64`. 23 | @since(version = 0.2.0) 24 | get-insecure-random-u64: func() -> u64; 25 | } 26 | -------------------------------------------------------------------------------- /examples/http/wit/deps/random/random.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | /// WASI Random is a random data API. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | @since(version = 0.2.0) 7 | interface random { 8 | /// Return `len` cryptographically-secure random or pseudo-random bytes. 9 | /// 10 | /// This function must produce data at least as cryptographically secure and 11 | /// fast as an adequately seeded cryptographically-secure pseudo-random 12 | /// number generator (CSPRNG). It must not block, from the perspective of 13 | /// the calling program, under any circumstances, including on the first 14 | /// request and on requests for numbers of bytes. The returned data must 15 | /// always be unpredictable. 16 | /// 17 | /// This function must always return fresh data. Deterministic environments 18 | /// must omit this function, rather than implementing it with deterministic 19 | /// data. 20 | @since(version = 0.2.0) 21 | get-random-bytes: func(len: u64) -> list; 22 | 23 | /// Return a cryptographically-secure random or pseudo-random `u64` value. 24 | /// 25 | /// This function returns the same type of data as `get-random-bytes`, 26 | /// represented as a `u64`. 27 | @since(version = 0.2.0) 28 | get-random-u64: func() -> u64; 29 | } 30 | -------------------------------------------------------------------------------- /examples/http/wit/deps/random/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import random; 7 | 8 | @since(version = 0.2.0) 9 | import insecure; 10 | 11 | @since(version = 0.2.0) 12 | import insecure-seed; 13 | } 14 | -------------------------------------------------------------------------------- /examples/http/wit/deps/sockets/instance-network.wit: -------------------------------------------------------------------------------- 1 | 2 | /// This interface provides a value-export of the default network handle.. 3 | @since(version = 0.2.0) 4 | interface instance-network { 5 | @since(version = 0.2.0) 6 | use network.{network}; 7 | 8 | /// Get a handle to the default network. 9 | @since(version = 0.2.0) 10 | instance-network: func() -> network; 11 | } 12 | -------------------------------------------------------------------------------- /examples/http/wit/deps/sockets/ip-name-lookup.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface ip-name-lookup { 3 | @since(version = 0.2.0) 4 | use wasi:io/poll@0.2.3.{pollable}; 5 | @since(version = 0.2.0) 6 | use network.{network, error-code, ip-address}; 7 | 8 | /// Resolve an internet host name to a list of IP addresses. 9 | /// 10 | /// Unicode domain names are automatically converted to ASCII using IDNA encoding. 11 | /// If the input is an IP address string, the address is parsed and returned 12 | /// as-is without making any external requests. 13 | /// 14 | /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. 15 | /// 16 | /// This function never blocks. It either immediately fails or immediately 17 | /// returns successfully with a `resolve-address-stream` that can be used 18 | /// to (asynchronously) fetch the results. 19 | /// 20 | /// # Typical errors 21 | /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. 22 | /// 23 | /// # References: 24 | /// - 25 | /// - 26 | /// - 27 | /// - 28 | @since(version = 0.2.0) 29 | resolve-addresses: func(network: borrow, name: string) -> result; 30 | 31 | @since(version = 0.2.0) 32 | resource resolve-address-stream { 33 | /// Returns the next address from the resolver. 34 | /// 35 | /// This function should be called multiple times. On each call, it will 36 | /// return the next address in connection order preference. If all 37 | /// addresses have been exhausted, this function returns `none`. 38 | /// 39 | /// This function never returns IPv4-mapped IPv6 addresses. 40 | /// 41 | /// # Typical errors 42 | /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) 43 | /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) 44 | /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) 45 | /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) 46 | @since(version = 0.2.0) 47 | resolve-next-address: func() -> result, error-code>; 48 | 49 | /// Create a `pollable` which will resolve once the stream is ready for I/O. 50 | /// 51 | /// Note: this function is here for WASI 0.2 only. 52 | /// It's planned to be removed when `future` is natively supported in Preview3. 53 | @since(version = 0.2.0) 54 | subscribe: func() -> pollable; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/http/wit/deps/sockets/network.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface network { 3 | @unstable(feature = network-error-code) 4 | use wasi:io/error@0.2.3.{error}; 5 | 6 | /// An opaque resource that represents access to (a subset of) the network. 7 | /// This enables context-based security for networking. 8 | /// There is no need for this to map 1:1 to a physical network interface. 9 | @since(version = 0.2.0) 10 | resource network; 11 | 12 | /// Error codes. 13 | /// 14 | /// In theory, every API can return any error code. 15 | /// In practice, API's typically only return the errors documented per API 16 | /// combined with a couple of errors that are always possible: 17 | /// - `unknown` 18 | /// - `access-denied` 19 | /// - `not-supported` 20 | /// - `out-of-memory` 21 | /// - `concurrency-conflict` 22 | /// 23 | /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. 24 | @since(version = 0.2.0) 25 | enum error-code { 26 | /// Unknown error 27 | unknown, 28 | 29 | /// Access denied. 30 | /// 31 | /// POSIX equivalent: EACCES, EPERM 32 | access-denied, 33 | 34 | /// The operation is not supported. 35 | /// 36 | /// POSIX equivalent: EOPNOTSUPP 37 | not-supported, 38 | 39 | /// One of the arguments is invalid. 40 | /// 41 | /// POSIX equivalent: EINVAL 42 | invalid-argument, 43 | 44 | /// Not enough memory to complete the operation. 45 | /// 46 | /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY 47 | out-of-memory, 48 | 49 | /// The operation timed out before it could finish completely. 50 | timeout, 51 | 52 | /// This operation is incompatible with another asynchronous operation that is already in progress. 53 | /// 54 | /// POSIX equivalent: EALREADY 55 | concurrency-conflict, 56 | 57 | /// Trying to finish an asynchronous operation that: 58 | /// - has not been started yet, or: 59 | /// - was already finished by a previous `finish-*` call. 60 | /// 61 | /// Note: this is scheduled to be removed when `future`s are natively supported. 62 | not-in-progress, 63 | 64 | /// The operation has been aborted because it could not be completed immediately. 65 | /// 66 | /// Note: this is scheduled to be removed when `future`s are natively supported. 67 | would-block, 68 | 69 | 70 | /// The operation is not valid in the socket's current state. 71 | invalid-state, 72 | 73 | /// A new socket resource could not be created because of a system limit. 74 | new-socket-limit, 75 | 76 | /// A bind operation failed because the provided address is not an address that the `network` can bind to. 77 | address-not-bindable, 78 | 79 | /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. 80 | address-in-use, 81 | 82 | /// The remote address is not reachable 83 | remote-unreachable, 84 | 85 | 86 | /// The TCP connection was forcefully rejected 87 | connection-refused, 88 | 89 | /// The TCP connection was reset. 90 | connection-reset, 91 | 92 | /// A TCP connection was aborted. 93 | connection-aborted, 94 | 95 | 96 | /// The size of a datagram sent to a UDP socket exceeded the maximum 97 | /// supported size. 98 | datagram-too-large, 99 | 100 | 101 | /// Name does not exist or has no suitable associated IP addresses. 102 | name-unresolvable, 103 | 104 | /// A temporary failure in name resolution occurred. 105 | temporary-resolver-failure, 106 | 107 | /// A permanent failure in name resolution occurred. 108 | permanent-resolver-failure, 109 | } 110 | 111 | /// Attempts to extract a network-related `error-code` from the stream 112 | /// `error` provided. 113 | /// 114 | /// Stream operations which return `stream-error::last-operation-failed` 115 | /// have a payload with more information about the operation that failed. 116 | /// This payload can be passed through to this function to see if there's 117 | /// network-related information about the error to return. 118 | /// 119 | /// Note that this function is fallible because not all stream-related 120 | /// errors are network-related errors. 121 | @unstable(feature = network-error-code) 122 | network-error-code: func(err: borrow) -> option; 123 | 124 | @since(version = 0.2.0) 125 | enum ip-address-family { 126 | /// Similar to `AF_INET` in POSIX. 127 | ipv4, 128 | 129 | /// Similar to `AF_INET6` in POSIX. 130 | ipv6, 131 | } 132 | 133 | @since(version = 0.2.0) 134 | type ipv4-address = tuple; 135 | @since(version = 0.2.0) 136 | type ipv6-address = tuple; 137 | 138 | @since(version = 0.2.0) 139 | variant ip-address { 140 | ipv4(ipv4-address), 141 | ipv6(ipv6-address), 142 | } 143 | 144 | @since(version = 0.2.0) 145 | record ipv4-socket-address { 146 | /// sin_port 147 | port: u16, 148 | /// sin_addr 149 | address: ipv4-address, 150 | } 151 | 152 | @since(version = 0.2.0) 153 | record ipv6-socket-address { 154 | /// sin6_port 155 | port: u16, 156 | /// sin6_flowinfo 157 | flow-info: u32, 158 | /// sin6_addr 159 | address: ipv6-address, 160 | /// sin6_scope_id 161 | scope-id: u32, 162 | } 163 | 164 | @since(version = 0.2.0) 165 | variant ip-socket-address { 166 | ipv4(ipv4-socket-address), 167 | ipv6(ipv6-socket-address), 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /examples/http/wit/deps/sockets/tcp-create-socket.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface tcp-create-socket { 3 | @since(version = 0.2.0) 4 | use network.{network, error-code, ip-address-family}; 5 | @since(version = 0.2.0) 6 | use tcp.{tcp-socket}; 7 | 8 | /// Create a new TCP socket. 9 | /// 10 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. 11 | /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 12 | /// 13 | /// This function does not require a network capability handle. This is considered to be safe because 14 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` 15 | /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 16 | /// 17 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 18 | /// 19 | /// # Typical errors 20 | /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 21 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 22 | /// 23 | /// # References 24 | /// - 25 | /// - 26 | /// - 27 | /// - 28 | @since(version = 0.2.0) 29 | create-tcp-socket: func(address-family: ip-address-family) -> result; 30 | } 31 | -------------------------------------------------------------------------------- /examples/http/wit/deps/sockets/udp-create-socket.wit: -------------------------------------------------------------------------------- 1 | @since(version = 0.2.0) 2 | interface udp-create-socket { 3 | @since(version = 0.2.0) 4 | use network.{network, error-code, ip-address-family}; 5 | @since(version = 0.2.0) 6 | use udp.{udp-socket}; 7 | 8 | /// Create a new UDP socket. 9 | /// 10 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. 11 | /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 12 | /// 13 | /// This function does not require a network capability handle. This is considered to be safe because 14 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, 15 | /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 16 | /// 17 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 18 | /// 19 | /// # Typical errors 20 | /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 21 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 22 | /// 23 | /// # References: 24 | /// - 25 | /// - 26 | /// - 27 | /// - 28 | @since(version = 0.2.0) 29 | create-udp-socket: func(address-family: ip-address-family) -> result; 30 | } 31 | -------------------------------------------------------------------------------- /examples/http/wit/deps/sockets/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:sockets@0.2.3; 2 | 3 | @since(version = 0.2.0) 4 | world imports { 5 | @since(version = 0.2.0) 6 | import instance-network; 7 | @since(version = 0.2.0) 8 | import network; 9 | @since(version = 0.2.0) 10 | import udp; 11 | @since(version = 0.2.0) 12 | import udp-create-socket; 13 | @since(version = 0.2.0) 14 | import tcp; 15 | @since(version = 0.2.0) 16 | import tcp-create-socket; 17 | @since(version = 0.2.0) 18 | import ip-name-lookup; 19 | } 20 | -------------------------------------------------------------------------------- /examples/http/wit/world.wit: -------------------------------------------------------------------------------- 1 | package examples:http; 2 | 3 | world http { 4 | import wasi:http/incoming-handler@0.2.3; 5 | } 6 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "advisory-db": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1735928634, 7 | "narHash": "sha256-Qg1vJOuEohAbdRmTTOLrbbGsyK9KRB54r3+aBuOMctM=", 8 | "owner": "rustsec", 9 | "repo": "advisory-db", 10 | "rev": "63a2f39924f66ca89cf5761f299a8a244fe02543", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "rustsec", 15 | "repo": "advisory-db", 16 | "type": "github" 17 | } 18 | }, 19 | "crane": { 20 | "locked": { 21 | "lastModified": 1736101677, 22 | "narHash": "sha256-iKOPq86AOWCohuzxwFy/MtC8PcSVGnrxBOvxpjpzrAY=", 23 | "owner": "ipetkov", 24 | "repo": "crane", 25 | "rev": "61ba163d85e5adeddc7b3a69bb174034965965b2", 26 | "type": "github" 27 | }, 28 | "original": { 29 | "owner": "ipetkov", 30 | "repo": "crane", 31 | "type": "github" 32 | } 33 | }, 34 | "fenix": { 35 | "inputs": { 36 | "nixpkgs": [ 37 | "nixify", 38 | "nixpkgs-nixos" 39 | ], 40 | "rust-analyzer-src": "rust-analyzer-src" 41 | }, 42 | "locked": { 43 | "lastModified": 1736318091, 44 | "narHash": "sha256-RkRHXZaMgOMGgkW2YmEqxxDDYRiGFbfr1JuaI0VrCKo=", 45 | "owner": "nix-community", 46 | "repo": "fenix", 47 | "rev": "9e13860d50cbfd42e79101a516e1939c7723f093", 48 | "type": "github" 49 | }, 50 | "original": { 51 | "owner": "nix-community", 52 | "repo": "fenix", 53 | "type": "github" 54 | } 55 | }, 56 | "flake-utils": { 57 | "inputs": { 58 | "systems": "systems" 59 | }, 60 | "locked": { 61 | "lastModified": 1731533236, 62 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 63 | "owner": "numtide", 64 | "repo": "flake-utils", 65 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 66 | "type": "github" 67 | }, 68 | "original": { 69 | "owner": "numtide", 70 | "repo": "flake-utils", 71 | "type": "github" 72 | } 73 | }, 74 | "macos-sdk": { 75 | "flake": false, 76 | "locked": { 77 | "lastModified": 1694769349, 78 | "narHash": "sha256-TEvVJy+NMPyzgWSk/6S29ZMQR+ICFxSdS3tw247uhFc=", 79 | "type": "tarball", 80 | "url": "https://github.com/roblabla/MacOSX-SDKs/releases/download/macosx14.0/MacOSX14.0.sdk.tar.xz" 81 | }, 82 | "original": { 83 | "type": "tarball", 84 | "url": "https://github.com/roblabla/MacOSX-SDKs/releases/download/macosx14.0/MacOSX14.0.sdk.tar.xz" 85 | } 86 | }, 87 | "nix-filter": { 88 | "locked": { 89 | "lastModified": 1731533336, 90 | "narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=", 91 | "owner": "numtide", 92 | "repo": "nix-filter", 93 | "rev": "f7653272fd234696ae94229839a99b73c9ab7de0", 94 | "type": "github" 95 | }, 96 | "original": { 97 | "owner": "numtide", 98 | "repo": "nix-filter", 99 | "type": "github" 100 | } 101 | }, 102 | "nix-flake-tests": { 103 | "locked": { 104 | "lastModified": 1677844186, 105 | "narHash": "sha256-ErJZ/Gs1rxh561CJeWP5bohA2IcTq1rDneu1WT6CVII=", 106 | "owner": "antifuchs", 107 | "repo": "nix-flake-tests", 108 | "rev": "bbd9216bd0f6495bb961a8eb8392b7ef55c67afb", 109 | "type": "github" 110 | }, 111 | "original": { 112 | "owner": "antifuchs", 113 | "repo": "nix-flake-tests", 114 | "type": "github" 115 | } 116 | }, 117 | "nix-log": { 118 | "inputs": { 119 | "nix-flake-tests": "nix-flake-tests", 120 | "nixify": [ 121 | "nixify" 122 | ], 123 | "nixlib": [ 124 | "nixlib" 125 | ] 126 | }, 127 | "locked": { 128 | "lastModified": 1733747205, 129 | "narHash": "sha256-8BRnYXnl0exUL/sRD2I382KHiY5TKWzVBQw6+6YO4yw=", 130 | "owner": "rvolosatovs", 131 | "repo": "nix-log", 132 | "rev": "354b9acbdb08a5567a97791546c1e23c9f476ef6", 133 | "type": "github" 134 | }, 135 | "original": { 136 | "owner": "rvolosatovs", 137 | "repo": "nix-log", 138 | "type": "github" 139 | } 140 | }, 141 | "nixify": { 142 | "inputs": { 143 | "advisory-db": "advisory-db", 144 | "crane": "crane", 145 | "fenix": "fenix", 146 | "flake-utils": "flake-utils", 147 | "macos-sdk": "macos-sdk", 148 | "nix-filter": "nix-filter", 149 | "nix-log": [ 150 | "nix-log" 151 | ], 152 | "nixlib": [ 153 | "nixlib" 154 | ], 155 | "nixpkgs-darwin": "nixpkgs-darwin", 156 | "nixpkgs-nixos": "nixpkgs-nixos", 157 | "rust-overlay": "rust-overlay" 158 | }, 159 | "locked": { 160 | "lastModified": 1736778069, 161 | "narHash": "sha256-OFCnASI5/7ajYDWjExp+nszfAKVpTp5VLCDzxqlsj0M=", 162 | "owner": "rvolosatovs", 163 | "repo": "nixify", 164 | "rev": "6bc7e4282939913a2d9cbe41ea44b19cdc7c4111", 165 | "type": "github" 166 | }, 167 | "original": { 168 | "owner": "rvolosatovs", 169 | "repo": "nixify", 170 | "type": "github" 171 | } 172 | }, 173 | "nixlib": { 174 | "locked": { 175 | "lastModified": 1736643958, 176 | "narHash": "sha256-tmpqTSWVRJVhpvfSN9KXBvKEXplrwKnSZNAoNPf/S/s=", 177 | "owner": "nix-community", 178 | "repo": "nixpkgs.lib", 179 | "rev": "1418bc28a52126761c02dd3d89b2d8ca0f521181", 180 | "type": "github" 181 | }, 182 | "original": { 183 | "owner": "nix-community", 184 | "repo": "nixpkgs.lib", 185 | "type": "github" 186 | } 187 | }, 188 | "nixpkgs-darwin": { 189 | "locked": { 190 | "lastModified": 1735954111, 191 | "narHash": "sha256-yPzRuUXx99iu9QMF9GKxxqqC7kpKdFlQi7PA9N85wxQ=", 192 | "owner": "nixos", 193 | "repo": "nixpkgs", 194 | "rev": "a4c40a8a970c147cea7e98e86b2ff2ffb184aedd", 195 | "type": "github" 196 | }, 197 | "original": { 198 | "owner": "nixos", 199 | "ref": "nixpkgs-24.11-darwin", 200 | "repo": "nixpkgs", 201 | "type": "github" 202 | } 203 | }, 204 | "nixpkgs-nixos": { 205 | "locked": { 206 | "lastModified": 1736200483, 207 | "narHash": "sha256-JO+lFN2HsCwSLMUWXHeOad6QUxOuwe9UOAF/iSl1J4I=", 208 | "owner": "nixos", 209 | "repo": "nixpkgs", 210 | "rev": "3f0a8ac25fb674611b98089ca3a5dd6480175751", 211 | "type": "github" 212 | }, 213 | "original": { 214 | "owner": "nixos", 215 | "ref": "nixos-24.11", 216 | "repo": "nixpkgs", 217 | "type": "github" 218 | } 219 | }, 220 | "root": { 221 | "inputs": { 222 | "nix-log": "nix-log", 223 | "nixify": "nixify", 224 | "nixlib": "nixlib" 225 | } 226 | }, 227 | "rust-analyzer-src": { 228 | "flake": false, 229 | "locked": { 230 | "lastModified": 1736266405, 231 | "narHash": "sha256-V2FDSb8YjuquZduBRNp5niWYlWurja2yGN6Xzh5GPYk=", 232 | "owner": "rust-lang", 233 | "repo": "rust-analyzer", 234 | "rev": "91fc0a239af4e56b84b1d3974ac0f34dcc99b895", 235 | "type": "github" 236 | }, 237 | "original": { 238 | "owner": "rust-lang", 239 | "ref": "nightly", 240 | "repo": "rust-analyzer", 241 | "type": "github" 242 | } 243 | }, 244 | "rust-overlay": { 245 | "inputs": { 246 | "nixpkgs": [ 247 | "nixify", 248 | "nixpkgs-nixos" 249 | ] 250 | }, 251 | "locked": { 252 | "lastModified": 1736303309, 253 | "narHash": "sha256-IKrk7RL+Q/2NC6+Ql6dwwCNZI6T6JH2grTdJaVWHF0A=", 254 | "owner": "oxalica", 255 | "repo": "rust-overlay", 256 | "rev": "a0b81d4fa349d9af1765b0f0b4a899c13776f706", 257 | "type": "github" 258 | }, 259 | "original": { 260 | "owner": "oxalica", 261 | "repo": "rust-overlay", 262 | "type": "github" 263 | } 264 | }, 265 | "systems": { 266 | "locked": { 267 | "lastModified": 1681028828, 268 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 269 | "owner": "nix-systems", 270 | "repo": "default", 271 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 272 | "type": "github" 273 | }, 274 | "original": { 275 | "owner": "nix-systems", 276 | "repo": "default", 277 | "type": "github" 278 | } 279 | } 280 | }, 281 | "root": "root", 282 | "version": 7 283 | } 284 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | nixConfig.extra-substituters = [ 3 | "https://bytecodealliance.cachix.org" 4 | "https://wasmcloud.cachix.org" 5 | "https://nixify.cachix.org" 6 | "https://crane.cachix.org" 7 | "https://nix-community.cachix.org" 8 | ]; 9 | nixConfig.extra-trusted-substituters = [ 10 | "https://bytecodealliance.cachix.org" 11 | "https://wasmcloud.cachix.org" 12 | "https://nixify.cachix.org" 13 | "https://crane.cachix.org" 14 | "https://nix-community.cachix.org" 15 | ]; 16 | nixConfig.extra-trusted-public-keys = [ 17 | "bytecodealliance.cachix.org-1:0SBgh//n2n0heh0sDFhTm+ZKBRy2sInakzFGfzN531Y=" 18 | "wasmcloud.cachix.org-1:9gRBzsKh+x2HbVVspreFg/6iFRiD4aOcUQfXVDl3hiM=" 19 | "nixify.cachix.org-1:95SiUQuf8Ij0hwDweALJsLtnMyv/otZamWNRp1Q1pXw=" 20 | "crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=" 21 | "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" 22 | ]; 23 | 24 | inputs.nix-log.inputs.nixify.follows = "nixify"; 25 | inputs.nix-log.inputs.nixlib.follows = "nixlib"; 26 | inputs.nix-log.url = "github:rvolosatovs/nix-log"; 27 | inputs.nixify.inputs.nix-log.follows = "nix-log"; 28 | inputs.nixify.inputs.nixlib.follows = "nixlib"; 29 | inputs.nixify.url = "github:rvolosatovs/nixify"; 30 | inputs.nixlib.url = "github:nix-community/nixpkgs.lib"; 31 | 32 | outputs = { 33 | self, 34 | nix-log, 35 | nixify, 36 | nixlib, 37 | ... 38 | }: 39 | with nixlib.lib; 40 | with builtins; 41 | with nix-log.lib; 42 | with nixify.lib; let 43 | lib.tar = { 44 | wit-deps ? self.packages.${pkgs.buildPlatform.system}.wit-deps, 45 | id, 46 | lock, 47 | manifest, 48 | outputHashAlgo ? "sha512", 49 | pkgs, 50 | }: let 51 | outputHash = (readTOML lock).${id}.${outputHashAlgo}; 52 | in 53 | trace' "wit-deps.lib.tar" { 54 | inherit 55 | id 56 | lock 57 | manifest 58 | outputHash 59 | outputHashAlgo 60 | ; 61 | } 62 | pkgs.stdenv.mkDerivation { 63 | inherit 64 | outputHash 65 | outputHashAlgo 66 | ; 67 | 68 | name = "wit-deps-dep-${id}.tar"; 69 | builder = pkgs.writeShellScript "wit-deps-tar" '' 70 | ${wit-deps}/bin/wit-deps --lock ${lock} --manifest ${manifest} tar ${id} --output $out 71 | ''; 72 | 73 | preferLocalBuild = true; 74 | 75 | outputType = "flat"; 76 | }; 77 | 78 | lib.lock = { 79 | wit-deps ? self.packages.${pkgs.buildPlatform.system}.wit-deps, 80 | lock, 81 | manifest, 82 | pkgs, 83 | }: 84 | trace' "wit-deps.lib.lock" { 85 | inherit 86 | lock 87 | manifest 88 | ; 89 | } 90 | mapAttrs (id: _: 91 | pkgs.stdenv.mkDerivation { 92 | name = "wit-deps-dep-${id}"; 93 | src = lib.tar { 94 | inherit 95 | wit-deps 96 | id 97 | lock 98 | manifest 99 | pkgs 100 | ; 101 | }; 102 | installPhase = '' 103 | mkdir -p $out 104 | mv * $out 105 | ''; 106 | preferLocalBuild = true; 107 | }) 108 | (readTOML lock); 109 | 110 | lib.writeLockScript = { 111 | wit-deps ? self.packages.${pkgs.buildPlatform.system}.wit-deps, 112 | lock, 113 | manifest, 114 | out ? "$out", 115 | pkgs, 116 | }: let 117 | lock' = lib.lock { 118 | inherit 119 | wit-deps 120 | lock 121 | manifest 122 | pkgs 123 | ; 124 | }; 125 | in 126 | trace' "wit-deps.lib.writeLockScript" { 127 | inherit 128 | lock 129 | manifest 130 | out 131 | ; 132 | } 133 | pkgs.writeShellScript "wit-deps-lock" (concatLines ( 134 | [ 135 | '' 136 | mkdir -p ${out} 137 | '' 138 | ] 139 | ++ ( 140 | attrValues ( 141 | mapAttrs (id: dep: '' 142 | ln -s ${dep} ${out}/${id} 143 | '') 144 | lock' 145 | ) 146 | ) 147 | )); 148 | in 149 | rust.mkFlake { 150 | src = ./.; 151 | 152 | name = "wit-deps"; 153 | 154 | excludePaths = [ 155 | ".github" 156 | ".gitignore" 157 | "CHANGELOG.md" 158 | "CODE_OF_CONDUCT.md" 159 | "flake.lock" 160 | "flake.nix" 161 | "garnix.yaml" 162 | "LICENSE" 163 | "ORG_CODE_OF_CONDUCT.md" 164 | "README.md" 165 | ]; 166 | 167 | targets.wasm32-unknown-unknown = false; 168 | targets.wasm32-wasip1 = false; 169 | targets.wasm32-wasip2 = false; 170 | 171 | clippy.allTargets = true; 172 | clippy.deny = ["warnings"]; 173 | clippy.workspace = true; 174 | 175 | doc.packages = ["wit-deps"]; 176 | 177 | test.workspace = true; 178 | 179 | buildOverrides = { 180 | pkgs, 181 | pkgsCross ? pkgs, 182 | ... 183 | } @ args: { 184 | doCheck, 185 | preBuild ? "", 186 | ... 187 | } @ craneArgs: 188 | with pkgsCross; let 189 | lock.build-test = lib.writeLockScript ({ 190 | inherit pkgs; 191 | 192 | lock = ./tests/build/wit/deps.lock; 193 | manifest = ./tests/build/wit/deps.toml; 194 | out = "./tests/build/wit/deps"; 195 | } 196 | // optionalAttrs (doCheck && !(args ? pkgsCross)) { 197 | # for native builds, break the recursive dependency cycle by using untested wit-deps to lock deps 198 | wit-deps = self.packages.${pkgs.buildPlatform.system}.wit-deps.overrideAttrs (_: { 199 | inherit preBuild; 200 | doCheck = false; 201 | }); 202 | }); 203 | in 204 | optionalAttrs (craneArgs ? cargoArtifacts) { 205 | # only lock deps in non-dep builds 206 | preBuild = 207 | preBuild 208 | + '' 209 | ${lock.build-test} 210 | ''; 211 | }; 212 | 213 | withChecks = { 214 | checks, 215 | pkgs, 216 | ... 217 | }: 218 | checks 219 | // self.lib.lock { 220 | inherit 221 | pkgs 222 | ; 223 | lock = ./examples/http/wit/deps.lock; 224 | manifest = ./examples/http/wit/deps.toml; 225 | }; 226 | } 227 | // { 228 | inherit lib; 229 | }; 230 | } 231 | -------------------------------------------------------------------------------- /garnix.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | include: 3 | - 'checks.aarch64-darwin.*' 4 | - 'checks.x86_64-linux.*' 5 | 6 | - devShells.aarch64-darwin 7 | - devShells.aarch64-linux 8 | - devShells.x86_64-linux 9 | 10 | - packages.aarch64-darwin.default 11 | - packages.aarch64-darwin.wit-deps 12 | - packages.aarch64-darwin.wit-deps-aarch64-apple-darwin 13 | - packages.aarch64-darwin.wit-deps-aarch64-apple-darwin-oci 14 | 15 | - packages.x86_64-linux.default 16 | - packages.x86_64-linux.wit-deps 17 | - packages.x86_64-linux.wit-deps-aarch64-unknown-linux-musl 18 | - packages.x86_64-linux.wit-deps-aarch64-unknown-linux-musl-oci 19 | - packages.x86_64-linux.wit-deps-armv7-unknown-linux-musleabihf 20 | - packages.x86_64-linux.wit-deps-armv7-unknown-linux-musleabihf-oci 21 | - packages.x86_64-linux.wit-deps-x86_64-unknown-linux-musl 22 | - packages.x86_64-linux.wit-deps-x86_64-unknown-linux-musl-oci 23 | -------------------------------------------------------------------------------- /src/bin/wit-deps/main.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::pedantic)] 2 | 3 | use std::path::PathBuf; 4 | use std::process::ExitCode; 5 | 6 | use anyhow::Context; 7 | use clap::{Parser, Subcommand}; 8 | use tokio::fs::File; 9 | use tokio::io; 10 | use tokio_util::compat::TokioAsyncWriteCompatExt; 11 | use tracing_subscriber::prelude::*; 12 | use wit_deps::Identifier; 13 | 14 | #[derive(Debug, Parser)] 15 | #[command(author, version, about, long_about = None)] 16 | struct Cli { 17 | /// Dependency output directory 18 | #[arg(short, long, default_value = "wit/deps")] 19 | deps: PathBuf, 20 | 21 | /// Dependency manifest path 22 | #[arg(short, long, default_value = "wit/deps.toml")] 23 | manifest: PathBuf, 24 | 25 | /// Dependency lock path 26 | #[arg(short, long, default_value = "wit/deps.lock")] 27 | lock: PathBuf, 28 | 29 | #[command(subcommand)] 30 | command: Option, 31 | } 32 | 33 | #[derive(Debug, Subcommand)] 34 | enum Command { 35 | /// Lock dependencies 36 | Lock { 37 | /// Exit with an error code if dependencies were not already in-sync 38 | #[arg(long, short, action)] 39 | check: bool, 40 | }, 41 | /// Update dependencies 42 | Update, 43 | /// Write a deterministic tar containing the `wit` subdirectory for a package to stdout 44 | Tar { 45 | /// Package to archive 46 | package: Identifier, 47 | 48 | /// Optional output path, if not specified, the archive will be written to stdout 49 | #[arg(short, long)] 50 | output: Option, 51 | }, 52 | } 53 | 54 | #[tokio::main] 55 | async fn main() -> anyhow::Result { 56 | tracing_subscriber::registry() 57 | .with( 58 | tracing_subscriber::fmt::layer() 59 | .compact() 60 | .without_time() 61 | .with_file(false) 62 | .with_target(false) 63 | .with_writer(std::io::stderr), 64 | ) 65 | .with( 66 | tracing_subscriber::EnvFilter::builder() 67 | .with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into()) 68 | .from_env_lossy(), 69 | ) 70 | .init(); 71 | 72 | let Cli { 73 | deps: deps_path, 74 | manifest: manifest_path, 75 | lock: lock_path, 76 | command, 77 | } = Cli::parse(); 78 | 79 | match command { 80 | None => wit_deps::lock_path(manifest_path, lock_path, deps_path) 81 | .await 82 | .map(|_| ExitCode::SUCCESS), 83 | Some(Command::Lock { check }) => wit_deps::lock_path(manifest_path, lock_path, deps_path) 84 | .await 85 | .map(|updated| { 86 | if check && updated { 87 | ExitCode::FAILURE 88 | } else { 89 | ExitCode::SUCCESS 90 | } 91 | }), 92 | Some(Command::Update) => wit_deps::update_path(manifest_path, lock_path, deps_path) 93 | .await 94 | .map(|()| ExitCode::SUCCESS), 95 | Some(Command::Tar { package, output }) => { 96 | wit_deps::lock_path(manifest_path, lock_path, &deps_path) 97 | .await 98 | .map(|_| ())?; 99 | let package = deps_path.join(package); 100 | if let Some(output) = output { 101 | let output = File::create(&output).await.with_context(|| { 102 | format!("failed to create output path `{}`", output.display()) 103 | })?; 104 | wit_deps::tar(package, output.compat_write()).await?; 105 | } else { 106 | wit_deps::tar(package, io::stdout().compat_write()).await?; 107 | } 108 | Ok(ExitCode::SUCCESS) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tests/build/.gitignore: -------------------------------------------------------------------------------- 1 | /wit/deps 2 | /subcrate/wit/deps 3 | -------------------------------------------------------------------------------- /tests/build/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "build-test" 3 | version = "0.0.0" 4 | description = "build.rs test" 5 | 6 | authors.workspace = true 7 | categories.workspace = true 8 | edition.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | 12 | [dependencies] 13 | subcrate = { path = "./subcrate" } 14 | wit-bindgen = { workspace = true, features = ["default"] } 15 | 16 | [build-dependencies] 17 | anyhow = { workspace = true, features = ["std"] } 18 | tracing-subscriber = { workspace = true, features = ["ansi", "env-filter", "fmt", "json", "std"] } 19 | wit-deps = { workspace = true } 20 | -------------------------------------------------------------------------------- /tests/build/build.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use tracing_subscriber::prelude::*; 3 | 4 | fn main() -> anyhow::Result<()> { 5 | tracing_subscriber::registry() 6 | .with( 7 | tracing_subscriber::fmt::layer() 8 | .pretty() 9 | .without_time() 10 | .with_writer(std::io::stderr), 11 | ) 12 | .with( 13 | tracing_subscriber::EnvFilter::try_from_default_env() 14 | .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info,wit_deps=trace")), 15 | ) 16 | .init(); 17 | 18 | wit_deps::lock_sync!().context("failed to lock root WIT dependencies")?; 19 | 20 | println!("cargo:rerun-if-changed=wit/deps"); 21 | println!("cargo:rerun-if-changed=wit/deps.lock"); 22 | println!("cargo:rerun-if-changed=wit/deps.toml"); 23 | 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /tests/build/src/lib.rs: -------------------------------------------------------------------------------- 1 | wit_bindgen::generate!({ 2 | world: "build:root/build", 3 | generate_all, 4 | }); 5 | -------------------------------------------------------------------------------- /tests/build/subcrate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "subcrate" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | wit-bindgen = { workspace = true, features = ["default"] } 8 | 9 | [build-dependencies] 10 | anyhow = { workspace = true, features = ["std"] } 11 | tracing-subscriber = { workspace = true, features = ["ansi", "env-filter", "fmt", "json", "std"] } 12 | wit-deps = { workspace = true } 13 | -------------------------------------------------------------------------------- /tests/build/subcrate/build.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use tracing_subscriber::prelude::*; 3 | 4 | fn main() -> anyhow::Result<()> { 5 | tracing_subscriber::registry() 6 | .with( 7 | tracing_subscriber::fmt::layer() 8 | .pretty() 9 | .without_time() 10 | .with_writer(std::io::stderr), 11 | ) 12 | .with( 13 | tracing_subscriber::EnvFilter::try_from_default_env() 14 | .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info,wit_deps=trace")), 15 | ) 16 | .init(); 17 | 18 | // NOTE: Root WIT definitions are a dependency of this crate, so those have to be locked first 19 | wit_deps::lock_sync!("../wit").context("failed to lock root WIT dependencies")?; 20 | wit_deps::lock_sync!().context("failed to lock own WIT dependencies")?; 21 | 22 | println!("cargo:rerun-if-changed=wit/deps"); 23 | println!("cargo:rerun-if-changed=wit/deps.lock"); 24 | println!("cargo:rerun-if-changed=wit/deps.toml"); 25 | 26 | println!("cargo:rerun-if-changed=../wit"); 27 | 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /tests/build/subcrate/src/lib.rs: -------------------------------------------------------------------------------- 1 | wit_bindgen::generate!({ 2 | world: "build:subcrate/subcrate", 3 | generate_all, 4 | }); 5 | -------------------------------------------------------------------------------- /tests/build/subcrate/wit/deps.lock: -------------------------------------------------------------------------------- 1 | [build] 2 | path = "../../wit" 3 | sha256 = "2f3f31cc16c93ed0424160598ca7b2393d1390775323402f69c5c54d94d3b92a" 4 | sha512 = "1c23db559bc2c17ba5bc4103902450a71b042664c0473190e38c788643372c98eafd5dc28d7e3a5a5e2dfda549e2c65edf81fc79b749f49f740cf20a7568de82" 5 | deps = ["cli", "clocks", "clocks-0-3-0", "filesystem", "http", "io", "logging", "random", "sockets"] 6 | 7 | [cli] 8 | sha256 = "4dadd13d55aaf626833d1f4b9c34a17b0f04e993babd09552b785cda3b95ea76" 9 | sha512 = "898dcc4e8c15d18acc6b88dbe232336fa4d19019430a910dbc9e7aeaace3077a164af3be9f002de6e7e65ef693df340801ac0c7e421e9a746bf1b6d698a90835" 10 | 11 | [clocks] 12 | sha256 = "93a701968a7dd3c5d69031bc0601681c468972fdf7e28a93bb6150a67d6ebe8b" 13 | sha512 = "98fca567c7a01887b0fb38981f1772169b6ea8de475b546508f8b86738d84e44ba95cae81def40ac34e8809f5f60e85224077ab8cb6d6d5d6296acc1df73c159" 14 | 15 | [clocks-0-3-0] 16 | sha256 = "26e315db0d371495f8834edfc0e479042f94152ce677d96d54d3623d0e4ffb1e" 17 | sha512 = "e1c76f499435841316f9287b88d8173558e64f277c321ff390556de8707a0b18dd6c1749bbb17bbbba8d523da246ef6eb05c990ceddb762e03efb2ae30cacc76" 18 | 19 | [filesystem] 20 | sha256 = "69d42fb10a04a33545b17e055f13db9b1e10e82ba0ed5bdb52334e40dc07c679" 21 | sha512 = "612effbac6f4804fe0c29dae20b78bbba59e52cb754c15402f5fe229c3153a221e0fbdff1d9d00ceaa3fe049c6a95523a5b99f772f1c16d972eade2c88326a30" 22 | 23 | [http] 24 | sha256 = "b5f254849300c7d6ec1d65dff9c8b3d24c6c8a9ee8bf29013fb17b3728820a34" 25 | sha512 = "e421df67df7722a8fb9673bb8c455581e6b52be90073518569d22ae70368043f095adfd03e0655678cd9622965be3d73f60054bbd3a81ea21a4e58ff68e1b461" 26 | 27 | [io] 28 | sha256 = "1cccbfe4122686ea57a25cd368e8cdfc408cbcad089f47fb6685b6f92e96f050" 29 | sha512 = "7a95f964c13da52611141acd89bc8876226497f128e99dd176a4270c5b5efbd8cc847b5fbd1a91258d028c646db99e0424d72590cf1caf20f9f3a3343fad5017" 30 | 31 | [logging] 32 | sha256 = "ad81d8b7f7a8ceb729cf551f1d24586f0de9560a43eea57a9bb031d2175804e1" 33 | sha512 = "1687ad9a02ab3e689443e67d1a0605f58fc5dea828d2e4d2c7825c6002714fac9bd4289b1a68b61a37dcca6c3b421f4c8ed4b1e6cc29f6460e0913cf1bf11c04" 34 | 35 | [random] 36 | sha256 = "dd0c91e7125172eb8fd4568e15ad9fc7305643015e6ece4396c3cc5e8c2bf79a" 37 | sha512 = "d1ca2e7b0616a94a3b39d1b9450bb3fb595b01fd94a8626ad75433038dde40988ecb41ab93a374d569ab72163af3b30038d7bfc3499b9c07193181f4f1d9292a" 38 | 39 | [sockets] 40 | sha256 = "2bc0f65a8046207ee3330ad7d63f6fafeafd4cc0ea4084f081bd5e4f7b177e74" 41 | sha512 = "3e5490e41547dffa78d52631825d93da8d60f4af0246cbaf97e1ecb879285953a86d5f1f390b10c32f91dd7eaec6f43e625a26b1c92c32a0c86fde428aedaaab" 42 | -------------------------------------------------------------------------------- /tests/build/subcrate/wit/deps.toml: -------------------------------------------------------------------------------- 1 | build = "../../wit" 2 | -------------------------------------------------------------------------------- /tests/build/subcrate/wit/world.wit: -------------------------------------------------------------------------------- 1 | package build:subcrate; 2 | 3 | world subcrate { 4 | import build:root/foo; 5 | } 6 | -------------------------------------------------------------------------------- /tests/build/wit/build.wit: -------------------------------------------------------------------------------- 1 | package build:root; 2 | 3 | interface foo { 4 | foo: func() -> u32; 5 | } 6 | 7 | world build { 8 | import wasi:http/incoming-handler@0.2.3; 9 | import wasi:logging/logging@0.1.0-draft; 10 | } 11 | -------------------------------------------------------------------------------- /tests/build/wit/deps.lock: -------------------------------------------------------------------------------- 1 | [cli] 2 | sha256 = "4dadd13d55aaf626833d1f4b9c34a17b0f04e993babd09552b785cda3b95ea76" 3 | sha512 = "898dcc4e8c15d18acc6b88dbe232336fa4d19019430a910dbc9e7aeaace3077a164af3be9f002de6e7e65ef693df340801ac0c7e421e9a746bf1b6d698a90835" 4 | 5 | [clocks] 6 | sha256 = "93a701968a7dd3c5d69031bc0601681c468972fdf7e28a93bb6150a67d6ebe8b" 7 | sha512 = "98fca567c7a01887b0fb38981f1772169b6ea8de475b546508f8b86738d84e44ba95cae81def40ac34e8809f5f60e85224077ab8cb6d6d5d6296acc1df73c159" 8 | 9 | [clocks-0-3-0] 10 | url = "https://github.com/WebAssembly/wasi-clocks/archive/3850f9dfda730d4eab51610caf76c8ef3b31cf14.tar.gz" 11 | subdir = "wit-0.3.0-draft" 12 | sha256 = "26e315db0d371495f8834edfc0e479042f94152ce677d96d54d3623d0e4ffb1e" 13 | sha512 = "e1c76f499435841316f9287b88d8173558e64f277c321ff390556de8707a0b18dd6c1749bbb17bbbba8d523da246ef6eb05c990ceddb762e03efb2ae30cacc76" 14 | 15 | [filesystem] 16 | sha256 = "69d42fb10a04a33545b17e055f13db9b1e10e82ba0ed5bdb52334e40dc07c679" 17 | sha512 = "612effbac6f4804fe0c29dae20b78bbba59e52cb754c15402f5fe229c3153a221e0fbdff1d9d00ceaa3fe049c6a95523a5b99f772f1c16d972eade2c88326a30" 18 | 19 | [http] 20 | url = "https://github.com/WebAssembly/wasi-http/archive/8eb7f8a6525c19fde576f9f6acc5b2a25d4f5df5.tar.gz" 21 | sha256 = "b5f254849300c7d6ec1d65dff9c8b3d24c6c8a9ee8bf29013fb17b3728820a34" 22 | sha512 = "e421df67df7722a8fb9673bb8c455581e6b52be90073518569d22ae70368043f095adfd03e0655678cd9622965be3d73f60054bbd3a81ea21a4e58ff68e1b461" 23 | deps = ["cli", "clocks", "filesystem", "io", "random", "sockets"] 24 | 25 | [io] 26 | sha256 = "1cccbfe4122686ea57a25cd368e8cdfc408cbcad089f47fb6685b6f92e96f050" 27 | sha512 = "7a95f964c13da52611141acd89bc8876226497f128e99dd176a4270c5b5efbd8cc847b5fbd1a91258d028c646db99e0424d72590cf1caf20f9f3a3343fad5017" 28 | 29 | [logging] 30 | url = "https://github.com/WebAssembly/wasi-logging/archive/d31c41d0d9eed81aabe02333d0025d42acf3fb75.tar.gz" 31 | sha256 = "ad81d8b7f7a8ceb729cf551f1d24586f0de9560a43eea57a9bb031d2175804e1" 32 | sha512 = "1687ad9a02ab3e689443e67d1a0605f58fc5dea828d2e4d2c7825c6002714fac9bd4289b1a68b61a37dcca6c3b421f4c8ed4b1e6cc29f6460e0913cf1bf11c04" 33 | 34 | [random] 35 | sha256 = "dd0c91e7125172eb8fd4568e15ad9fc7305643015e6ece4396c3cc5e8c2bf79a" 36 | sha512 = "d1ca2e7b0616a94a3b39d1b9450bb3fb595b01fd94a8626ad75433038dde40988ecb41ab93a374d569ab72163af3b30038d7bfc3499b9c07193181f4f1d9292a" 37 | 38 | [sockets] 39 | sha256 = "2bc0f65a8046207ee3330ad7d63f6fafeafd4cc0ea4084f081bd5e4f7b177e74" 40 | sha512 = "3e5490e41547dffa78d52631825d93da8d60f4af0246cbaf97e1ecb879285953a86d5f1f390b10c32f91dd7eaec6f43e625a26b1c92c32a0c86fde428aedaaab" 41 | -------------------------------------------------------------------------------- /tests/build/wit/deps.toml: -------------------------------------------------------------------------------- 1 | http = "https://github.com/WebAssembly/wasi-http/archive/8eb7f8a6525c19fde576f9f6acc5b2a25d4f5df5.tar.gz" 2 | logging = "https://github.com/WebAssembly/wasi-logging/archive/d31c41d0d9eed81aabe02333d0025d42acf3fb75.tar.gz" 3 | clocks-0-3-0 = { url = "https://github.com/WebAssembly/wasi-clocks/archive/3850f9dfda730d4eab51610caf76c8ef3b31cf14.tar.gz", subdir = "wit-0.3.0-draft" } 4 | --------------------------------------------------------------------------------