├── .gitattributes ├── .github ├── CODEOWNERS ├── actions │ └── mdbook │ │ └── action.yml └── workflows │ ├── deploy.yml │ └── mdbook.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── ORG_CODE_OF_CONDUCT.md ├── README.md ├── component-model ├── .gitignore ├── book.toml ├── examples │ ├── example-host │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── add.wasm │ │ └── src │ │ │ ├── async_add.rs │ │ │ ├── main.rs │ │ │ ├── state.rs │ │ │ └── sync_add.rs │ └── tutorial │ │ ├── README.md │ │ ├── adder │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── bindings.rs │ │ │ └── lib.rs │ │ ├── calculator │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── bindings.rs │ │ │ └── lib.rs │ │ ├── command │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── bindings.rs │ │ │ └── main.rs │ │ ├── composition.wac │ │ ├── jco │ │ ├── README.md │ │ ├── cli-calc.js │ │ ├── index.html │ │ └── package.json │ │ └── wit │ │ ├── adder │ │ └── world.wit │ │ └── calculator │ │ └── world.wit ├── src │ ├── SUMMARY.md │ ├── advanced │ │ └── canonical-abi.md │ ├── creating-and-consuming.md │ ├── creating-and-consuming │ │ ├── authoring.md │ │ ├── composing.md │ │ ├── distributing.md │ │ └── running.md │ ├── design │ │ ├── component-model-concepts.md │ │ ├── components.md │ │ ├── interfaces.md │ │ ├── packages.md │ │ ├── why-component-model.md │ │ ├── wit.md │ │ └── worlds.md │ ├── introduction.md │ ├── language-support.md │ ├── language-support │ │ ├── c.md │ │ ├── csharp.md │ │ ├── go.md │ │ ├── javascript.md │ │ ├── python.md │ │ └── rust.md │ ├── reference │ │ ├── useful-links.md │ │ └── videos.md │ ├── robots.txt │ ├── runtimes │ │ ├── jco.md │ │ └── wasmtime.md │ └── tutorial.md └── theme │ └── head.hbs ├── justfile └── scripts └── generate_sitemap.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Ensure all text is checked out with Unix EOL 2 | * text=auto eol=lf 3 | 4 | # Ensure binary files aren't considered as text 5 | *.wasm binary 6 | 7 | # Genrated code from cargo-component 8 | component-model/examples/tutorial/*/src/bindings.rs linguist-generated 9 | 10 | Cargo-component.lock linguist-language=toml 11 | 12 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This file is described here: https://help.github.com/en/articles/about-code-owners 2 | 3 | # Global Owners: These members are Maintainers of the docs 4 | * @itowlson 5 | * @kate-goldenring 6 | * @vados-cosmonic -------------------------------------------------------------------------------- /.github/actions/mdbook/action.yml: -------------------------------------------------------------------------------- 1 | name: mdbook 2 | 3 | description: | 4 | Run the mdbook build, optionally outputting a pages artifact 5 | 6 | inputs: 7 | publish-pages-artifact: 8 | type: boolean 9 | required: false 10 | default: false 11 | description: | 12 | Whether to publish a pages artifact 13 | 14 | publish-domain: 15 | type: string 16 | required: false 17 | default: "component-model.bytecodealliance.org" 18 | description: | 19 | Path to which to store the artifac 20 | 21 | mdbook-version: 22 | type: string 23 | required: false 24 | default: "0.4.21" 25 | description: | 26 | Version of mdbook to use (ex. '0.4.21') 27 | 28 | mdbook-alerts-version: 29 | type: string 30 | required: false 31 | default: "0.6.7" 32 | description: | 33 | Version of mdbook-alerts to use (ex. '0.6.7') 34 | 35 | mdbook-linkcheck-version: 36 | type: string 37 | required: false 38 | default: "0.7.7" 39 | description: | 40 | Version of mdbook-linkcheck to use (ex. '0.7.7') 41 | 42 | runs: 43 | using: composite 44 | steps: 45 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 46 | 47 | - uses: extractions/setup-just@v2 48 | 49 | - uses: taiki-e/cache-cargo-install-action@4d586f211d9b0bca9e7b59e57e2a0febf36c0929 # v2.1.1 50 | with: 51 | tool: "mdbook@${{ inputs.mdbook-version }}" 52 | 53 | - uses: taiki-e/cache-cargo-install-action@4d586f211d9b0bca9e7b59e57e2a0febf36c0929 # v2.1.1 54 | with: 55 | tool: "mdbook-alerts@${{ inputs.mdbook-alerts-version }}" 56 | 57 | - uses: taiki-e/cache-cargo-install-action@4d586f211d9b0bca9e7b59e57e2a0febf36c0929 # v2.1.1 58 | with: 59 | tool: "mdbook-linkcheck@${{ inputs.mdbook-linkcheck-version }}" 60 | 61 | - name: Setup Python 62 | uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 63 | with: 64 | python-version: '3.13' 65 | 66 | - name: Build with mdBook 67 | shell: bash 68 | id: book-build 69 | env: 70 | PUBLISH_DOMAIN: ${{ inputs.publish-domain }} 71 | run: | 72 | just build-book 73 | echo "output-path=$(just print-book-dir)" >> $GITHUB_OUTPUT 74 | 75 | - name: Generate sitemap 76 | shell: bash 77 | run: | 78 | just build-sitemap 79 | 80 | - name: Ensure publish-domain was provided 81 | if: ${{ inputs.publish-pages-artifact }} 82 | shell: bash 83 | run: | 84 | if [ -z "${{ inputs.publish-domain }}" ]; then 85 | echo "[error] publish-domain input is required when publishing"; 86 | exit 1; 87 | fi 88 | 89 | - name: Setup Pages 90 | if: ${{ inputs.publish-pages-artifact }} 91 | id: pages 92 | uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0 93 | 94 | - name: Upload artifact 95 | if: ${{ inputs.publish-pages-artifact }} 96 | uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 97 | with: 98 | path: ${{ steps.book-build.outputs.output-path }} 99 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy mdBook site to Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | workflow_dispatch: 9 | inputs: 10 | ref: 11 | required: false 12 | type: string 13 | default: main 14 | description: | 15 | The git ref to deploy (ex. 'main', 'branch', '') 16 | 17 | publish-domain: 18 | required: false 19 | type: string 20 | default: "component-model.bytecodealliance.org" 21 | description: | 22 | The domain to which to publish (ex. 'component-model.bytecodealliance.org') 23 | 24 | permissions: 25 | contents: read 26 | pages: write 27 | id-token: write 28 | 29 | concurrency: 30 | group: "pages" 31 | cancel-in-progress: false 32 | 33 | env: 34 | MDBOOK_VERSION: 0.4.21 35 | MDBOOK_ALERTS_VERSION: 0.6.7 36 | MDBOOK_LINKCHECK_VERSION: 0.7.7 37 | ARTIFACT_PATH: ./component-model/book/html 38 | 39 | jobs: 40 | build: 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 44 | with: 45 | ref: ${{ inputs.ref || 'main' }} 46 | 47 | - name: build mdbook 48 | uses: ./.github/actions/mdbook 49 | with: 50 | publish-pages-artifact: true 51 | publish-domain: ${{ inputs.publish-domain || 'component-model.bytecodealliance.org' }} 52 | 53 | deploy: 54 | if: ${{ github.repository_owner == 'bytecodealliance' }} 55 | runs-on: ubuntu-latest 56 | needs: 57 | - build 58 | environment: 59 | name: github-pages 60 | url: ${{ steps.deployment.outputs.page_url }} 61 | steps: 62 | - name: Deploy to GitHub Pages 63 | id: deployment 64 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 65 | -------------------------------------------------------------------------------- /.github/workflows/mdbook.yml: -------------------------------------------------------------------------------- 1 | name: mdbook 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 11 | 12 | - name: build mdbook 13 | uses: ./.github/actions/mdbook 14 | with: 15 | publish-pages-artifact: true 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | **/.vscode 3 | *.swp 4 | *.swo 5 | 6 | # Build artifacts from examples 7 | **/jco/bindings 8 | **/examples/**/*.wasm 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | The Component Model documentation is a [Bytecode Alliance](https://bytecodealliance.org/) project, and follows the Bytecode Alliance's [Code of Conduct](CODE_OF_CONDUCT.md) and [Organizational Code of Conduct](ORG_CODE_OF_CONDUCT.md). 4 | 5 | ## Using this repository 6 | 7 | You can run the website locally using the [mdBook](https://rust-lang.github.io/mdBook/index.html) command line tool. 8 | 9 | ### Prerequisites 10 | 11 | To use this repository, you need [mdBook](https://rust-lang.github.io/mdBook/guide/installation.html) installed on your workstation. 12 | 13 | This repository also makes use of mdBook plugins. To install mdBook and the plugins for this project, you can use [`cargo`][cargo]: 14 | 15 | ```console 16 | cargo install --version 0.4.21 mdbook 17 | cargo install --version 0.6.7 mdbook-alerts 18 | cargo install --version 0.7.7 mdbook-linkcheck 19 | ``` 20 | 21 | [cargo]: https://doc.rust-lang.org/cargo 22 | 23 | ### Running the website locally 24 | 25 | After installing mdBook, you'll need to clone the code via git and navigate to the directory. 26 | 27 | ```bash 28 | git clone https://github.com/bytecodealliance/component-docs 29 | cd component-docs 30 | ``` 31 | 32 | To build and test the site locally, run: 33 | 34 | ```bash 35 | cd component-model 36 | mdbook serve --open 37 | ``` 38 | 39 | You can use mdbook-linkcheck to check the links in the docs automatically. First, add the lines following lines in `book.toml`. 40 | 41 | ```toml 42 | [output.linkcheck] 43 | follow-web-links = true 44 | ``` 45 | 46 | After this, install the extension and build the project again. You should see the link checker do its work in the console output. 47 | 48 | ```bash 49 | cargo install mdbook-linkcheck 50 | mdbook build 51 | ``` 52 | 53 | Don't forget to remove the changes in `book.toml` before you commit! 54 | 55 | ## Submitting Changes 56 | 57 | You can click the Fork button in the upper-right area of the screen to create a copy of this repository in your GitHub account. This copy is called a fork. Make any changes you want in your fork, and when you are ready to submit those changes, go to your fork and create a new pull request to let us know about it. 58 | 59 | Everyone is welcome to submit a pull request! Once your pull request is created, we'll try to get to reviewing it or responding to it in at most a few days. As the owner of the pull request, it is your responsibility to modify your pull request to address the feedback that has been provided to you by the reviewer. 60 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | [WebAssembly Components Documentation](https://github.com/bytecodealliance/component-docs) © 2023 by [The Bytecode Alliance Contributors](https://bytecodealliance.org) is licensed under [CC BY 4.0](http://creativecommons.org/licenses/by/4.0/) 2 | drawing 3 | drawing 4 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebAssembly Component Model Documentation 2 | 3 | This repository contains user-facing documentation for the WebAssembly component model. "User-facing" means it focuses on how to use the component model, not on the internal design and implementation details. 4 | 5 | The documentation is published at . 6 | 7 | Documentation on WASI (WebAssembly System Interface) can be found at https://wasi.dev/. See the [`wasi.dev`](https://github.com/bytecodealliance/wasi.dev) repository to contribute to that documentation. 8 | 9 | Contributions are welcome - see [Contributing](./CONTRIBUTING.md) for more info. Planned work is listed in the Issues section, but if there's content missing that you think would be helpful to gain understanding of the component model, please feel free to add a new issue, or send a PR directly! 10 | -------------------------------------------------------------------------------- /component-model/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | .spin 3 | -------------------------------------------------------------------------------- /component-model/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Bytecode Alliance"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "The WebAssembly Component Model" 7 | 8 | [output.html] 9 | git-repository-url = "https://github.com/bytecodealliance/component-docs/tree/main/component-model" 10 | edit-url-template = "https://github.com/bytecodealliance/component-docs/tree/main/component-model/{path}" 11 | additional-css = ["theme/head.hbs"] 12 | 13 | [preprocessor.alerts] 14 | 15 | [output.linkcheck] 16 | -------------------------------------------------------------------------------- /component-model/examples/example-host/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-host" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = """ 6 | Example Rust-based WebAssembly host that executes WebAssembly components 7 | """ 8 | 9 | [dependencies] 10 | anyhow = "1.0.72" 11 | async-std = { version = "1.13", features = ["attributes"] } 12 | clap = { version = "4", features = ["derive"] } 13 | wasmtime = "27.0" 14 | wasmtime-wasi = "27.0" 15 | -------------------------------------------------------------------------------- /component-model/examples/example-host/README.md: -------------------------------------------------------------------------------- 1 | # Rust Host Application for Example Components 2 | 3 | This is a native Rust CLI application that can run [WebAssembly Components][wasm-components] of the `adder` world, 4 | defined in [`examples/tutorial/wit/adder/world.wit`][adder-wit], using [WebAssembly Interface Types ("WIT")][wit]. 5 | 6 | The `adder` world exports an interface called `add` which defines an function that takes two unsigned and adds them: 7 | 8 | ```wit 9 | package docs:adder@0.1.0; 10 | 11 | interface add { 12 | add: func(x: u32, y: u32) -> u32; 13 | } 14 | 15 | world adder { 16 | export add; 17 | } 18 | ``` 19 | 20 | The application uses WebAssembly ecosystem crates (e.g. [`wasmtime`][wasmtime]) to generate Rust bindings, instantiate WASI worlds, and 21 | executes the exported `add` function (`docs:adder/add.add`) of a provided component. 22 | 23 | This host binary takes in two unsigned 32bit integers (`u32`) operands and a path to a component. This host then: 24 | 25 | 1. Loads the component from the given path 26 | 2. Instantiates it as an implementer of the `adder` world 27 | 3. Executes the `add` function exported by the component 28 | 4. Prints the result 29 | 30 | If running with [`cargo`][cargo] (part of the [Rust toolchain][rust-toolchain]), then you should see output like the following: 31 | 32 | ``` 33 | $ cargo run --release -- 1 2 add.wasm 34 | 1 + 2 = 3 35 | ``` 36 | 37 | > [!NOTE] 38 | > `add.wasm` is available in this folder, but can be replaced with your own built WebAssembly component 39 | > at any time (written in any language that supports WebAssembly Components), given that it satisfies 40 | > the `adder` world described above. 41 | 42 | [wasmtime]: https://github.com/bytecodealliance/wasmtime 43 | [wasm-components]: https://component-model.bytecodealliance.org/design/components.html 44 | [adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit 45 | [wit]: https://component-model.bytecodealliance.org/design/wit.html 46 | [cargo]: https://doc.rust-lang.org/cargo/ 47 | [rust-toolchain]: https://www.rust-lang.org/tools/install 48 | -------------------------------------------------------------------------------- /component-model/examples/example-host/add.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytecodealliance/component-docs/65a4f09e5c8f563d9c3ebf9208e53033a6628693/component-model/examples/example-host/add.wasm -------------------------------------------------------------------------------- /component-model/examples/example-host/src/async_add.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use anyhow::Context; 4 | use wasmtime::component::{Component, Linker}; 5 | use wasmtime::{Config, Engine, Store}; 6 | 7 | use crate::state::States; 8 | 9 | mod bindings { 10 | //! Generated code for the 11 | wasmtime::component::bindgen!({ 12 | path: "../tutorial/wit/adder/world.wit", 13 | world: "adder", 14 | async: true 15 | }); 16 | } 17 | 18 | /// Perform the add operation for a given WebAssembly component 19 | /// 20 | /// This operation asynchronously (as opposed to synchronously 21 | /// without an async runtime like `tokio` or `async-std`). 22 | /// 23 | /// # Arguments 24 | /// 25 | /// * `path` - Path to the Wasm component bytes 26 | /// * `x` - The left hand side of the addition 27 | /// * `y` - The right hand side of the addition 28 | /// 29 | pub async fn add(path: PathBuf, x: u32, y: u32) -> wasmtime::Result { 30 | // Construct engine 31 | let mut config = Config::default(); 32 | config.async_support(true); 33 | let engine = Engine::new(&config)?; 34 | 35 | // Construct component 36 | let component = Component::from_file(&engine, path).context("Component file not found")?; 37 | 38 | // Construct store for storing running states of the component 39 | let wasi_view = States::new(); 40 | let mut store = Store::new(&engine, wasi_view); 41 | 42 | // Construct linker for linking interfaces. 43 | let mut linker = Linker::new(&engine); 44 | 45 | // Add wasi exports to linker to support I/O (as in `wasi:io`) interfaces 46 | // see: https://github.com/WebAssembly/wasi-io 47 | wasmtime_wasi::add_to_linker_async(&mut linker)?; 48 | 49 | // Instantiate the component as an instance of the `adder` world, 50 | // with the generated bindings 51 | let instance = bindings::Adder::instantiate_async(&mut store, &component, &linker) 52 | .await 53 | .context("Failed to instantiate the example world")?; 54 | 55 | // Call the add function on instance 56 | instance 57 | .docs_adder_add() 58 | .call_add(&mut store, x, y) 59 | .await 60 | .context("calling add function") 61 | } 62 | -------------------------------------------------------------------------------- /component-model/examples/example-host/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use std::path::PathBuf; 3 | 4 | mod async_add; 5 | mod state; 6 | mod sync_add; 7 | 8 | /// A CLI for executing WebAssembly components that 9 | /// implement the `example` world. 10 | #[derive(Parser)] 11 | #[clap(name = "add-host", version = env!("CARGO_PKG_VERSION"))] 12 | struct AddApp { 13 | /// The first operand 14 | x: u32, 15 | /// The second operand 16 | y: u32, 17 | /// The path to the component. 18 | #[clap(value_name = "COMPONENT_PATH")] 19 | component: PathBuf, 20 | } 21 | 22 | impl AddApp { 23 | async fn run(self) -> anyhow::Result<()> { 24 | let sum1 = async_add::add(self.component.clone(), self.x, self.y).await?; 25 | let sum2 = sync_add::add(self.component, self.x, self.y)?; 26 | assert_eq!(sum1, sum2); 27 | println!("{} + {} = {sum1}", self.x, self.y); 28 | Ok(()) 29 | } 30 | } 31 | 32 | #[async_std::main] 33 | async fn main() -> anyhow::Result<()> { 34 | AddApp::parse().run().await 35 | } 36 | -------------------------------------------------------------------------------- /component-model/examples/example-host/src/state.rs: -------------------------------------------------------------------------------- 1 | use wasmtime::component::ResourceTable; 2 | use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiView}; 3 | 4 | pub struct States { 5 | table: ResourceTable, 6 | ctx: WasiCtx, 7 | } 8 | 9 | impl States { 10 | pub fn new() -> Self { 11 | let table = ResourceTable::new(); 12 | let ctx = WasiCtxBuilder::new().build(); 13 | Self { table, ctx } 14 | } 15 | } 16 | 17 | impl WasiView for States { 18 | fn table(&mut self) -> &mut ResourceTable { 19 | &mut self.table 20 | } 21 | 22 | fn ctx(&mut self) -> &mut WasiCtx { 23 | &mut self.ctx 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /component-model/examples/example-host/src/sync_add.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use anyhow::Context; 4 | use wasmtime::component::{Component, Linker}; 5 | use wasmtime::{Engine, Store}; 6 | use wasmtime_wasi; 7 | 8 | use crate::state::States; 9 | 10 | mod bindings { 11 | wasmtime::component::bindgen!({ 12 | path: "../tutorial/wit/adder/world.wit", 13 | world: "adder", 14 | async: false 15 | }); 16 | } 17 | 18 | /// Perform a add operation for a given WebAssembly component 19 | /// 20 | /// This operation happens synchronously (as opposed to asynchronously 21 | /// powered by an async runtime like `tokio` or `async-std`). 22 | /// 23 | /// # Arguments 24 | /// 25 | /// * `path` - Path to the Wasm component bytes 26 | /// * `x` - The left hand side of the addition 27 | /// * `y` - The right hand side of the addition 28 | /// 29 | pub fn add(path: PathBuf, x: u32, y: u32) -> wasmtime::Result { 30 | // Construct engine 31 | let engine = Engine::default(); 32 | 33 | // Construct component 34 | let component = Component::from_file(&engine, path).context("Component file not found")?; 35 | 36 | // Construct store for storing running states of the component 37 | let wasi_view = States::new(); 38 | let mut store = Store::new(&engine, wasi_view); 39 | 40 | // Construct linker for linking interfaces. 41 | let mut linker = Linker::new(&engine); 42 | 43 | // Add wasi exports to linker to support I/O (as in `wasi:io`) interfaces 44 | // see: https://github.com/WebAssembly/wasi-io 45 | wasmtime_wasi::add_to_linker_sync(&mut linker).expect("Could not add wasi to linker"); 46 | 47 | // Instantiate the component as an instance of the `adder` world, 48 | // with the generated bindings 49 | let instance = bindings::Adder::instantiate(&mut store, &component, &linker) 50 | .context("Failed to instantiate the example world")?; 51 | 52 | // Call the add function on instance 53 | instance 54 | .docs_adder_add() 55 | .call_add(&mut store, x, y) 56 | .context("calling add function") 57 | } 58 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/README.md: -------------------------------------------------------------------------------- 1 | # Building a Calculator of Wasm Components 2 | 3 | This tutorial walks through how to compose a component to build a Wasm calculator. 4 | The WIT package for the calculator consists of a world for each mathematical operator 5 | add an `op` enum that delineates each operator. The following example interface only 6 | has an `add` operation: 7 | 8 | ```wit adder 9 | package docs:adder@0.1.0; 10 | 11 | 12 | interface add { 13 | add: func(x: u32, y: u32) -> u32; 14 | } 15 | 16 | world adder { 17 | export add; 18 | } 19 | ``` 20 | 21 | ```wit calculator 22 | package docs:calculator@0.1.0; 23 | 24 | interface calculate { 25 | enum op { 26 | add, 27 | } 28 | eval-expression: func(op: op, x: u32, y: u32) -> u32; 29 | } 30 | 31 | world calculator { 32 | export calculate; 33 | import docs:adder/add; 34 | } 35 | 36 | world app { 37 | import calculate; 38 | } 39 | ``` 40 | 41 | To expand the exercise to add more components, add another operator world, expand the enum, and modify the `command` component to call it. 42 | 43 | ## Building and running the example 44 | 45 | Use [`cargo-component`](https://github.com/bytecodealliance/cargo-component) and [`wac`](https://github.com/bytecodealliance/wac) to build and compose the calculator component. 46 | 47 | ```sh 48 | (cd calculator && cargo component build --release) 49 | (cd adder && cargo component build --release) 50 | (cd command && cargo component build --release) 51 | wac plug calculator/target/wasm32-wasip1/release/calculator.wasm --plug adder/target/wasm32-wasip1/release/adder.wasm -o composed.wasm 52 | wac plug command/target/wasm32-wasip1/release/command.wasm --plug composed.wasm -o final.wasm 53 | ``` 54 | 55 | Now, run the component with Wasmtime: 56 | 57 | ```sh 58 | wasmtime run final.wasm 1 2 add 59 | 1 + 2 = 3 60 | ``` 61 | 62 | ## Composing with the WAC Language 63 | 64 | `wac plug` is a convenience to achieve a common pattern in component compositions like above. However, composition can be arbitrarily complicated. In cases where `wac plug` is not sufficient, the WAC language can give us the ability to create arbitrarily complex compositions. To get more experience using the WAC language, let's look at how we could use it to create our composition. 65 | 66 | `wac` can compose local components and components hosted in registries. To compose local components, first move the components to a `deps` folder, the default location in which `wac` looks for local components. `wac` infers the subpath to components from the package name of components defined in a WAC file. For example, if the instantiation expression for the adder component in the WAC file is `new docs:adder-impl{}`, the local component is expected to have the following path `deps/docs/adder-impl.wasm`. With this in mind, let's move all out components to a `deps/docs` folder and rename to ease clarifying WAC concepts. 67 | 68 | ```sh 69 | mkdir -p deps/docs 70 | cp adder/target/wasm32-wasip1/release/adder.wasm deps/docs/adder-impl.wasm 71 | cp calculator/target/wasm32-wasip1/release/calculator.wasm deps/docs/calculator-impl.wasm 72 | cp command/target/wasm32-wasip1/release/command.wasm deps/docs/command-impl.wasm 73 | ``` 74 | 75 | Now we are ready to construct a WAC file to define our composition. Ours instantiates our three components, declaring 76 | which components satisfy each of their imports. It ends with an export of the `wasi:cli/run` interface from the command component. This is the export that the Wasmtime CLI requires in order to execute the final component on the command line. 77 | 78 | ```wac 79 | // Provide a package name for the resulting composition 80 | package example:composition; 81 | 82 | // Instantiate the adder-impl component that implements the adder world. 83 | // We are giving this instance the local name `adder-instance`. 84 | let adder-instance = new docs:adder-impl { }; 85 | 86 | // Instantiate the calculator-impl component that implements the calculator world. 87 | // In the `new` expression, specify the source of the `add` import to be `adder-instance`'s `add` export. 88 | let calculator-instance = new docs:calculator-impl { add: adder-instance.add }; 89 | 90 | // Instantiate a command-impl component that implements the app world. 91 | // The command component might import other interfaces, such as WASI interfaces, but we want to leave 92 | // those as imports in the final component, so supply `...` to allow those other imports to remain unresolved. 93 | // The command's exports (in this case, `wasi:cli/run`) remain unaffected in the resulting instance. 94 | let command-instance = new docs:command-impl { calculate: calculator-instance.calculate,... }; 95 | 96 | // Export the `wasi:cli/run` interface from the command instance 97 | // This could also have been expressed using the postfix access expression `command-instance.run` 98 | export command-instance["wasi:cli/run@0.2.0"]; 99 | ``` 100 | 101 | Now, perform your composition by passing the WAC file to `wac compose`. 102 | 103 | ```sh 104 | wac compose composition.wac -o final.wasm 105 | ``` 106 | 107 | > Note, instead of moving all the components to a `deps/docs` directory, you can pass the paths to the components inline 108 | > ```sh 109 | > wac compose --dep docs:adder-impl=./adder/target/wasm32-wasip1/release/adder.wasm --dep docs:calculator-impl=./calculator/target/wasm32-wasip1/release/calculator.wasm --dep docs:command-impl=./command/target/wasm32-wasip1/release/command.wasm -o final.wasm composition.wac 110 | > ``` 111 | 112 | Run the component with Wasmtime: 113 | 114 | ```sh 115 | wasmtime run final.wasm 1 2 add 116 | 1 + 2 = 3 117 | ``` 118 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/adder/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "adder" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "wit-bindgen-rt", 10 | ] 11 | 12 | [[package]] 13 | name = "bitflags" 14 | version = "2.4.0" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" 17 | 18 | [[package]] 19 | name = "wit-bindgen-rt" 20 | version = "0.37.0" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "fc801b991c56492f87ab3086e786468f75c285a4d73017ab0ebc2fa1aed5d82c" 23 | dependencies = [ 24 | "bitflags", 25 | ] 26 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/adder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "adder" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | wit-bindgen-rt = { version = "0.37.0", features = ["bitflags"] } 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [package.metadata.component] 13 | package = "docs:adder" 14 | 15 | [package.metadata.component.dependencies] 16 | 17 | [package.metadata.component.target] 18 | path = "../wit/adder" 19 | world = "adder" 20 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/adder/src/bindings.rs: -------------------------------------------------------------------------------- 1 | // Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! 2 | // Options used: 3 | // * runtime_path: "wit_bindgen_rt" 4 | #[rustfmt::skip] 5 | #[allow(dead_code, clippy::all)] 6 | pub mod exports { 7 | pub mod docs { 8 | pub mod adder { 9 | #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] 10 | pub mod add { 11 | #[used] 12 | #[doc(hidden)] 13 | static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_custom_section_describing_imports; 14 | use super::super::super::super::_rt; 15 | #[doc(hidden)] 16 | #[allow(non_snake_case)] 17 | pub unsafe fn _export_add_cabi(arg0: i32, arg1: i32) -> i32 { 18 | #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); 19 | let result0 = T::add(arg0 as u32, arg1 as u32); 20 | _rt::as_i32(result0) 21 | } 22 | pub trait Guest { 23 | fn add(x: u32, y: u32) -> u32; 24 | } 25 | #[doc(hidden)] 26 | macro_rules! __export_docs_adder_add_0_1_0_cabi { 27 | ($ty:ident with_types_in $($path_to_types:tt)*) => { 28 | const _ : () = { #[unsafe (export_name = 29 | "docs:adder/add@0.1.0#add")] unsafe extern "C" fn export_add(arg0 30 | : i32, arg1 : i32,) -> i32 { unsafe { $($path_to_types)*:: 31 | _export_add_cabi::<$ty > (arg0, arg1) } } }; 32 | }; 33 | } 34 | #[doc(hidden)] 35 | pub(crate) use __export_docs_adder_add_0_1_0_cabi; 36 | } 37 | } 38 | } 39 | } 40 | #[rustfmt::skip] 41 | mod _rt { 42 | #![allow(dead_code, clippy::all)] 43 | #[cfg(target_arch = "wasm32")] 44 | pub fn run_ctors_once() { 45 | wit_bindgen_rt::run_ctors_once(); 46 | } 47 | pub fn as_i32(t: T) -> i32 { 48 | t.as_i32() 49 | } 50 | pub trait AsI32 { 51 | fn as_i32(self) -> i32; 52 | } 53 | impl<'a, T: Copy + AsI32> AsI32 for &'a T { 54 | fn as_i32(self) -> i32 { 55 | (*self).as_i32() 56 | } 57 | } 58 | impl AsI32 for i32 { 59 | #[inline] 60 | fn as_i32(self) -> i32 { 61 | self as i32 62 | } 63 | } 64 | impl AsI32 for u32 { 65 | #[inline] 66 | fn as_i32(self) -> i32 { 67 | self as i32 68 | } 69 | } 70 | impl AsI32 for i16 { 71 | #[inline] 72 | fn as_i32(self) -> i32 { 73 | self as i32 74 | } 75 | } 76 | impl AsI32 for u16 { 77 | #[inline] 78 | fn as_i32(self) -> i32 { 79 | self as i32 80 | } 81 | } 82 | impl AsI32 for i8 { 83 | #[inline] 84 | fn as_i32(self) -> i32 { 85 | self as i32 86 | } 87 | } 88 | impl AsI32 for u8 { 89 | #[inline] 90 | fn as_i32(self) -> i32 { 91 | self as i32 92 | } 93 | } 94 | impl AsI32 for char { 95 | #[inline] 96 | fn as_i32(self) -> i32 { 97 | self as i32 98 | } 99 | } 100 | impl AsI32 for usize { 101 | #[inline] 102 | fn as_i32(self) -> i32 { 103 | self as i32 104 | } 105 | } 106 | } 107 | /// Generates `#[unsafe(no_mangle)]` functions to export the specified type as 108 | /// the root implementation of all generated traits. 109 | /// 110 | /// For more information see the documentation of `wit_bindgen::generate!`. 111 | /// 112 | /// ```rust 113 | /// # macro_rules! export{ ($($t:tt)*) => (); } 114 | /// # trait Guest {} 115 | /// struct MyType; 116 | /// 117 | /// impl Guest for MyType { 118 | /// // ... 119 | /// } 120 | /// 121 | /// export!(MyType); 122 | /// ``` 123 | #[allow(unused_macros)] 124 | #[doc(hidden)] 125 | macro_rules! __export_adder_impl { 126 | ($ty:ident) => { 127 | self::export!($ty with_types_in self); 128 | }; 129 | ($ty:ident with_types_in $($path_to_types_root:tt)*) => { 130 | $($path_to_types_root)*:: 131 | exports::docs::adder::add::__export_docs_adder_add_0_1_0_cabi!($ty with_types_in 132 | $($path_to_types_root)*:: exports::docs::adder::add); 133 | }; 134 | } 135 | #[doc(inline)] 136 | pub(crate) use __export_adder_impl as export; 137 | #[cfg(target_arch = "wasm32")] 138 | #[unsafe( 139 | link_section = "component-type:wit-bindgen:0.41.0:docs:adder@0.1.0:adder:encoded world" 140 | )] 141 | #[doc(hidden)] 142 | #[allow(clippy::octal_escapes)] 143 | pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 203] = *b"\ 144 | \0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07P\x01A\x02\x01A\x02\x01\ 145 | B\x02\x01@\x02\x01xy\x01yy\0y\x04\0\x03add\x01\0\x04\0\x14docs:adder/add@0.1.0\x05\ 146 | \0\x04\0\x16docs:adder/adder@0.1.0\x04\0\x0b\x0b\x01\0\x05adder\x03\0\0\0G\x09pr\ 147 | oducers\x01\x0cprocessed-by\x02\x0dwit-component\x070.227.1\x10wit-bindgen-rust\x06\ 148 | 0.41.0"; 149 | #[inline(never)] 150 | #[doc(hidden)] 151 | pub fn __link_custom_section_describing_imports() { 152 | wit_bindgen_rt::maybe_link_cabi_realloc(); 153 | } 154 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/adder/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[allow(warnings)] 2 | mod bindings; 3 | 4 | // The comments that follow the `use` declaration below 5 | // correlate the rust module path segments with their 6 | // `world.wit` counterparts: 7 | use bindings::exports::docs::adder::add::Guest; 8 | // <- items bundled with `export` keyword 9 | // <- package namespace 10 | // <- package 11 | // <- interface name 12 | 13 | struct Component; 14 | 15 | impl Guest for Component { 16 | fn add(x: u32, y: u32) -> u32 { 17 | x + y 18 | } 19 | } 20 | 21 | bindings::export!(Component with_types_in bindings); 22 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/calculator/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bitflags" 7 | version = "2.5.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 10 | 11 | [[package]] 12 | name = "calculator" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "wit-bindgen-rt", 16 | ] 17 | 18 | [[package]] 19 | name = "wit-bindgen-rt" 20 | version = "0.24.0" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96" 23 | dependencies = [ 24 | "bitflags", 25 | ] 26 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/calculator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calculator" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | wit-bindgen-rt = { version = "0.24.0", features = ["bitflags"] } 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [package.metadata.component] 13 | package = "docs:calculator" 14 | 15 | [package.metadata.component.target.dependencies] 16 | "docs:adder" = { path = "../wit/adder" } # directory containing the WIT package 17 | 18 | [package.metadata.component.target] 19 | path = "../wit/calculator" 20 | world = "calculator" 21 | 22 | [package.metadata.component.dependencies] 23 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/calculator/src/bindings.rs: -------------------------------------------------------------------------------- 1 | // Generated by `wit-bindgen` 0.25.0. DO NOT EDIT! 2 | // Options used: 3 | #[allow(dead_code)] 4 | pub mod docs { 5 | #[allow(dead_code)] 6 | pub mod adder { 7 | #[allow(dead_code, clippy::all)] 8 | pub mod add { 9 | #[used] 10 | #[doc(hidden)] 11 | #[cfg(target_arch = "wasm32")] 12 | static __FORCE_SECTION_REF: fn() = 13 | super::super::super::__link_custom_section_describing_imports; 14 | use super::super::super::_rt; 15 | #[allow(unused_unsafe, clippy::all)] 16 | pub fn add(x: u32, y: u32) -> u32 { 17 | unsafe { 18 | #[cfg(target_arch = "wasm32")] 19 | #[link(wasm_import_module = "docs:adder/add@0.1.0")] 20 | extern "C" { 21 | #[link_name = "add"] 22 | fn wit_import(_: i32, _: i32) -> i32; 23 | } 24 | 25 | #[cfg(not(target_arch = "wasm32"))] 26 | fn wit_import(_: i32, _: i32) -> i32 { 27 | unreachable!() 28 | } 29 | let ret = wit_import(_rt::as_i32(&a), _rt::as_i32(&b)); 30 | ret as u32 31 | } 32 | } 33 | } 34 | } 35 | } 36 | #[allow(dead_code)] 37 | pub mod exports { 38 | #[allow(dead_code)] 39 | pub mod docs { 40 | #[allow(dead_code)] 41 | pub mod calculator { 42 | #[allow(dead_code, clippy::all)] 43 | pub mod calculate { 44 | #[used] 45 | #[doc(hidden)] 46 | #[cfg(target_arch = "wasm32")] 47 | static __FORCE_SECTION_REF: fn() = 48 | super::super::super::super::__link_custom_section_describing_imports; 49 | use super::super::super::super::_rt; 50 | #[repr(u8)] 51 | #[derive(Clone, Copy, Eq, PartialEq)] 52 | pub enum Op { 53 | Add, 54 | } 55 | impl ::core::fmt::Debug for Op { 56 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 57 | match self { 58 | Op::Add => f.debug_tuple("Op::Add").finish(), 59 | } 60 | } 61 | } 62 | 63 | impl Op { 64 | #[doc(hidden)] 65 | pub unsafe fn _lift(val: u8) -> Op { 66 | if !cfg!(debug_assertions) { 67 | return ::core::mem::transmute(val); 68 | } 69 | 70 | match val { 71 | 0 => Op::Add, 72 | 73 | _ => panic!("invalid enum discriminant"), 74 | } 75 | } 76 | } 77 | 78 | #[doc(hidden)] 79 | #[allow(non_snake_case)] 80 | pub unsafe fn _export_eval_expression_cabi( 81 | arg0: i32, 82 | arg1: i32, 83 | arg2: i32, 84 | ) -> i32 { 85 | #[cfg(target_arch = "wasm32")] 86 | _rt::run_ctors_once(); 87 | let result0 = 88 | T::eval_expression(Op::_lift(arg0 as u8), arg1 as u32, arg2 as u32); 89 | _rt::as_i32(result0) 90 | } 91 | pub trait Guest { 92 | fn eval_expression(op: Op, x: u32, y: u32) -> u32; 93 | } 94 | #[doc(hidden)] 95 | 96 | macro_rules! __export_docs_calculator_calculate_0_1_0_cabi{ 97 | ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = { 98 | 99 | #[export_name = "docs:calculator/calculate@0.1.0#eval-expression"] 100 | unsafe extern "C" fn export_eval_expression(arg0: i32,arg1: i32,arg2: i32,) -> i32 { 101 | $($path_to_types)*::_export_eval_expression_cabi::<$ty>(arg0, arg1, arg2) 102 | } 103 | };); 104 | } 105 | #[doc(hidden)] 106 | pub(crate) use __export_docs_calculator_calculate_0_1_0_cabi; 107 | } 108 | } 109 | } 110 | } 111 | mod _rt { 112 | 113 | pub fn as_i32(t: T) -> i32 { 114 | t.as_i32() 115 | } 116 | 117 | pub trait AsI32 { 118 | fn as_i32(self) -> i32; 119 | } 120 | 121 | impl<'a, T: Copy + AsI32> AsI32 for &'a T { 122 | fn as_i32(self) -> i32 { 123 | (*self).as_i32() 124 | } 125 | } 126 | 127 | impl AsI32 for i32 { 128 | #[inline] 129 | fn as_i32(self) -> i32 { 130 | self as i32 131 | } 132 | } 133 | 134 | impl AsI32 for u32 { 135 | #[inline] 136 | fn as_i32(self) -> i32 { 137 | self as i32 138 | } 139 | } 140 | 141 | impl AsI32 for i16 { 142 | #[inline] 143 | fn as_i32(self) -> i32 { 144 | self as i32 145 | } 146 | } 147 | 148 | impl AsI32 for u16 { 149 | #[inline] 150 | fn as_i32(self) -> i32 { 151 | self as i32 152 | } 153 | } 154 | 155 | impl AsI32 for i8 { 156 | #[inline] 157 | fn as_i32(self) -> i32 { 158 | self as i32 159 | } 160 | } 161 | 162 | impl AsI32 for u8 { 163 | #[inline] 164 | fn as_i32(self) -> i32 { 165 | self as i32 166 | } 167 | } 168 | 169 | impl AsI32 for char { 170 | #[inline] 171 | fn as_i32(self) -> i32 { 172 | self as i32 173 | } 174 | } 175 | 176 | impl AsI32 for usize { 177 | #[inline] 178 | fn as_i32(self) -> i32 { 179 | self as i32 180 | } 181 | } 182 | 183 | #[cfg(target_arch = "wasm32")] 184 | pub fn run_ctors_once() { 185 | wit_bindgen_rt::run_ctors_once(); 186 | } 187 | } 188 | 189 | /// Generates `#[no_mangle]` functions to export the specified type as the 190 | /// root implementation of all generated traits. 191 | /// 192 | /// For more information see the documentation of `wit_bindgen::generate!`. 193 | /// 194 | /// ```rust 195 | /// # macro_rules! export{ ($($t:tt)*) => (); } 196 | /// # trait Guest {} 197 | /// struct MyType; 198 | /// 199 | /// impl Guest for MyType { 200 | /// // ... 201 | /// } 202 | /// 203 | /// export!(MyType); 204 | /// ``` 205 | #[allow(unused_macros)] 206 | #[doc(hidden)] 207 | 208 | macro_rules! __export_calculator_impl { 209 | ($ty:ident) => (self::export!($ty with_types_in self);); 210 | ($ty:ident with_types_in $($path_to_types_root:tt)*) => ( 211 | $($path_to_types_root)*::exports::docs::calculator::calculate::__export_docs_calculator_calculate_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::docs::calculator::calculate); 212 | ) 213 | } 214 | #[doc(inline)] 215 | pub(crate) use __export_calculator_impl as export; 216 | 217 | #[cfg(target_arch = "wasm32")] 218 | #[link_section = "component-type:wit-bindgen:0.25.0:calculator:encoded world"] 219 | #[doc(hidden)] 220 | pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 308] = *b"\ 221 | \0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xb3\x01\x01A\x02\x01\ 222 | A\x04\x01B\x02\x01@\x02\x01ay\x01by\0y\x04\0\x03add\x01\0\x03\x01\x14docs:adder/\ 223 | add@0.1.0\x05\0\x01B\x04\x01m\x01\x03add\x04\0\x02op\x03\0\0\x01@\x03\x02op\x01\x01\ 224 | xy\x01yy\0y\x04\0\x0feval-expression\x01\x02\x04\x01\x1fdocs:calculator/calculat\ 225 | e@0.1.0\x05\x01\x04\x01\x20docs:calculator/calculator@0.1.0\x04\0\x0b\x10\x01\0\x0a\ 226 | calculator\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070\ 227 | .208.1\x10wit-bindgen-rust\x060.25.0"; 228 | 229 | #[inline(never)] 230 | #[doc(hidden)] 231 | #[cfg(target_arch = "wasm32")] 232 | pub fn __link_custom_section_describing_imports() { 233 | wit_bindgen_rt::maybe_link_cabi_realloc(); 234 | } 235 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/calculator/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[allow(warnings)] 2 | mod bindings; 3 | 4 | use bindings::exports::docs::calculator::calculate::{Guest, Op}; 5 | 6 | // Bring the imported add function into scope 7 | use bindings::docs::adder::add::add; 8 | 9 | struct Component; 10 | 11 | impl Guest for Component { 12 | fn eval_expression(op: Op, x: u32, y: u32) -> u32 { 13 | match op { 14 | Op::Add => add(x, y), 15 | } 16 | } 17 | } 18 | 19 | bindings::export!(Component with_types_in bindings); 20 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/command/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.11" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "utf8parse", 17 | ] 18 | 19 | [[package]] 20 | name = "anstyle" 21 | version = "1.0.4" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" 24 | 25 | [[package]] 26 | name = "anstyle-parse" 27 | version = "0.2.3" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" 30 | dependencies = [ 31 | "utf8parse", 32 | ] 33 | 34 | [[package]] 35 | name = "anstyle-query" 36 | version = "1.0.2" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" 39 | dependencies = [ 40 | "windows-sys", 41 | ] 42 | 43 | [[package]] 44 | name = "anstyle-wincon" 45 | version = "3.0.2" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 48 | dependencies = [ 49 | "anstyle", 50 | "windows-sys", 51 | ] 52 | 53 | [[package]] 54 | name = "anyhow" 55 | version = "1.0.79" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" 58 | 59 | [[package]] 60 | name = "bitflags" 61 | version = "2.4.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" 64 | 65 | [[package]] 66 | name = "clap" 67 | version = "4.4.18" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" 70 | dependencies = [ 71 | "clap_builder", 72 | "clap_derive", 73 | ] 74 | 75 | [[package]] 76 | name = "clap_builder" 77 | version = "4.4.18" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" 80 | dependencies = [ 81 | "anstream", 82 | "anstyle", 83 | "clap_lex", 84 | "strsim", 85 | ] 86 | 87 | [[package]] 88 | name = "clap_derive" 89 | version = "4.4.7" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" 92 | dependencies = [ 93 | "heck", 94 | "proc-macro2", 95 | "quote", 96 | "syn", 97 | ] 98 | 99 | [[package]] 100 | name = "clap_lex" 101 | version = "0.6.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" 104 | 105 | [[package]] 106 | name = "colorchoice" 107 | version = "1.0.0" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 110 | 111 | [[package]] 112 | name = "command" 113 | version = "0.1.0" 114 | dependencies = [ 115 | "anyhow", 116 | "clap", 117 | "wit-bindgen-rt", 118 | ] 119 | 120 | [[package]] 121 | name = "heck" 122 | version = "0.4.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 125 | 126 | [[package]] 127 | name = "proc-macro2" 128 | version = "1.0.78" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 131 | dependencies = [ 132 | "unicode-ident", 133 | ] 134 | 135 | [[package]] 136 | name = "quote" 137 | version = "1.0.36" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 140 | dependencies = [ 141 | "proc-macro2", 142 | ] 143 | 144 | [[package]] 145 | name = "strsim" 146 | version = "0.10.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 149 | 150 | [[package]] 151 | name = "syn" 152 | version = "2.0.48" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 155 | dependencies = [ 156 | "proc-macro2", 157 | "quote", 158 | "unicode-ident", 159 | ] 160 | 161 | [[package]] 162 | name = "unicode-ident" 163 | version = "1.0.11" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 166 | 167 | [[package]] 168 | name = "utf8parse" 169 | version = "0.2.1" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 172 | 173 | [[package]] 174 | name = "windows-sys" 175 | version = "0.52.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 178 | dependencies = [ 179 | "windows-targets", 180 | ] 181 | 182 | [[package]] 183 | name = "windows-targets" 184 | version = "0.52.0" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 187 | dependencies = [ 188 | "windows_aarch64_gnullvm", 189 | "windows_aarch64_msvc", 190 | "windows_i686_gnu", 191 | "windows_i686_msvc", 192 | "windows_x86_64_gnu", 193 | "windows_x86_64_gnullvm", 194 | "windows_x86_64_msvc", 195 | ] 196 | 197 | [[package]] 198 | name = "windows_aarch64_gnullvm" 199 | version = "0.52.0" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" 202 | 203 | [[package]] 204 | name = "windows_aarch64_msvc" 205 | version = "0.52.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" 208 | 209 | [[package]] 210 | name = "windows_i686_gnu" 211 | version = "0.52.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" 214 | 215 | [[package]] 216 | name = "windows_i686_msvc" 217 | version = "0.52.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" 220 | 221 | [[package]] 222 | name = "windows_x86_64_gnu" 223 | version = "0.52.0" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" 226 | 227 | [[package]] 228 | name = "windows_x86_64_gnullvm" 229 | version = "0.52.0" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" 232 | 233 | [[package]] 234 | name = "windows_x86_64_msvc" 235 | version = "0.52.5" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 238 | 239 | [[package]] 240 | name = "wit-bindgen-rt" 241 | version = "0.24.0" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96" 244 | dependencies = [ 245 | "bitflags", 246 | ] 247 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/command/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "command" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1" 8 | wit-bindgen-rt = { version = "0.24.0", features = ["bitflags"] } 9 | clap = { version = "4.3.19", features = ["derive"] } 10 | 11 | [package.metadata.component.target] 12 | path = "../wit/calculator" 13 | world = "app" 14 | 15 | [package.metadata.component.target.dependencies] 16 | "docs:adder" = { path = "../wit/adder" } 17 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/command/src/bindings.rs: -------------------------------------------------------------------------------- 1 | // Generated by `wit-bindgen` 0.25.0. DO NOT EDIT! 2 | // Options used: 3 | #[allow(dead_code)] 4 | pub mod docs { 5 | #[allow(dead_code)] 6 | pub mod calculator { 7 | #[allow(dead_code, clippy::all)] 8 | pub mod calculate { 9 | #[used] 10 | #[doc(hidden)] 11 | #[cfg(target_arch = "wasm32")] 12 | static __FORCE_SECTION_REF: fn() = 13 | super::super::super::__link_custom_section_describing_imports; 14 | use super::super::super::_rt; 15 | #[repr(u8)] 16 | #[derive(Clone, Copy, Eq, PartialEq)] 17 | pub enum Op { 18 | Add, 19 | } 20 | impl ::core::fmt::Debug for Op { 21 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 22 | match self { 23 | Op::Add => f.debug_tuple("Op::Add").finish(), 24 | } 25 | } 26 | } 27 | 28 | impl Op { 29 | #[doc(hidden)] 30 | pub unsafe fn _lift(val: u8) -> Op { 31 | if !cfg!(debug_assertions) { 32 | return ::core::mem::transmute(val); 33 | } 34 | 35 | match val { 36 | 0 => Op::Add, 37 | 38 | _ => panic!("invalid enum discriminant"), 39 | } 40 | } 41 | } 42 | 43 | #[allow(unused_unsafe, clippy::all)] 44 | pub fn eval_expression(op: Op, x: u32, y: u32) -> u32 { 45 | unsafe { 46 | #[cfg(target_arch = "wasm32")] 47 | #[link(wasm_import_module = "docs:calculator/calculate@0.1.0")] 48 | extern "C" { 49 | #[link_name = "eval-expression"] 50 | fn wit_import(_: i32, _: i32, _: i32) -> i32; 51 | } 52 | 53 | #[cfg(not(target_arch = "wasm32"))] 54 | fn wit_import(_: i32, _: i32, _: i32) -> i32 { 55 | unreachable!() 56 | } 57 | let ret = wit_import(op.clone() as i32, _rt::as_i32(&x), _rt::as_i32(&y)); 58 | ret as u32 59 | } 60 | } 61 | } 62 | } 63 | } 64 | mod _rt { 65 | 66 | pub fn as_i32(t: T) -> i32 { 67 | t.as_i32() 68 | } 69 | 70 | pub trait AsI32 { 71 | fn as_i32(self) -> i32; 72 | } 73 | 74 | impl<'a, T: Copy + AsI32> AsI32 for &'a T { 75 | fn as_i32(self) -> i32 { 76 | (*self).as_i32() 77 | } 78 | } 79 | 80 | impl AsI32 for i32 { 81 | #[inline] 82 | fn as_i32(self) -> i32 { 83 | self as i32 84 | } 85 | } 86 | 87 | impl AsI32 for u32 { 88 | #[inline] 89 | fn as_i32(self) -> i32 { 90 | self as i32 91 | } 92 | } 93 | 94 | impl AsI32 for i16 { 95 | #[inline] 96 | fn as_i32(self) -> i32 { 97 | self as i32 98 | } 99 | } 100 | 101 | impl AsI32 for u16 { 102 | #[inline] 103 | fn as_i32(self) -> i32 { 104 | self as i32 105 | } 106 | } 107 | 108 | impl AsI32 for i8 { 109 | #[inline] 110 | fn as_i32(self) -> i32 { 111 | self as i32 112 | } 113 | } 114 | 115 | impl AsI32 for u8 { 116 | #[inline] 117 | fn as_i32(self) -> i32 { 118 | self as i32 119 | } 120 | } 121 | 122 | impl AsI32 for char { 123 | #[inline] 124 | fn as_i32(self) -> i32 { 125 | self as i32 126 | } 127 | } 128 | 129 | impl AsI32 for usize { 130 | #[inline] 131 | fn as_i32(self) -> i32 { 132 | self as i32 133 | } 134 | } 135 | } 136 | 137 | #[cfg(target_arch = "wasm32")] 138 | #[link_section = "component-type:wit-bindgen:0.25.0:app:encoded world"] 139 | #[doc(hidden)] 140 | pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 246] = *b"\ 141 | \0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07}\x01A\x02\x01A\x02\x01\ 142 | B\x04\x01m\x01\x03add\x04\0\x02op\x03\0\0\x01@\x03\x02op\x01\x01xy\x01yy\0y\x04\0\ 143 | \x0feval-expression\x01\x02\x03\x01\x1fdocs:calculator/calculate@0.1.0\x05\0\x04\ 144 | \x01\x19docs:calculator/app@0.1.0\x04\0\x0b\x09\x01\0\x03app\x03\0\0\0G\x09produ\ 145 | cers\x01\x0cprocessed-by\x02\x0dwit-component\x070.208.1\x10wit-bindgen-rust\x06\ 146 | 0.25.0"; 147 | 148 | #[inline(never)] 149 | #[doc(hidden)] 150 | #[cfg(target_arch = "wasm32")] 151 | pub fn __link_custom_section_describing_imports() { 152 | wit_bindgen_rt::maybe_link_cabi_realloc(); 153 | } 154 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/command/src/main.rs: -------------------------------------------------------------------------------- 1 | mod bindings; 2 | 3 | use clap::Parser; 4 | use std::fmt; 5 | 6 | use bindings::docs::calculator::{calculate, calculate::Op}; 7 | 8 | fn parse_operator(op: &str) -> anyhow::Result { 9 | match op { 10 | "add" => Ok(Op::Add), 11 | _ => anyhow::bail!("Unknown operation: {}", op), 12 | } 13 | } 14 | 15 | impl fmt::Display for Op { 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 | match self { 18 | Op::Add => write!(f, "+"), 19 | } 20 | } 21 | } 22 | 23 | /// A CLI for executing mathematical expressions 24 | /// using WebAssembly 25 | #[derive(Parser)] 26 | #[clap(name = "calculator", version = env!("CARGO_PKG_VERSION"))] 27 | struct Command { 28 | /// The first operand 29 | x: u32, 30 | /// The second operand 31 | y: u32, 32 | /// Expression operator 33 | #[clap(value_parser = parse_operator)] 34 | op: Op, 35 | } 36 | 37 | impl Command { 38 | fn run(self) { 39 | let res = calculate::eval_expression(self.op, self.x, self.y); 40 | println!("{} {} {} = {res}", self.x, self.op, self.y); 41 | } 42 | } 43 | 44 | fn main() { 45 | Command::parse().run() 46 | } 47 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/composition.wac: -------------------------------------------------------------------------------- 1 | // Provide a package name for the resulting composition 2 | package example:composition; 3 | 4 | // Instantiate the adder-impl component that implements the adder world. 5 | // Bind this instance's exports to the local name `adder-instance`. 6 | let adder-instance = new docs:adder-impl { }; 7 | 8 | // Instantiate the calculator-impl component that implements the calculator world. 9 | // In the `new` expression, connect it's `add` import to the `adder-instance`'s `add` export. 10 | let calculator-instance = new docs:calculator-impl { add: adder-instance.add }; 11 | 12 | // Instantiate a command-impl component that implements the app world. 13 | // The command component might import other interfaces, such as WASI interfaces, but we want to leave 14 | // those as imports in the final component, so supply `...` to allow those other imports to remain unresolved. 15 | // The command's exports (in this case, `wasi:cli/run`) remain unaffected in the resulting instance. 16 | let command-instance = new docs:command-impl { calculate: calculator-instance.calculate,... }; 17 | 18 | // Export the `wasi:cli/run` interface from the command instance 19 | // This could also have been expressed using the postfix access expression `command-instance.run` 20 | export command-instance["wasi:cli/run@0.2.0"]; -------------------------------------------------------------------------------- /component-model/examples/tutorial/jco/README.md: -------------------------------------------------------------------------------- 1 | # Wasm Component Calculator in JavaScript 2 | 3 | This is a `node` CLI and browser based example implementation of running a component that exports the `calculate` interface from a JavaScript application. It uses [`jco`](https://bytecodealliance.github.io/jco/) to generate JavaScript bindings and shows how the same component can be executed in the browser or locally with Node. For another example of using `jco` with components in multiple environments, see the [`jco` example](https://github.com/bytecodealliance/jco/blob/main/docs/src/example.md). 4 | 5 | ```sh 6 | # Wasm referenced here was generated by cargo component. 7 | # See top-level README for commands to generate it. 8 | # 9 | # We want to *omit* wasm requiring Wasi, 10 | # thus use `composed.wasm`, not the `command.wasm`. 11 | 12 | # Transpile to generate bindings for JS: 13 | jco transpile ../composed.wasm -o bindings 14 | 15 | # Serve required files (index.html & jco generated files minimally): 16 | npx live-server . 17 | 18 | # Run CLI example: 19 | node cli-calc.js 20 | ``` 21 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/jco/cli-calc.js: -------------------------------------------------------------------------------- 1 | // See the README for details on *generation* of the required import 2 | import { calculate } from "./bindings/composed.js"; 3 | 4 | console.log("Answer (to life) = " + calculate.evalExpression("add", 21, 21)); 5 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/jco/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Wasm Component Demo

5 | 6 | + 7 | 8 | 9 | 10 |

11 | 12 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/jco/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /component-model/examples/tutorial/wit/adder/world.wit: -------------------------------------------------------------------------------- 1 | package docs:adder@0.1.0; 2 | 3 | interface add { 4 | add: func(x: u32, y: u32) -> u32; 5 | } 6 | 7 | world adder { 8 | export add; 9 | } -------------------------------------------------------------------------------- /component-model/examples/tutorial/wit/calculator/world.wit: -------------------------------------------------------------------------------- 1 | package docs:calculator@0.1.0; 2 | 3 | interface calculate { 4 | enum op { 5 | add, 6 | } 7 | eval-expression: func(op: op, x: u32, y: u32) -> u32; 8 | } 9 | 10 | world calculator { 11 | export calculate; 12 | import docs:adder/add@0.1.0; 13 | } 14 | 15 | world app { 16 | import calculate; 17 | } 18 | -------------------------------------------------------------------------------- /component-model/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | 2 | # Summary 3 | 4 | - [Introduction](./introduction.md) 5 | 6 | # Understanding Component Model 7 | 8 | - [Why the Component Model?](./design/why-component-model.md) 9 | - [Component Model Concepts](./design/component-model-concepts.md) 10 | - [Components](./design/components.md) 11 | - [Interfaces](./design/interfaces.md) 12 | - [Worlds](./design/worlds.md) 13 | - [Packages](./design/packages.md) 14 | - [WIT Reference](./design/wit.md) 15 | 16 | # Using Component Model 17 | 18 | - [Language Support for Components](./language-support.md) 19 | - [C/C++](./language-support/c.md) 20 | - [C#](./language-support/csharp.md) 21 | - [Go](./language-support/go.md) 22 | - [JavaScript](./language-support/javascript.md) 23 | - [Python](./language-support/python.md) 24 | - [Rust](./language-support/rust.md) 25 | - [Creating and Consuming Components](./creating-and-consuming.md) 26 | - [Authoring Components](./creating-and-consuming/authoring.md) 27 | - [Composing Components](./creating-and-consuming/composing.md) 28 | - [Running Components](./creating-and-consuming/running.md) 29 | - [Distributing and Fetching Components and WIT](./creating-and-consuming/distributing.md) 30 | - [Tutorial](./tutorial.md) 31 | 32 | # Runtime Support 33 | 34 | - [Wasmtime](./runtimes/wasmtime.md) 35 | - [jco](./runtimes/jco.md) 36 | 37 | # Advanced Topics 38 | 39 | - [Canonical ABI](./advanced/canonical-abi.md) 40 | 41 | # Reference 42 | 43 | - [Useful Links](./reference/useful-links.md) 44 | -------------------------------------------------------------------------------- /component-model/src/advanced/canonical-abi.md: -------------------------------------------------------------------------------- 1 | # Canonical ABI 2 | 3 | An ABI is an **application binary interface** - an agreement on how to pass data around in a binary format. ABIs are specifically concerned with data layout at the bits-and-bytes level. For example, an ABI might define how integers are represented (big-endian or little-endian?), how strings are represented (pointer to null-terminated character sequence or length-prefixed? UTF-8 or UTF-16 encoded?), and how composite types are represented (the offsets of each field from the start of the structure). 4 | 5 | The component model defines a **canonical ABI** - an ABI to which all [components](../design/components.md) adhere. This guarantees that components can talk to each other without confusion, even if they are built in different languages. Internally, a C component might represent strings in a quite different way from a Rust component, but the canonical ABI provides a format for them to pass strings across the boundary between them. 6 | 7 | > For a more formal definition of what the Canonical ABI is, take a look at the [Canonical ABI explainer](https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md). 8 | -------------------------------------------------------------------------------- /component-model/src/creating-and-consuming.md: -------------------------------------------------------------------------------- 1 | # Creating and Consuming Components 2 | 3 | The component model defines how components interface to each other and to hosts. This section describes how to work with components - from authoring them in custom code or by composing existing components, through to using them in applications and distributing them via registries. 4 | -------------------------------------------------------------------------------- /component-model/src/creating-and-consuming/authoring.md: -------------------------------------------------------------------------------- 1 | # Authoring Components 2 | 3 | You can write WebAssembly core modules in a wide variety of languages, and the set of languages that can directly create components is growing. See the [Language Support](../language-support.md) section for information on building components directly from source code. 4 | 5 | If your preferred language supports WebAssembly but not components, you can still create components using the [`wasm-tools component`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) tool. (A future version of this page will cover this in more detail.) -------------------------------------------------------------------------------- /component-model/src/creating-and-consuming/composing.md: -------------------------------------------------------------------------------- 1 | # Composing Components 2 | 3 | Because the WebAssembly component model packages code in a portable binary format, and provides machine-readable interfaces in [WIT](../design/wit.md) with a standardised ABI (Application Binary Interface), it enables applications and components to work together, no matter what languages they were originally written in. In the same way that, for example, a Rust package (crate) can be compiled together with other Rust code to create a higher-level library or an application, a Wasm component can be linked with other components. 4 | 5 | > Component model interoperation is more convenient and expressive than language-specific foreign function interfaces. A typical C FFI involves language-specific types, so it is not possible to link between arbitrary languages without at least some C-language wrapping or conversion. The component model, by contrast, provides a common way of expressing interfaces, and a standard binary representation of those interfaces. So if an import and an export have the same shape, they fit together directly. 6 | 7 | ## What is composition? 8 | 9 | When you compose components, you wire up the imports of one "primary" component to the exports of one or more other "dependency" components, creating a new component. The new component, like the original components, is a `.wasm` file, and its interface is defined as: 10 | 11 | * The new component _exports_ the same exports as the primary component 12 | * The new component _does not export_ the exports of the dependencies 13 | * The new component _imports_ all the imports of the dependency components 14 | * The new component _imports_ any imports of the primary component imports that the dependencies didn't satisfy 15 | * If several components import the same interface, the new component imports that interface - it doesn't "remember" that the import was declared in several different places 16 | 17 | For example, consider two components with the following worlds: 18 | 19 | ```wit 20 | // component `validator` 21 | package docs:validator@0.1.0; 22 | 23 | interface validator { 24 | validate-text: func(text: string) -> string; 25 | } 26 | 27 | world validator { 28 | export validator; 29 | import docs:regex/match@0.1.0; 30 | } 31 | ``` 32 | 33 | ```wit 34 | // component 'regex' 35 | package docs:regex@0.1.0; 36 | 37 | interface match { 38 | first-match: func(regex: string, text: string) -> string; 39 | } 40 | 41 | world regex { 42 | export match; 43 | } 44 | ``` 45 | 46 | If we compose `validator` with `regex`, `validator`'s import of `docs:regex/match@0.1.0` is wired up to `regex`'s export of `match`. The net result is that the composed component exports `docs:validator/validator@0.1.0` and has no imports. The composed component does _not_ export `docs:regex/match@0.1.0` - that has become an internal implementation detail of the composed component. 47 | 48 | Component composition tools are in their early stages right now. Here are some tips to avoid or diagnose errors: 49 | 50 | * Composition happens at the level of interfaces. If the initial component directly imports functions, then composition will fail. If composition reports an error such as "component `path/to/component` has a non-instance import named ``" then check that all imports and exports are defined by interfaces. 51 | * Composition is asymmetrical. It is not just "gluing components together" - it takes a primary component which has imports, and satisfies its imports using dependency components. For example, composing an implementation of `validator` with an implementation of `regex` makes sense because `validator` has a dependency that `regex` can satisfy; doing it the other way round doesn't work, because `regex` doesn't have any dependencies, let alone ones that `validator` can satisfy. 52 | * Composition cares about interface versions, and current tools are inconsistent about when they infer or inject versions. For example, if a Rust component exports `test:mypackage`, `cargo component build` will decorate this with the crate version, e.g. `test:mypackage@0.1.0`. If another Rust component _imports_ an interface from `test:mypackage`, that won't match `test:mypackage@0.1.0`. You can use [`wasm-tools component wit`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) to view the imports and exports embedded in the `.wasm` files and check whether they match up. 53 | 54 | ## Composing components with WAC 55 | 56 | You can use the [WAC](https://github.com/bytecodealliance/wac) CLI to compose components at the command line. 57 | 58 | To perform quick and simple compositions, use the `wac plug` command. `wac plug` satisfies the import of a "socket" component by plugging a "plug" component's export into the socket. For example, a component that implements the [`validator` world above](#what-is-composition) needs to satisfy it's `match` import. It is a socket. While a component that implements the `regex` world, exports the `match` interface, and can be used as a plug. `wac plug` can plug a regex component's export into the validator component's import, creating a resultant composition: 59 | 60 | ```sh 61 | wac plug validator-component.wasm --plug regex-component.wasm -o composed.wasm 62 | ``` 63 | 64 | A component can also be composed with two components it depends on. 65 | 66 | ```sh 67 | wac plug path/to/component.wasm --plug path/to/dep1.wasm --plug path/to/dep2.wasm -o composed.wasm 68 | ``` 69 | 70 | Here `component.wasm` is the component that imports interfaces from `dep1.wasm` and `dep2.wasm`, which export them. The composed component, with those dependencies satisfied and tucked away inside it, is saved to `composed.wasm`. 71 | 72 | The `plug` syntax doesn't cover transitive dependencies. If, for example, `dep1.wasm` has unsatisfied imports that you want to satisfy from `dep3.wasm`, you'd need to be deliberate about the order of your composition. You could compose `dep1.wasm` with `dep3.wasm` first, then refer to that composed component instead of `dep1.wasm`. However, this doesn't scale to lots of transitive dependencies, which is why the WAC language was created. 73 | 74 | ### Advanced composition with the WAC language 75 | 76 | `wac plug` is a convenience to achieve a common pattern in component compositions like above. However, composition can be arbitrarily complicated. In cases where `wac plug` is not sufficient, the [WAC language](https://github.com/bytecodealliance/wac/blob/main/LANGUAGE.md) can give us the ability to create arbitrarily complex compositions. 77 | 78 | In a WAC file, you use the WAC language to describe a composition. For example, the following is a WAC file that could be used to create that validator component from [earlier](#what-is-composition). 79 | 80 | ``` 81 | //composition.wac 82 | // Provide a package name for the resulting composition 83 | package docs:composition; 84 | 85 | // Instantiate the regex-impl component that implements the `regex` world. Bind this instance's exports to the local name `regex`. 86 | let regex = new docs:regex-impl { }; 87 | 88 | // Instantiate the validator-impl component which implements the `validator` world and imports the match interface from the regex component. 89 | let validator = new docs:validator-impl { match: regex.match, ... }; 90 | 91 | // Export all remaining exports of the validator instance 92 | export validator...; 93 | ``` 94 | 95 | Then, `wac compose` can be used to compose the components, passing in the paths to the components. Alternatively, you can place the components in a `deps` directory with an expected structure, and in the near future, you will be able to pull in components from registries. See the [`wac` documentation](https://github.com/bytecodealliance/wac) for more details. 96 | 97 | ```sh 98 | wac compose --dep docs:regex-impl=regex-component.wasm --dep docs:validator-impl=validator-component.wasm -o composed.wasm composition.wac 99 | ``` 100 | 101 | For an in depth description about how to use the wac tool, you can check out the [wac language index](https://github.com/bytecodealliance/wac/blob/main/LANGUAGE.md) and [examples](https://github.com/bytecodealliance/wac/tree/main/examples). 102 | 103 | ## Composing components with a visual interface 104 | 105 | You can compose components visually using the builder app at [wasmbuilder.app](https://wasmbuilder.app/). 106 | 107 | 1. Use the Add Component Button to upload the `.wasm` component files you want to compose. The components appear in the sidebar. 108 | 109 | 2. Drag the components onto the canvas. You'll see imports listed on the left of each component, and exports on the right. 110 | 111 | 3. Click the box in the top left to choose the 'primary' component, that is, the one whose exports will be preserved. (The clickable area is quite small - wait for the cursor to change from a hand to a pointer.) 112 | 113 | 4. To fulfil one of the primary component's imports with a dependency's export, drag from the "I" icon next to the export to the "I" item next to the import. (Again, the clickable area is quite small - wait for the cursor to change from a hand to a cross.) 114 | 115 | 5. When you have connected all the imports and exports that you want, click the Download Component button to download the composed component as a `.wasm` file. 116 | -------------------------------------------------------------------------------- /component-model/src/creating-and-consuming/distributing.md: -------------------------------------------------------------------------------- 1 | # Distributing and Fetching Components and WIT 2 | 3 | Modern applications rely extensively on third-party packages - so extensively that distributing packages is almost an industry in itself. Traditionally, these have been specific to a language. For example, JavaScript developers are used to using packages from NPM, and Rust developers use `crates.io`. Some runtimes support binary distribution and linking, enabling limited cross-language interop; for example, Maven packages can be written in any language that targets the Java runtime. Services like this are variously referred to as "package managers" or "registries." 4 | 5 | Publishing and distribution are not defined by the core component model, but form important part of the component ecosystem. For example, if you're writing JavaScript, and want to pull in a highly optimised machine learning algorithm written in C and compiled to Wasm, you can pull it from a registry, ideally just as easily as you would add a NPM package from the NPM registry. 6 | 7 | You can get involved with improving the packaging and hosting of Wasm components by joining the [Bytecode Alliance Packaging Special Interest Group (SIG)](https://github.com/bytecodealliance/governance/blob/main/SIGs/sig-packaging/proposal.md). 8 | 9 | ## The `wkg` Registry Tool 10 | 11 | The [`wasm-pkg-tools` project](https://github.com/bytecodealliance/wasm-pkg-tools) enables fetching and publishing Wasm components to OCI registries. It contains a `wkg` CLI tool that eases distributing and fetching components and WIT packages. The usual way of using `wkg` is to address packages by their package name, i.e. `example:adder@1.0.0`. When using `wkg` this way, you don't need to know about the physical location of the package, as the `wkg` configuration handles that for you. If you need to, though, you can also use `wkg` to work with OCI artifacts directly, addressing them by OCI references when using the `wkg oci` subcommand. 12 | 13 | `wkg` contains several subcommand: 14 | 15 | - `wkg oci` - pushes/pulls Wasm artifacts to/from any OCI registry 16 | - `wkg publish` - publish components or WIT packages by package name 17 | - `wkg get` - pulls components or WIT packages by package name 18 | - `wkg wit` - commands for interacting with WIT files and dependencies 19 | - `wkg config` - interact with the `wkg` configuration 20 | 21 | The following sections detail a subset of actions that can be performed with `wkg`. 22 | 23 | ## `wkg` Configuration Files 24 | 25 | When you use most `wkg` commands (`wkg oci` being the exception), you don't interact with physical locations, only with package names. The `wkg` configuration file is used to map package naming to physical location. It provides the ability to configure: 26 | 27 | - The default registry for packages in a given namespace. For example, the location for `wasi` packages such as `wasi:clocks` or `wasi:http`. 28 | - Registry overrides for specific packages, or packages not stored in the same place as the rest of their namespace. For example, if `wasi:key-value` were stored in a different registry from other `wasi` packages. 29 | - The default registry for all packages not listed in one of the previous sections 30 | 31 | The configuration file also includes credentials for private registries, or for pushing to registries where you have permission, and other configuration options. See the [`wkg` docs for more configuration options](https://github.com/bytecodealliance/wasm-pkg-tools?tab=readme-ov-file#configuration). 32 | 33 | For example, to fetch WASI packages, such as `wasi:clocks` and `wasi:http`, you can add a line under the `namespace_registries` section for the `wasi` namespace. Specifically, the example below configures `wkg` to fetch WASI packages from the [WebAssembly OCI GitHub Container Registry](https://github.com/orgs/WebAssembly/packages), where the latest interfaces are published upon WASI releases. To edit your `wkg` config file, run `wkg config --edit`. 34 | 35 | > Remember, all package names consist of the a namespace field followed by the package field. The package name `wasi:clocks` has a namespace of `wasi` and package field of `clocks`. In this way, the following configuration ensures that `wkg` will know to route fetches and publishes of any `wasi:` to the configured location. 36 | 37 | ```toml 38 | # $XDG_CONFIG_HOME/wasm-pkg/config.toml 39 | default_registry = "ghcr.io" 40 | 41 | [namespace_registries] 42 | # Tell wkg that packages with the `wasi` namespace are in an OCI registry under ghcr.io/webassembly 43 | wasi = { registry = "wasi", metadata = { preferredProtocol = "oci", "oci" = {registry = "ghcr.io", namespacePrefix = "webassembly/" } } } 44 | ``` 45 | 46 | As a more generic example, The following configuration, instructs `wkg` to use [ttl.sh](https://ttl.sh/) OCI registry for all packages with the `docs` namespace. 47 | 48 | ```toml 49 | # $XDG_CONFIG_HOME/wasm-pkg/config.toml 50 | default_registry = "ghcr.io" 51 | 52 | [namespace_registries] 53 | # Instruct wkg to use the OCI protocol to fetch packages with the `foo` namespace from ttl.sh/wasm-components 54 | docs = { registry = "docs-registry", metadata = { preferredProtocol = "oci", "oci" = {registry = "ttl.sh", namespacePrefix = "wasm-components/" } } } 55 | ``` 56 | 57 | > Note: the registry name can be referenced in the `package_registry_overrides` section of the `wkg` config to provide overrides for specific packages of a namespace. 58 | 59 | ## Distributing WIT and Components by Package Name with `wkg publish` 60 | 61 | Once you've [configured `wkg`](#wkg-configuration-files) to know where to publish packages to, you can use the `wkg publish` command to publish *components* or *interfaces* to be consumed by others. 62 | 63 | Imagine you have defined the following `adder` world in WIT: 64 | 65 | ```wit 66 | package docs:adder@0.1.0; 67 | 68 | interface add { 69 | add: func(a: u32, b: u32) -> u32; 70 | } 71 | 72 | world adder { 73 | export add; 74 | } 75 | ``` 76 | 77 | You can publish this *WIT* using `wkg` by wrapping it up as a Wasm component. Yes, you heard that right! We are packaging WIT as Wasm. 78 | 79 | ```sh 80 | # Package the contents of add WIT directory as Wasm 81 | wkg wit build --wit-dir tutorial/wit/adder 82 | # Publish the produced component 83 | wkg publish docs:adder@0.1.0.wasm 84 | ``` 85 | 86 | If you had configured `wkg` as described in the [`wkg` configuration section](#wkg-configuration-files), this would publish the component to `ttl.sh/wasm-components/docs/adder:0.1.0`. This WIT can then be fetched using `wkg get`, specifying the format `wit`: 87 | 88 | ```sh 89 | wkg get --format wit docs:adder@0.1.0 --output adder.wit 90 | ``` 91 | 92 | Instead of publishing the WIT interface, you could publish the built component by running: 93 | 94 | ```sh 95 | wkg publish adder.wasm --package docs:adder@0.1.0 96 | ``` 97 | 98 | You can then fetch the component by running: 99 | 100 | ```sh 101 | wkg get docs:adder@0.1.0 --output adder.wasm 102 | ``` 103 | 104 | ## More Generic Operations with `wkg oci` 105 | 106 | The `wkg oci` subcommand enables pushing/pulling Wasm artifacts to/from any OCI registry. Unlike `wkg publish` and `wkg get`, providing the WIT package is not required. 107 | 108 | To push a component to an OCI registry, use `wkg oci pull`. The example below pushes a component to a GitHub Container Registry. 109 | 110 | ```sh 111 | wkg oci push ghcr.io/user/component:0.1.0 component.wasm 112 | ``` 113 | 114 | To pull a component, run: 115 | 116 | ```sh 117 | wkg oci pull ghcr.io/user/component:0.1.0 -o component.wasm 118 | ``` 119 | 120 | ## Fetching WIT Package Dependencies using `wkg` 121 | 122 | Sometimes fetching a single package is not sufficient because it depends on other packages. For example, the following world describes a simple Wasm service which requires `wasi:http/proxy`: 123 | 124 | ```wit 125 | package foo:wasi-http-service; 126 | 127 | world target-world { 128 | include wasi:http/proxy@0.2.3; 129 | } 130 | ``` 131 | 132 | You may be tempted to simply get the `wasi:http` package with `wkg get --format wit wasi:http@0.2.3 -o wit/deps/http/`. However, `wasi:http` depends on other WASI packages such as `wasi:clocks` and `wasi:io`. To make sure to fetch a package and all its dependencies, use `wkg wit fetch`, which will read the package containing the world(s) you have defined in the given wit directory (`wit` by default). It will then fetch the 133 | dependencies and write them to the `deps` directory along with a lock file. 134 | 135 | After placing the above file in `./wit`, run the following to fetch the dependencies: 136 | 137 | ```sh 138 | wkg wit fetch 139 | ``` 140 | 141 | The `./wit` directory will be populated as follows: 142 | ```sh 143 | wit 144 | ├── deps 145 | │ ├── wasi-cli-0.2.3 146 | │ │ └── package.wit 147 | │ ├── wasi-clocks-0.2.3 148 | │ │ └── package.wit 149 | │ ├── wasi-http-0.2.3 150 | │ │ └── package.wit 151 | │ ├── wasi-io-0.2.3 152 | │ │ └── package.wit 153 | │ └── wasi-random-0.2.3 154 | │ └── package.wit 155 | └── world.wit 156 | ``` 157 | 158 | Now, you can use the language toolchain of your choice to generate bindings and create your component. -------------------------------------------------------------------------------- /component-model/src/creating-and-consuming/running.md: -------------------------------------------------------------------------------- 1 | # Running Components 2 | 3 | You can "run" a component by calling one of its exports. In some cases, this requires a custom host. For "command" components, though, you can use the `wasmtime` command line. This can be a convenient tool for testing components and exploring the component model. Other runtimes are also available - see the "Runtimes" section of the sidebar for more info. 4 | 5 | > A "command" component is one that exports the `wasi:cli/run` interface, and imports only interfaces listed in the [`wasi:cli/command` world](https://github.com/WebAssembly/wasi-cli/blob/main/wit/command.wit). 6 | 7 | You must use a recent version of `wasmtime` ([`v14.0.0` or greater](https://github.com/bytecodealliance/wasmtime/releases)), as earlier releases of the `wasmtime` command line do not include component model support. 8 | 9 | To run your component, run: 10 | 11 | ```sh 12 | wasmtime run 13 | ``` 14 | 15 | ## Running components with custom exports 16 | 17 | If you're writing a library-style component - that is, one that exports a custom API - then you can run it in `wasmtime` by writing a "command" component that imports and invokes your custom API. By [composing](./composing.md) the command and the library, you can exercise the library in `wasmtime`. 18 | 19 | 1. Write your library component. The component's world (`.wit` file) must export an interface and/or one or more functions through which a consumer can call it. See the [language support guide](../language-support.md) for how to implement an export. 20 | 21 | 2. Build your library component to a `.wasm` file. 22 | 23 | 3. Write your command component. The component's world (`.wit` file) must import the interface or functions exported from the library. Write the command to call the library's API. See the [language support guide](../language-support.md) for how to call an imported interface. 24 | 25 | 4. Build your command component to a `.wasm` file. You will not be able to run this in `wasmtime` yet, as its imports are not yet satisfied. 26 | 27 | 5. Compose your command component with your library component by running `wac plug --plug -o main.wasm`. 28 | 29 | 6. Run the composed component using `wasmtime run main.wasm` 30 | 31 | See [Composing Components](./composing.md) for more details. 32 | -------------------------------------------------------------------------------- /component-model/src/design/component-model-concepts.md: -------------------------------------------------------------------------------- 1 | ## Component Model Concepts 2 | 3 | This section introduces the core concepts and [rationale](./why-component-model.md) of the component model. 4 | 5 | * A [WebAssembly Component](./components.md) is the next evolution of core WebAssembly binaries. 6 | * WebAssembly components are *nestable* -- they may contain one or more core modules and/or sub-components composed together. 7 | * The Component Model extends core WebAssembly by introducing higher level types and interface-driven development 8 | * [WebAssembly Interface Types (WIT)][wit] is the [IDL (Interface Definition Language)][wiki-idl] used to formally define functionality for WebAssembly modules. 9 | * With WIT, WebAssembly components gain the ability to conform an language-agnostic and encode that support, so any WebAssembly component binary can be interrogated *and* executed. 10 | * An [Interface](./interfaces.md) describes the types and functions used for a specific, focused bit of functionality. 11 | * A [World](./worlds.md) assembles interfaces to express what features a component offers, and what features it depends on. 12 | * A [Package](./packages.md) is a set of WIT files containing a related set of interfaces and worlds. 13 | * The Component Model introduces the idea of a "platform" to core WebAssembly -- enabling the structured, standardized use of "host" functionality for WebAssembly "guest"s. 14 | * The WebAssembly System Interface (WASI) defines in WIT a family of interfaces for common system-level functions. 15 | * WASI defines common execution environments such as the command line (`wasi:cli`) or a HTTP server (`wasi:http`). 16 | * The Component Model introducs makes core WebAssembly composable -- components that provide functionality and those that use them can be composed together into *one* resulting component 17 | 18 | > [!NOTE] 19 | > The Component Model is stewarded by the Bytecode Alliance and designed [in the open][cm-repo]. 20 | > 21 | > See the [`WebAssembly/component-model`][cm-repo] repository for [Goals][goals],[use cases][use-cases], and [high level design choices][design-choices]. 22 | 23 | [cm-repo]: https://github.com/WebAssembly/component-model 24 | [wiki-idl]: https://en.wikipedia.org/wiki/Web_IDL 25 | [goals]: https://github.com/WebAssembly/component-model/blob/main/design/high-level/Goals.md 26 | [use-cases]: https://github.com/WebAssembly/component-model/blob/main/design/high-level/UseCases.md 27 | [design-choices]: https://github.com/WebAssembly/component-model/blob/main/design/high-level/Choices.md 28 | [wit]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md 29 | 30 | [!NOTE]: # 31 | -------------------------------------------------------------------------------- /component-model/src/design/components.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | * Logically, components are containers for modules - or other components - which express their [interfaces](./interfaces.md) and dependencies via [WIT](./wit.md). 4 | * Conceptually, components are self-describing units of code that interact only through interfaces instead of shared memory. 5 | * Physically, a **component** is a specially-formatted WebAssembly file. Internally, the component could include multiple traditional ("core") WebAssembly modules, and sub-components, composed via their imports and exports. 6 | 7 | The external interface of a component - its imports and exports - corresponds to a [world](./worlds.md). The component, however, internally defines how that world is implemented. 8 | 9 | > For a more formal definition of what a component is, take a look at the [Component Model specification](https://github.com/WebAssembly/component-model). 10 | -------------------------------------------------------------------------------- /component-model/src/design/interfaces.md: -------------------------------------------------------------------------------- 1 | # Interfaces 2 | 3 | An **interface** describes a single-focus, composable contract, through which components can interact with each other and with hosts. Interfaces describe the types and functions used to carry out that interaction. For example: 4 | 5 | * A "receive HTTP requests" interface might have only a single "handle request" function, but contain types representing incoming requests, outgoing responses, HTTP methods and headers, and so on. 6 | * A "wall clock" interface might have two functions, one to get the current time and one to get the granularity of the timer. It would also include a type to represent an instant in time. 7 | 8 | Interfaces are defined using [the WIT language](./wit.md). 9 | 10 | > For a more formal definition of what an interface is, take a look at the [WIT specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md). 11 | -------------------------------------------------------------------------------- /component-model/src/design/packages.md: -------------------------------------------------------------------------------- 1 | # WIT Packages 2 | 3 | A **WIT package** is a set of one or more [WIT (Wasm Interface Type)](./wit.md) files containing a related set of interfaces and worlds. WIT is an IDL (interface definition language) for the Component Model. Packages provide a way for worlds and interfaces to refer to each other, and thus for an ecosystem of components to share common definitions. 4 | 5 | A WIT package is not a [world](./worlds.md). It's a way of grouping related interfaces and worlds together for ease of discovery and reference, more like a namespace. 6 | 7 | * The WebAssembly System Interface (WASI) defines a number of packages, including one named `wasi:clocks`. Our HTTP proxy world could import the `wall-clock` interface from the `wasi:clocks` package, rather than having to define a custom clock interface. 8 | 9 | > For a more formal definition of what a WIT package is, take a look at the [WIT specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md). 10 | -------------------------------------------------------------------------------- /component-model/src/design/why-component-model.md: -------------------------------------------------------------------------------- 1 | # Why the Component Model? 2 | 3 | If you've tried out WebAssembly, you'll be familiar with the concept of a _module_. Roughly speaking, a module corresponds to a single `.wasm` file, with functions, memory, imports and exports, and so on. These "core" modules can run in the browser, or via a separate runtime such as Wasmtime or WAMR. A module is defined by the [WebAssembly Core Specification](https://webassembly.github.io/spec/core/), and if you compile a program written in Rust, C, Go or whatever to WebAssembly, then a core module is what you'll get. 4 | 5 | Core modules are, however, limited in how they expose their functionality to the outside world to functions that take and return only a small number of core WebAssembly types (essentially only integers and floating-point numbers). Richer types, such as strings, lists, records (a.k.a. structs), etc. have to be represented in terms of integers and floating point numbers, for example by the use of pointers and offsets. Those representations are often times not interchangeable across languages. For example, a string in C might be represented entirely differently from a string in Rust or in JavaScript. 6 | 7 | For Wasm modules to interoperate, therefore, there needs to be an agreed-upon way for exposing those richer types across module boundaries. 8 | 9 | In the component model, these type definitions are written in a language called [WIT (Wasm Interface Type)](./wit.md), and the way they translate into bits and bytes is called the [Canonical ABI (Application Binary Interface)](./../advanced/canonical-abi.md). A Wasm [component](./components.md) is thus a wrapper around a core module that specifies its imports and exports using such [Interfaces](./interfaces.md). 10 | 11 | The agreement of an interface adds a new dimension to Wasm portability. Not only are components portable across architectures and operating systems, but they are now portable across languages. A Go component can communicate directly and safely with a C or Rust component. It need not even know which language another component was written in - it needs only the component interface, expressed in WIT. Additionally, components can be linked into larger graphs, with one component's exports satisfying another's imports. 12 | 13 | Combined with Wasm's strong sandboxing, this opens the door to yet further benefits. By expressing higher-level semantics than integers and floats, it becomes possible to statically analyse and reason about a component's behaviour - to enforce and guarantee properties just by looking at the surface of the component. The relationships within a graph of components can be analysed, for example to verify that a component containing business logic has no access to a component containing personally identifiable information. 14 | 15 | Moreover, a component interacts with a runtime or other components _only_ by calling its imports and having its exports called. Specifically, unlike core modules, a component may not export Wasm memory, and thus it cannot indirectly communicate to others by writing to its memory and having others read from that memory. This not only reinforces sandboxing, but enables interoperation between languages that make different assumptions about memory - for example, allowing a component that relies on Wasm GC (garbage collected) memory to collaborate with one that uses conventional linear memory. 16 | 17 | Now that you have a better idea about how the component model can help you, take a look at [how to build components](../language-support.md) in your favorite language! 18 | 19 | > For more background on why the component model was created, take a look at the specification's [goals](https://github.com/WebAssembly/component-model/blob/main/design/high-level/Goals.md), [use cases](https://github.com/WebAssembly/component-model/blob/main/design/high-level/UseCases.md) and [design choices](https://github.com/WebAssembly/component-model/blob/main/design/high-level/Choices.md). 20 | -------------------------------------------------------------------------------- /component-model/src/design/wit.md: -------------------------------------------------------------------------------- 1 | # An Overview of WIT 2 | 3 | The WIT (Wasm Interface Type) language is used to define Component Model [interfaces](#interfaces) and [worlds](#worlds). WIT isn't a general-purpose coding language and doesn't define behaviour; it defines only _contracts_ between components. This topic provides an overview of key elements of the WIT language. The official WIT specification and history can be found in the [`WebAssembly/component-model` repository](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md). 4 | 5 | - [An Overview of WIT](#an-overview-of-wit) 6 | - [Structure of a WIT file](#structure-of-a-wit-file) 7 | - [Comments](#comments) 8 | - [Documentation](#documentation) 9 | - [Identifiers](#identifiers) 10 | - [Built-in types](#built-in-types) 11 | - [Primitive types](#primitive-types) 12 | - [Lists](#lists) 13 | - [Options](#options) 14 | - [Results](#results) 15 | - [Tuples](#tuples) 16 | - [User-defined types](#user-defined-types) 17 | - [Records](#records) 18 | - [Variants](#variants) 19 | - [Enums](#enums) 20 | - [Resources](#resources) 21 | - [Flags](#flags) 22 | - [Type aliases](#type-aliases) 23 | - [Functions](#functions) 24 | - [Interfaces](#interfaces) 25 | - [Using definitions from elsewhere](#using-definitions-from-elsewhere) 26 | - [Worlds](#worlds) 27 | - [Interfaces from other packages](#interfaces-from-other-packages) 28 | - [Inline interfaces](#inline-interfaces) 29 | - [Including other worlds](#including-other-worlds) 30 | - [Packages](#packages) 31 | 32 | ## Structure of a WIT file 33 | 34 | A WIT file contains one or more **interfaces** or **worlds**. An interface or world can define **types** and/or **functions**. 35 | 36 | > Types and functions can't be defined outside of interfaces or worlds. 37 | 38 | A file may optionally start with a **package** declaration. 39 | 40 | ## Comments 41 | 42 | WIT comment syntax is similar to the one used by the C++ family of languages: 43 | 44 | * Everything from `//` to end of line is a comment. 45 | * Any text enclosed in `/*` ... `*/` is a comment. 46 | * Unlike the C++ family, block comments _can_ be nested, e.g. `/* blah /* rabbit */ rhubarb */`. 47 | 48 | ## Documentation 49 | 50 | WIT defines special comment formats for documentation: 51 | 52 | * Everything from `///` to end of line is documentation for the following item. 53 | * Any text enclosed in `/**` ... `*/` is documentation for the following item. 54 | 55 | For example: 56 | 57 | ```wit 58 | /// Prints "hello". 59 | print-hello: func(); 60 | 61 | /** 62 | Prints "hello". 63 | */ 64 | print-hello: func(); 65 | ``` 66 | 67 | ## Identifiers 68 | 69 | WIT identifiers have a slightly different set of rules from what you might be familiar with from, say, C, Rust, or Java. These rules apply to all names - types, functions, interfaces, and worlds. (Package identifiers are a little more complex and will be covered in the [Packages section](#packages).) 70 | 71 | * Identifiers are restricted to ASCII `kebab-case` - sequences of words, separated by single hyphens. 72 | * Double hyphens (`--`) are not allowed. 73 | * Hyphens aren't allowed at the beginning or end of the sequence, only between words. 74 | * An identifier may be preceded by a single `%` sign. 75 | * This is _required_ if the identifier would otherwise be a WIT keyword. For example, `interface` is **not** a legal identifier, but `%interface` is legal. 76 | * Each word in the sequence must begin with an ASCII letter, and may contain only ASCII letters and digits. 77 | * A word cannot begin with a digit. 78 | * A word cannot contain a non-ASCII Unicode character. 79 | * A word cannot contain punctuation, underscores, etc. 80 | * Each word must be either all `lowercase` or all `UPPERCASE`. 81 | * Different words in the identifier may have different cases. For example, `WIT-demo` is allowed. 82 | * An identifier cannot be a WIT keyword such as `interface` (unless preceded by a `%` sign). 83 | 84 | ## Built-in types 85 | 86 | The types in this section are defined by the WIT language itself. 87 | 88 | ### Primitive types 89 | 90 | WIT defines the following primitive types: 91 | 92 | | Identifier | Description | 93 | |----------------------------|-------------| 94 | | `bool` | Boolean value `true` or `false`. | 95 | | `s8`, `s16`, `s32`, `s64` | Signed integers of the appropriate width. For example, `s32` is a signed 32-bit integer. | 96 | | `u8`, `u16`, `u32`, `u64` | Unsigned integers of the appropriate width. For example, `u32` is an unsigned 32-bit integer. | 97 | | `f32`, `f64` | Floating-point numbers of the appropriate width. For example, `f64` is a 64-bit (double precision) floating-point number. See the note on `NaN`s below. | 98 | | `char` | Unicode character. (Specifically, a [Unicode scalar value](https://unicode.org/glossary/#unicode_scalar_value).) | 99 | | `string` | A Unicode string - that is, a finite sequence of characters. | 100 | 101 | > The `f32` and `f64` types support the usual set of IEEE 754 single and double-precision values, except that they logically only have a single `nan` value. The exact bit-level representation of an IEEE 754 `NaN` is not guaranteed to be preserved when values pass through WIT interfaces as the singular WIT `nan` value. 102 | 103 | ### Lists 104 | 105 | `list` for any type `T` denotes an ordered sequence of values of type `T`. `T` can be any type, built-in or user-defined: 106 | 107 | ```wit 108 | list // byte buffer 109 | list // a list of customers 110 | ``` 111 | 112 | This is similar to Rust `Vec`, or Java `List`. 113 | 114 | ### Options 115 | 116 | `option` for any type `T` may contain a value of type `T`, or may contain no value. `T` can be any type, built-in or user-defined. For example, a lookup function might return an option, allowing for the possibility that the lookup key wasn't found: 117 | 118 | ```wit 119 | option 120 | ``` 121 | 122 | This is similar to Rust `Option`, C++ `std::optional`, or Haskell `Maybe`. 123 | 124 | > This is a special case of a [variant](#variants) type. WIT defines it so that there is a common way of expressing it, so that you don't need to create a variant type for every value type, and to enable it to be mapped idiomatically into languages with option types. 125 | 126 | ### Results 127 | 128 | `result` for any types `T` and `E `may contain a value of type `T` _or_ a value of type `E` (but not both). This is typically used for "value or error" situations; for example, a HTTP request function might return a result, with the success case (the `T` type) representing a HTTP response, and the error case (the `E` type) representing the various kinds of error that might occur: 129 | 130 | ```wit 131 | result 132 | ``` 133 | 134 | This is similar to Rust `Result`, or Haskell `Either`. 135 | 136 | > This is a special case of a [variant](#variants) type. WIT defines it so that there is a common way of expressing it, so that you don't need to create a variant type for every combination of value and error types, and to enable it to be mapped idiomatically into languages with result or "either" types. 137 | 138 | Sometimes there is no data associated with one or both of the cases. For example, a `print` function could return an error code if it fails, but has nothing to return if it succeeds. In this case, you can omit the corresponding type as follows: 139 | 140 | ```wit 141 | result // no data associated with the error case 142 | result<_, u32> // no data associated with the success case 143 | result // no data associated with either case 144 | ``` 145 | 146 | ### Tuples 147 | 148 | A `tuple` type is an ordered _fixed length_ sequence of values of specified types. It is similar to a [_record_](#records), except that the fields are identified by their order instead of by names. 149 | 150 | ```wit 151 | tuple // An integer and a string 152 | tuple // An integer, then a string, then an integer 153 | ``` 154 | 155 | This is similar to tuples in Rust or OCaml. 156 | 157 | ## User-defined types 158 | 159 | You can define your own types within an `interface` or `world`. WIT offers several ways of defining new types. 160 | 161 | ### Records 162 | 163 | A `record` type declares a set of named fields, each of the form `name: type`, separated by commas. A record instance contains a value for every field. Field types can be built-in or user-defined. The syntax is as follows: 164 | 165 | ```wit 166 | record customer { 167 | id: u64, 168 | name: string, 169 | picture: option>, 170 | account-manager: employee, 171 | } 172 | ``` 173 | 174 | Records are similar to C or Rust `struct`s. 175 | 176 | > User-defined records can't be generic (that is, parameterised by type). Only built-in types can be generic. 177 | 178 | ### Variants 179 | 180 | A `variant` type declares one or more cases. Each case has a name and, optionally, a type of data associated with that case. A variant instance contains exactly one case. Cases are separated by commas. The syntax is as follows: 181 | 182 | ```wit 183 | variant allowed-destinations { 184 | none, 185 | any, 186 | restricted(list
), 187 | } 188 | ``` 189 | 190 | Variants are similar to Rust `enum`s or OCaml discriminated unions. The closest C equivalent is a tagged union, but WIT both takes care of the "tag" (the case) and enforces the correct data shape for each tag. 191 | 192 | > User-defined variants can't be generic (that is, parameterised by type). Only built-in types can be generic. 193 | 194 | ### Enums 195 | 196 | An `enum` type is a variant type where none of the cases have associated data: 197 | 198 | ```wit 199 | enum color { 200 | hot-pink, 201 | lime-green, 202 | navy-blue, 203 | } 204 | ``` 205 | 206 | This can provide a simpler representation in languages without discriminated unions. For example, a WIT `enum` can translate directly to a C++ `enum`. 207 | 208 | ### Resources 209 | 210 | Resources are handles to some entity that lives outside of the component. They 211 | describe things that can't or shouldn't be copied by value; instead, their 212 | ownership or reference can be passed between two components via a handle. Unlike 213 | other WIT types which are simply plain data, resources only expose behavior 214 | through methods. Resources can be thought of as _objects that implement_ an 215 | interface. 216 | 217 | For example, we could model a blob (binary large object) as a resource. The 218 | following WIT defines the `blob` resource type, which contains a constructor, 219 | two methods, and a static function: 220 | 221 | ```wit 222 | resource blob { 223 | constructor(init: list); 224 | write: func(bytes: list); 225 | read: func(n: u32) -> list; 226 | merge: static func(lhs: blob, rhs: blob) -> blob; 227 | } 228 | ``` 229 | 230 | As shown in the `blob` example, a resource can contain: 231 | 232 | - _methods_: functions that implicitly take a `self` (often called `this` in many languages) 233 | parameter that is a handle 234 | - _static functions_: functions which do not have an implicit `self` parameter 235 | but are meant to be nested in the scope of the resource type 236 | - at most one _constructor_: a function that is syntactic sugar for a function 237 | returning a handle of the containing resource type 238 | 239 | Methods always desugar to a borrowed `self` parameter whereas constructors 240 | always desugar to an owned return value. For example, the `blob` resource 241 | [above](#resources) could be approximated as: 242 | 243 | ```wit 244 | resource blob; 245 | blob-constructor: func(bytes: list) -> blob; 246 | blob-write: func(self: borrow, bytes: list); 247 | blob-read: func(self: borrow, n: u32) -> list; 248 | blob-merge: static func(lhs: blob, rhs: blob) -> blob; 249 | ``` 250 | 251 | When a `resource` type name is wrapped with `borrow<...>`, it stands for a 252 | "borrowed" resource. A borrowed resource represents a temporary loan of a resource from the 253 | caller to the callee for the duration of the call. In contrast, when the owner 254 | of an owned resource drops that resource, the resource is destroyed. 255 | 256 | > More precisely, these are borrowed or owned `handles` of the resource. Learn more about `handles` in the [upstream component model specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#handles). 257 | 258 | ### Flags 259 | 260 | A `flags` type is a set of named booleans. In an instance of the type, each flag will be either `true` or `false`. 261 | 262 | ```wit 263 | flags allowed-methods { 264 | get, 265 | post, 266 | put, 267 | delete, 268 | } 269 | ``` 270 | 271 | > A `flags` type is logically equivalent to a record type where each field is of type `bool`, but it is represented more efficiently (as a bitfield) at the binary level. 272 | 273 | ### Type aliases 274 | 275 | You can define a new named type using `type ... = ...`. This can be useful for giving shorter or more meaningful names to types: 276 | 277 | ```wit 278 | type buffer = list; 279 | type http-result = result; 280 | ``` 281 | 282 | ## Functions 283 | 284 | A function is defined by a name and a function type. Like in record fields, the name is separated from the type by a colon: 285 | 286 | ```wit 287 | do-nothing: func(); 288 | ``` 289 | 290 | The function type is the word `func`, followed by a parenthesised, comma-separated list of parameters (names and types). If the function returns a value, this is expressed as an arrow symbol (`->`) followed by the return type: 291 | 292 | ```wit 293 | // This function does not return a value 294 | print: func(message: string); 295 | 296 | // These functions return values 297 | add: func(a: u64, b: u64) -> u64; 298 | lookup: func(store: kv-store, key: string) -> option; 299 | ``` 300 | 301 | A function can have multiple return values. In this case the return values must be named, similar to the parameter list. All return values must be populated (in the same way as tuple or record fields). 302 | 303 | ```wit 304 | get-customers-paged: func(cont: continuation-token) -> (customers: list, cont: continuation-token); 305 | ``` 306 | 307 | A function can be declared as part of an [interface](#interfaces), or can be declared as an import or export in a [world](#worlds). 308 | 309 | ## Interfaces 310 | 311 | An interface is a named set of types and functions, enclosed in braces and introduced with the `interface` keyword: 312 | 313 | ```wit 314 | interface canvas { 315 | type canvas-id = u64; 316 | 317 | record point { 318 | x: u32, 319 | y: u32, 320 | } 321 | 322 | draw-line: func(canvas: canvas-id, from: point, to: point); 323 | } 324 | ``` 325 | 326 | Notice that items in an interface are _not_ comma-separated. 327 | 328 | ### Using definitions from elsewhere 329 | 330 | An interface can reuse types declared in another interface via a `use` directive. The `use` directive must give the interface where the types are declared, then a dot, then a braced list of the types to be reused. The interface can then refer to the types named in the `use`. 331 | 332 | ```wit 333 | interface types { 334 | type dimension = u32; 335 | record point { 336 | x: dimension, 337 | y: dimension, 338 | } 339 | } 340 | 341 | interface canvas { 342 | use types.{dimension, point}; 343 | type canvas-id = u64; 344 | draw-line: func(canvas: canvas-id, from: point, to: point, thickness: dimension); 345 | } 346 | ``` 347 | 348 | > Even if you are only using one type, it must still be enclosed in braces. For example, `use types.{dimension}` is legal but `use types.dimension` is not. 349 | 350 | This works across files as long as the files are in the same package (effectively, in the same directory). For information about using definitions from other packages, see [the specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#interfaces-worlds-and-use). 351 | 352 | ## Worlds 353 | 354 | A world describes a set of imports and exports, enclosed in braces and introduced with the `world` keyword. Roughly, a world describes the contract of a component. Exports are provided by the component, and define what consumers of the component may call; imports are things the component may call. The imports and exports may be interfaces or individual functions. 355 | 356 | ```wit 357 | interface printer { 358 | print: func(text: string); 359 | } 360 | 361 | interface error-reporter { 362 | report-error: func(error-message: string); 363 | } 364 | 365 | world multi-function-device { 366 | // The component implements the `printer` interface 367 | export printer; 368 | 369 | // The component implements the `scan` function 370 | export scan: func() -> list; 371 | 372 | // The component needs to be supplied with an `error-reporter` 373 | import error-reporter; 374 | } 375 | ``` 376 | 377 | ### Interfaces from other packages 378 | 379 | You can import and export interfaces defined in other packages. This can be done using `package/name` syntax: 380 | 381 | ```wit 382 | world http-proxy { 383 | export wasi:http/incoming-handler; 384 | import wasi:http/outgoing-handler; 385 | } 386 | ``` 387 | 388 | > As this example shows, import and export apply at the interface level, not the package level. You can import one interface defined in a package, while exporting another interface defined in the same package. Packages group definitions; they don't represent behaviour. 389 | 390 | WIT does not define how packages are resolved - different tools may resolve them in different ways. 391 | 392 | ### Inline interfaces 393 | 394 | Interfaces can be declared inline in a world: 395 | 396 | ```wit 397 | world toy { 398 | export example: interface { 399 | do-nothing: func(); 400 | } 401 | } 402 | ``` 403 | 404 | ### Including other worlds 405 | 406 | You can `include` another world. This causes your world to export all that world's exports, and import all that world's imports. 407 | 408 | ```wit 409 | world glow-in-the-dark-multi-function-device { 410 | // The component provides all the same exports, and depends on 411 | // all the same imports, as a `multi-function-device`... 412 | include multi-function-device; 413 | 414 | // ...but also exports a function to make it glow in the dark 415 | export glow: func(brightness: u8); 416 | } 417 | ``` 418 | 419 | As with `use` directives, you can `include` worlds from other packages. 420 | 421 | ## Packages 422 | 423 | A package is a set of interfaces and worlds, potentially defined across multiple files. To declare a package, use the `package` directive to specify the package ID. This must include a namespace and name, separated by a colon, and may optionally include a semver-compliant version: 424 | 425 | ```wit 426 | package documentation:example; 427 | package documentation:example@1.0.1; 428 | ``` 429 | 430 | If a package spans multiple files, only one file needs to contain a package declaration (but if multiple files contain declarations then they must all be the same). All files must have the `.wit` extension and must be in the same directory. For example, the following `documentation:http` package is spread across four files: 431 | 432 | ```wit 433 | // types.wit 434 | interface types { 435 | record request { /* ... */ } 436 | record response { /* ... */ } 437 | } 438 | 439 | // incoming.wit 440 | interface incoming-handler { 441 | use types.{request, response}; 442 | // ... 443 | } 444 | 445 | // outgoing.wit 446 | interface outgoing-handler { 447 | use types.{request, response}; 448 | // ... 449 | } 450 | 451 | // http.wit 452 | package documentation:http@1.0.0; 453 | 454 | world proxy { 455 | export incoming-handler; 456 | import outgoing-handler; 457 | } 458 | ``` 459 | 460 | > For a more formal definition of the WIT language, take a look at the [WIT specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md). 461 | -------------------------------------------------------------------------------- /component-model/src/design/worlds.md: -------------------------------------------------------------------------------- 1 | # WIT Worlds 2 | 3 | A **WIT world** is a higher-level contract that describes a component's capabilities and needs. 4 | 5 | On one hand, a world describes the shape of a component - it says which interfaces the component exposes for other code to call (its exports), and which interfaces the component depends on (its imports). A world only defines the surface of a component, not the internal behaviour. If you're an application or library developer creating a component, you'll specify the world your component targets. This world describes the functionality your component exposes and declares the functionality your component depends on in order to be able to run. Your component may target a custom world definition you have created with a unique set of imports and exports tailored just for your use case, or it may target an existing world definition that someone else has already specified. 6 | 7 | On the other hand though, a world defines a _hosting environment_ for components (i.e., an environment in which a component can be instantiated and its functionality can be invoked). An environment supports a world by providing implementations for all of the imports and by optionally invoking one or more of the exports. 8 | 9 | For example, WASI (the WebAssembly System Interface) defines a "command line" world which imports interfaces that command line programs typically expect to have available to them such as file I/O, random number generation, clocks and so on. This world has a single export for running the command line tool. Components targeting this world must provide an implementation for this single export, and they may optionally call any of the imports. On the other hand, environments supporting this world must provide implementations for all of the imports and may invoke the single export. 10 | 11 | A world is composed of interfaces, but each interface is _directional_ - it indicates whether the interface is available for outside code to call (an "export"), or whether outside code must fulfill the interface for the component to call (an "import"). These interfaces strictly bound the component. A component cannot interact with anything outside itself except by having its exports called, or by it calling its imports. This provides very strong sandboxing; for example, if a component does not have an import for a secret store, then it _cannot_ access that secret store, even if the store is running in the same process. 12 | 13 | For a component to run, its imports must be fulfilled, by a host or by other components. Connecting up some or all of a component's imports to other components' matching exports is called _composition_. 14 | 15 | ## Example Worlds 16 | 17 | * A (trivial) "HTTP proxy" world would export a "handle HTTP requests" interface, and import a "send HTTP requests" interface. A host, or another component, would call the exported "handle" interface, passing an HTTP request; the component would forward it on via the imported "send" interface. To be a _useful_ proxy, the component may also need to import interfaces such as I/O and clock time - without those imports the component could not perform, for example, on-disk caching. 18 | * A "regex parser" world would export a "parse regex" function, and would import nothing. This declares not only that the component implementing this world can parse regular expressions, but also that it calls no other APIs. A user of such a parser could know, without looking at the implementation, that is does not access the file system, or send the user's regexes to a network service. 19 | 20 | > For a more formal definition of what a WIT world is, take a look at the [WIT world specification](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-worlds). 21 | -------------------------------------------------------------------------------- /component-model/src/introduction.md: -------------------------------------------------------------------------------- 1 | # Home 2 | 3 | The WebAssembly Component Model is a broad-reaching architecture for building interoperable WebAssembly libraries, applications, and environments. 4 | 5 | | Understanding components | Building components | Using components | 6 | |--------------------------|----------------------|-------------------| 7 | | [Why Components?] | [C/C++] | [Composing] | 8 | | [Components] | [C#] | [Running] | 9 | | [Interfaces] | [Go] | [Distributing] | 10 | | [Worlds] | [JavaScript] | | 11 | | | [Python] | | 12 | | | [Rust] | | 13 | 14 | [Why Components?]: ./design/why-component-model.md 15 | [Components]: ./design/components.md 16 | [Interfaces]: ./design/interfaces.md 17 | [Worlds]: ./design/worlds.md 18 | 19 | [C/C++]: ./language-support/c.md 20 | [C#]: ./language-support/csharp.md 21 | [Go]: ./language-support/go.md 22 | [JavaScript]: ./language-support/javascript.md 23 | [Python]: ./language-support/python.md 24 | [Rust]: ./language-support/rust.md 25 | 26 | [Composing]: ./creating-and-consuming/composing.md 27 | [Running]: ./creating-and-consuming/running.md 28 | [Distributing]: ./creating-and-consuming/distributing.md 29 | 30 | > [!NOTE] 31 | >This documentation is aimed at _users_ of the component model: developers of libraries and applications. 32 | > 33 | > _Compiler and Wasm runtime developers_ can take a look at the [Component Model specification](https://github.com/WebAssembly/component-model) to 34 | > see how to add support for the component model to their project. 35 | 36 | ## Status 37 | 38 | [WASI 0.2.0 was released](https://github.com/WebAssembly/WASI/pull/577) Jan 25, 2024, providing a stable release of WASI and the component model. 39 | This [is a stable set of WIT definitions](https://github.com/WebAssembly/WASI/tree/main/wasip2) that components can target. WASI proposals will 40 | continue to evolve and new ones will be introduced; however, users of the component model can now pin to any stable release >= `v0.2.0`. See [WASI.dev](https://wasi.dev) to stay up to date on the latest releases. 41 | 42 | ## Contributing 43 | 44 | If you find a mistake, omission, ambiguity, or other problem, please let us know via [GitHub issues](https://github.com/bytecodealliance/component-docs/issues). 45 | 46 | If you'd like to contribute content to the guide, please see the [contribution guide](https://github.com/bytecodealliance/component-docs/blob/main/CONTRIBUTING.md) for information on how to contribute. 47 | 48 | [!NOTE]: # 49 | -------------------------------------------------------------------------------- /component-model/src/language-support.md: -------------------------------------------------------------------------------- 1 | # Wasm Language Support 2 | 3 | WebAssembly can be targeted by the majority of top programming 4 | languages; however, the level of 5 | support varies. This document details the subset of languages that target WASI and support 6 | components. 7 | 8 | > This is a living document, so if you are aware of advancements in a toolchain, please do 9 | not hesitate to [contribute documentation](https://github.com/bytecodealliance/component-docs/blob/main/CONTRIBUTING.md). You can find more information about the development of support for specific languages in the [Guest Languages Special Interest Group Proposal](https://github.com/bytecodealliance/governance/blob/main/SIGs/SIG-guest-languages/proposal.md) document. 10 | 11 | One of the benefits of components is their portability across host runtimes. The runtime only needs 12 | to know what world the component is targeting in order to import or execute the component. This 13 | language guide hopes to demonstrate that with a prevailing `adder` world defined in 14 | [`examples/tutorial/wit/adder/world.wit`](https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/tutorial/wit/adder/world.wit). Furthermore, an example host that understands the `example` 15 | world has been provided in [`examples/example-host`](https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/README.md) for running components. Each 16 | toolchain section walks through creating a component of this world, which can be run either in the 17 | example host or from an application of that toolchain. This aims to provide a full story for using 18 | components within and among toolchains. 19 | 20 | Each section covers how to build and 21 | run components for a given toolchain: 22 | 23 | - [Wasm Language Support](#wasm-language-support) 24 | - [Language Agnostic Tooling](#language-agnostic-tooling) 25 | - [Building a Component with `wasm-tools`](#building-a-component-with-wasm-tools) 26 | - [Running a Component with Wasmtime](#running-a-component-with-wasmtime) 27 | - [C/C++ Tooling](./language-support/c.md) 28 | - [Building a Component with `wit-bindgen` and `wasm-tools`](./language-support/c.md#building-a-component-with-wit-bindgen-and-wasm-tools) 29 | - [Running a Component from C/C++ Applications](./language-support/c.md#running-a-component-from-cc-applications) 30 | - [C# Tooling](./language-support/csharp.md) 31 | - [Go Tooling](./language-support/go.md) 32 | - [JavaScript Tooling](./language-support/javascript.md) 33 | - [Building a Component with `jco`](./language-support/javascript.md#building-a-component-with-jco) 34 | - [Running a Component from JavaScript Applications](./language-support/javascript.md#running-a-component-from-javascript-applications) 35 | - [Python Tooling](./language-support/python.md) 36 | - [Building a Component with `componentize-py`](./language-support/python.md#building-a-component-with-componentize-py) 37 | - [Running components from Python Applications](./language-support/python.md#running-components-from-python-applications) 38 | - [Rust Tooling](./language-support/rust.md) 39 | - [Building a Component with `cargo component`](./language-support/rust.md#building-a-component-with-cargo-component) 40 | - [Running a Component from Rust Applications](./language-support/rust.md#running-a-component-from-rust-appliacations) 41 | 42 | ## Language Agnostic Tooling 43 | 44 | ### Building a Component with `wasm-tools` 45 | 46 | [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools) provides a suite of subcommands for 47 | working with WebAssembly modules and components. 48 | 49 | `wasm-tools` can be used to create a component from WebAssembly Text (WAT). This walks through creating a component from WAT that implements the [`adder` world](https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/tutorial/wit/adder/world.wit) and simply adds two numbers. 50 | 51 | 1. Install [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools/tree/main#installation), a 52 | tool for low-level manipulation of Wasm modules and components. 53 | 2. The `add` function is defined inside the following `world` world: 54 | 55 | ```wit 56 | package docs:adder@0.1.0; 57 | 58 | interface add { 59 | add: func(x: u32, y: u32) -> u32; 60 | } 61 | 62 | world adder { 63 | export add; 64 | } 65 | ``` 66 | 67 | 3. Define an `add` core module in WAT that exports an `add` function that adds two parameters: 68 | 69 | ```wat 70 | (module 71 | (func $add (param $lhs i32) (param $rhs i32) (result i32) 72 | local.get $lhs 73 | local.get $rhs 74 | i32.add) 75 | (export "docs:adder/add@0.1.0" (func $add)) 76 | ) 77 | ``` 78 | 79 | 4. Use `wasm-tools` to create a component from the core module, first embedding component metadata 80 | inside the core module and then encoding the WAT to a Wasm binary. 81 | 82 | ```sh 83 | $ wasm-tools component embed adder/world.wit add.wat -o add.wasm 84 | $ wasm-tools component new add.wasm -o add.component.wasm 85 | ``` 86 | 87 | ### Running a Component with Wasmtime 88 | 89 | You can "run" a component by calling one of its exports. Hosts and runtimes often only support 90 | running components with certain exports. The [`wasmtime`](https://github.com/bytecodealliance/wasmtime) CLI can only run "command" components, so in 91 | order to run the `add` function above, it first must be composed with a primary "command" component 92 | that calls it. See [documentation on running components](./creating-and-consuming/running.md) for 93 | more details. 94 | -------------------------------------------------------------------------------- /component-model/src/language-support/c.md: -------------------------------------------------------------------------------- 1 | # C/C++ Tooling 2 | 3 | WebAssembly components can be built from C and C++ using [`clang`][clang], the C language family frontend for [LLVM][llvm]. 4 | 5 | [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen) is a tool to generate guest language bindings from a 6 | given `.wit` file. 7 | 8 | Although `wit-bindgen` is a standalone tool (whereas some languages have more integrated toolchains like Rust's [`cargo-component`][cargo-component]), 9 | `wit-bindgen` can generate source-level bindings for `Rust`, `C`, `Java (TeaVM)`, and `TinyGo`, with the ability for more 10 | language generators to be added in the future. 11 | 12 | `wit-bindgen` can be used to build C applications that can be compiled directly to Wasm modules using [`clang`][clang] with a [`wasm32-wasi`][clang-tgt-wasm32-wasi] target. 13 | 14 | [clang]: https://clang.llvm.org/ 15 | [clang-tgt-wasm32-wasi]: https://clang.llvm.org/docs/ClangCommandLineReference.html#webassembly 16 | [llvm]: https://llvm.org/ 17 | [wasi]: https://wasi.dev/ 18 | [cargo-component]: https://crates.io/crates/cargo-component 19 | 20 | ## 1. Download dependencies 21 | 22 | First, install the CLI for [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen#cli-installation), [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools), and the [`WASI SDK`](https://github.com/webassembly/wasi-sdk). 23 | 24 | The WASI SDK will install a local version of `clang` configured with a wasi-sysroot. Follow [these instructions](https://github.com/WebAssembly/wasi-sdk#use) to configure it for use. Note that you can also use your installed system or emscripten `clang` by building with `--target=wasm32-wasi` but you will need some artifacts from WASI SDK to enable and link that build target (more information is available in WASI SDK's docs). 25 | 26 | ## 2. Generate program skeleton from WIT 27 | 28 | Start by generating a C skeleton from `wit-bindgen` using the [sample `adder/world.wit` file](https://github.com/bytecodealliance/component-docs/tree/main/examples/tutorial/wit/adder/world.wit): 29 | 30 | ``` 31 | > wit-bindgen c path/to/adder/world.wit 32 | Generating "adder.c" 33 | Generating "adder.h" 34 | Generating "adder_component_type.o" 35 | ``` 36 | 37 | This has generated several files: 38 | 39 | 1.`adder.h` (based on the `adder` world) with the prototype of the `add` function (prefixed by `exports_`) - `uint32_t exports_docs_adder_add_add(uint32_t x, uint32_t y);`. 40 | 2. `adder.c` that interfaces with the component model ABI to call your function. 41 | 3. `adder_component_type.o` which contains object code referenced in `adder.c` from an `extern` that must be linked via `clang`. 42 | 43 | ## 3. Write program code 44 | 45 | Next, create an `component.c` that implements the `adder` world (i.e. the interface defined in `adder.h`): 46 | 47 | ```c 48 | #include "adder.h" 49 | 50 | uint32_t exports_docs_adder_add_add(uint32_t x, uint32_t y) 51 | { 52 | return x + y; 53 | } 54 | ``` 55 | 56 | ## 4. Compile a WebAssembly module (P1) with `clang` 57 | 58 | Now, you can compile the function into a Wasm module via clang: 59 | 60 | ```console 61 | clang component.c adder.c adder_component_type.o -o adder.wasm -mexec-model=reactor 62 | ``` 63 | 64 | > Use the `clang` included in the WASI SDK installation, for example at `/bin/clang`. 65 | > 66 | > Alternatively, you can also use the published [`ghcr.io/webassembly/wasi-sdk` container images][wasi-sdk-images] 67 | > for performing builds. 68 | > 69 | > For example, to enter a container with `wasi-sdk` installed: 70 | > 71 | > ``` 72 | > docker run --rm -it --mount type=bind,src=path/to/app/src,dst=/app ghcr.io/webassembly/wasi-sdk:wasi-sdk-25 73 | > ``` 74 | > 75 | > See also: [`Dockerfile` in `wasi-sdk`][wasi-sdk-dockerfile] 76 | 77 | [wasi-sdk-images]: https://github.com/WebAssembly/wasi-sdk/pkgs/container/wasi-sdk 78 | [wasi-sdk-dockerfile]: https://github.com/WebAssembly/wasi-sdk/blob/main/docker/Dockerfile 79 | 80 | ## 5. Convert the P1 component to a P2 component with `wasm-tools` 81 | 82 | Next, we need to transform the P1 component to a P2 component. To do this, we can use `wasm-tools component new`: 83 | 84 | ```console 85 | wasm-tools component new ./adder.wasm -o adder.component.wasm 86 | ``` 87 | 88 | > [!NOTE] 89 | > The `.component.` extension has no special meaning -- `.wasm` files can be either modules or components. 90 | 91 | ## 6. (optional) Build a WASI-enabled WebAssembly (P2) component with `wasm-tools` 92 | 93 | Do note `wasm-tools component new` may fail if your code references any [WASI][wasi] APIs that must be imported, for 94 | example via standard library imports like `stdio.h`. 95 | 96 | Using WASI interfaces requires an additional step as the WASI SDK still references `wasi_snapshot_preview1` APIs that are not compatible directly with components. 97 | 98 | For example, modifying the above to reference `printf()` would compile: 99 | 100 | ```c 101 | #include "adder.h" 102 | #include 103 | 104 | uint32_t exports_docs_adder_add_add(uint32_t x, uint32_t y) 105 | { 106 | uint32_t result = x + y; 107 | printf("%d", result); 108 | return result; 109 | } 110 | ``` 111 | 112 | However, the module would fail to transform to a component: 113 | 114 | ``` 115 | >wasm-tools component new ./adder.wasm -o adder.component.wasm 116 | error: failed to encode a component from module 117 | 118 | Caused by: 119 | 0: failed to decode world from module 120 | 1: module was not valid 121 | 2: module requires an import interface named `wasi_snapshot_preview1` 122 | ``` 123 | 124 | To build a P2 component that uses [WASI][wasi] interfaces from a P1 component, we'll need to make use of adapter modules. 125 | 126 | Install the appropriate reactor adapter module [as documented here](https://github.com/bytecodealliance/wit-bindgen#creating-components-wasi). 127 | 128 | You can either get [the linked release][wasmtime-releases] of `wasi_snapshot_preview1.reactor.wasm` and rename it to `wasi_snapshot_preview1.wasm`, or build it directly from source in `wasmtime` following the [instructions here](https://github.com/bytecodealliance/wasmtime/tree/main/crates/wasi-preview1-component-adapter) (make sure you `git submodule update --init` first). 129 | 130 | Now, you can adapt preview1 to preview2 to build a component: 131 | 132 | ```console 133 | wasm-tools component new adder.wasm --adapt wasi_snapshot_preview1.wasm -o adder.component.wasm 134 | ``` 135 | 136 | [wasmtime-releases]: https://github.com/bytecodealliance/wasmtime/releases 137 | 138 | ## 7. Inspect the built component 139 | 140 | Finally, you can inspect the embedded wit to see your component (including any WASI imports if necessary): 141 | 142 | ``` 143 | >wasm-tools component wit adder.component.wasm 144 | package root:component; 145 | 146 | world root { 147 | import wasi:io/error@0.2.2; 148 | import wasi:io/streams@0.2.2; 149 | import wasi:cli/stdin@0.2.2; 150 | import wasi:cli/stdout@0.2.2; 151 | import wasi:cli/stderr@0.2.2; 152 | import wasi:cli/terminal-input@0.2.2; 153 | import wasi:cli/terminal-output@0.2.2; 154 | import wasi:cli/terminal-stdin@0.2.2; 155 | import wasi:cli/terminal-stdout@0.2.2; 156 | import wasi:cli/terminal-stderr@0.2.2; 157 | import wasi:clocks/wall-clock@0.2.2; 158 | import wasi:filesystem/types@0.2.2; 159 | import wasi:filesystem/preopens@0.2.2; 160 | 161 | export add: func(x: s32, y: s32) -> s32; 162 | } 163 | ... 164 | ``` 165 | 166 | ### 8. Running the component from the example host 167 | 168 | > [!WARNING] 169 | > You must be careful to use a version of the adapter (`wasi_snapshot_preview1.wasm`) that is compatible with the version of 170 | > `wasmtime` that will be used, to ensure that WASI interface versions (and relevant implementation) match. 171 | 172 | This repository contains an [example WebAssembly host][example-host] written in Rust that can run components that implement the `adder` world. 173 | 174 | > [!NOTE] 175 | > When hosts run components that use WASI interfaces, they must *explicitly* [add WASI to the linker][add-to-linker] to run the built component. 176 | 177 | A successful run should show the following output: 178 | 179 | ``` 180 | cargo run --release -- 1 2 adder.component.wasm 181 | Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host) 182 | Finished `release` profile [optimized] target(s) in 7.85s 183 | Running `target/debug/example-host 1 2 /tmp/docs/c/adder.component.wasm` 184 | 1 + 2 = 3 185 | ``` 186 | 187 | If *not* configured correctly, you may see errors like the following: 188 | 189 | ``` 190 | cargo run --release -- 1 2 adder.component.wasm 191 | Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host) 192 | Finished `release` profile [optimized] target(s) in 7.85s 193 | Running `target/release/example-host 1 2 adder.component.wasm` 194 | Error: Failed to instantiate the example world 195 | 196 | Caused by: 197 | 0: component imports instance `wasi:io/error@0.2.2`, but a matching implementation was not found in the linker 198 | 1: instance export `error` has the wrong type 199 | 2: resource implementation is missing 200 | ``` 201 | 202 | This kind of error normally indicates that the host in question does not contain satisfy WASI imports. 203 | 204 | [host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host 205 | [add-to-linker]: https://docs.wasmtime.dev/api/wasmtime_wasi/fn.add_to_linker_sync.html 206 | [example-host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host 207 | 208 | ## 9. Running a Component from C/C++ Applications 209 | 210 | It is not yet possible to run a WebAssembly Component using the C API of `wasmtime` `c-api`. See [`wasmtime` issue #6987](https://github.com/bytecodealliance/wasmtime/issues/6987) for more details. 211 | The c-api is preferred over directly using the example host Rust crate in C++. 212 | 213 | However, C/C++ language guest components can be composed with components written in any other language and 214 | run by their toolchains, or even composed with a C language command component and run via the `wasmtime` CLI 215 | or any other host. 216 | 217 | See the [Rust Tooling guide](../language-support/rust.md#running-a-component-from-rust-applications) for instructions on how to run this component from 218 | the Rust `example-host` (replacing the path to `add.wasm` with your `add-component` above). 219 | 220 | [!NOTE]: # 221 | [!WARNING]: # 222 | -------------------------------------------------------------------------------- /component-model/src/language-support/csharp.md: -------------------------------------------------------------------------------- 1 | # C# Tooling 2 | 3 | WebAssembly components in C# can be built with [componentize-dotnet][componentize-dotnet], 4 | a a NuGet package that can be used to create a fully AOT-compiled 5 | component, giving .NET developers a component experience comparable to those in Rust and TinyGo. 6 | 7 | [componentize-dotnet]: https://github.com/bytecodealliance/componentize-dotnet 8 | 9 | ## Building a Component with `componentize-dotnet` 10 | 11 | [`componentize-dotnet`][componentize-dotnet] serves as a one-stop shop, wrapping several tools into one: 12 | 13 | - [NativeAOT-LLVM](https://github.com/dotnet/runtimelab/tree/feature/NativeAOT-LLVM) (compilation) 14 | - [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen) (WIT imports and exports) 15 | - [wasm-tools](https://github.com/bytecodealliance/wasm-tools) (component conversion) 16 | - [WASI SDK](https://github.com/WebAssembly/wasi-sdk) (SDK used by NativeAOT-LLVM) 17 | - [Wac](https://github.com/bytecodealliance/wac) (used to compose components) 18 | 19 | First, install the .NET SDK. For this walkthrough, we’ll use the [.NET 10 SDK preview][dotnet-sdk]. 20 | You should also have [wasmtime](https://wasmtime.dev/) installed so you can run the binary that you produce. 21 | 22 | [dotnet-sdk]: https://dotnet.microsoft.com/en-us/download/dotnet/10.0 23 | [wasmtime]: https://wasmtime.dev/ 24 | 25 | ## 1. Create a new project 26 | 27 | Once you have the .NET SDK installed, create a new project: 28 | 29 | ```sh 30 | dotnet new install BytecodeAlliance.Componentize.DotNet.Templates 31 | dotnet new componentize.wasi.cli -o adder 32 | cd adder 33 | ``` 34 | 35 | ## 2. Create or download your WIT world 36 | 37 | Next, create or download the WIT world you would like to target. 38 | 39 | For this example we will use the [`adder` world][adder-world], with an `add` function (e.g. to `wit/component.wit`): 40 | 41 | ```wit 42 | package docs:adder@0.1.0; 43 | 44 | interface add { 45 | add: func(x: u32, y: u32) -> u32; 46 | } 47 | 48 | world adder { 49 | export add; 50 | } 51 | ``` 52 | 53 | In the `adder.csproj` project file, add a new ``: 54 | 55 | ```xml 56 | 57 | 58 | 59 | ``` 60 | 61 | Since this component will only export a function dotnet considers this a library project. 62 | Let's update the `` to be a library in the `adder.csproj`: 63 | 64 | ```diff 65 | - Exe 66 | + Library 67 | ``` 68 | 69 | And remove the automatically generated `Program.cs` file: 70 | 71 | ```bash 72 | rm Program.cs 73 | ``` 74 | 75 | [adder-world]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit 76 | 77 | ## 3. Write the implementation for the `adder` world 78 | 79 | If you try to build the project with `dotnet build`, you'll get an error like the following: 80 | 81 | ``` 82 | ➜ dotnet build 83 | Restore complete (8.6s) 84 | You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy 85 | adder failed with 1 error(s) (25.6s) 86 | /path/to/adder/obj/Debug/net10.0/wasi-wasm/wit_bindgen/AdderWorld.wit.exports.docs.adder.v0_1_0.AddInterop.cs(15,19): error CS0103: The name 'AddImpl' does not exist in the current context 87 | 88 | Build failed with 1 error(s) in 34.6s 89 | ``` 90 | 91 | This is because we've promised an implementation, but haven't yet written one for the `adder` world. 92 | 93 | To fix this, add the following code to your in a file called `Component.cs`: 94 | 95 | ```csharp 96 | namespace AdderWorld; 97 | 98 | public class AddImpl : IAdderWorld 99 | { 100 | public static uint Add(uint x, uint y) 101 | { 102 | return x + y; 103 | } 104 | } 105 | ``` 106 | 107 | Then, we can build our component: 108 | 109 | ```sh 110 | dotnet build 111 | ``` 112 | 113 | The component will be available at `bin/Debug/net10.0/wasi-wasm/native/adder.wasm`. 114 | 115 | ### 5. (optional) the component from the example host 116 | 117 | > [!WARNING] 118 | > You must be careful to use a version of the adapter (`wasi_snapshot_preview1.wasm`) that is compatible with the version of 119 | > `wasmtime` that will be used, to ensure that WASI interface versions (and relevant implementation) match. 120 | 121 | This repository contains an [example WebAssembly host][example-host] written in Rust that can run components that implement the `adder` world. 122 | 123 | > [!NOTE] 124 | > When hosts run components that use WASI interfaces, they must *explicitly* [add WASI to the linker][add-to-linker] to run the built component. 125 | 126 | A successful run should show the following output: 127 | 128 | ``` 129 | cargo run --release -- 1 2 adder.component.wasm 130 | Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host) 131 | Finished `release` profile [optimized] target(s) in 7.85s 132 | Running `target/debug/example-host 1 2 /tmp/docs/c/adder.component.wasm` 133 | 1 + 2 = 3 134 | ``` 135 | 136 | If *not* configured correctly, you may see errors like the following: 137 | 138 | ``` 139 | cargo run --release -- 1 2 adder.component.wasm 140 | Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host) 141 | Finished `release` profile [optimized] target(s) in 7.85s 142 | Running `target/release/example-host 1 2 adder.component.wasm` 143 | Error: Failed to instantiate the example world 144 | 145 | Caused by: 146 | 0: component imports instance `wasi:io/error@0.2.2`, but a matching implementation was not found in the linker 147 | 1: instance export `error` has the wrong type 148 | 2: resource implementation is missing 149 | ``` 150 | 151 | This kind of error normally indicates that the host in question does not contain satisfy WASI imports. 152 | 153 | [host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host 154 | [add-to-linker]: https://docs.wasmtime.dev/api/wasmtime_wasi/fn.add_to_linker_sync.html 155 | [example-host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host 156 | 157 | ## Building a component that exports an interface 158 | 159 | The previous example uses a WIT file that exports a function. However, you'll often prefer to export an interface, 160 | either to comply with an existing specification or to capture a set of functions and types that tend to go 161 | together. Let's expand our `example` world to export an interface rather than directly 162 | export the function. We are also adding the `hostapp` world to our WIT file which we will implement 163 | in [the next section](#building-a-component-that-imports-an-interface) to demonstrate how to build a 164 | component that *imports* an interface. 165 | 166 | ```wit 167 | // adder/world.wit 168 | package example:component; 169 | 170 | interface add { 171 | add: func(x: u32, y: u32) -> u32; 172 | } 173 | 174 | world example { 175 | export add; 176 | } 177 | 178 | world hostapp { 179 | import add; 180 | } 181 | ``` 182 | 183 | If you peek at the bindings, you'll notice that we now implement a class for the `add` interface 184 | rather than for the `example` world -- this is a consistent pattern. As you export more interfaces 185 | from your world, you implement more classes. 186 | 187 | Our `Component.cs` example gets the slight update of: 188 | 189 | ```csharp 190 | namespace ExampleWorld.wit.exports.example.component; 191 | 192 | public class AddImpl : IAdd 193 | { 194 | public static uint Add(uint x, uint y) 195 | { 196 | return x + y; 197 | } 198 | } 199 | ``` 200 | 201 | Once again, compile an application to a Wasm component using `dotnet build`: 202 | 203 | ```sh 204 | $ dotnet build 205 | Restore complete (0.4s) 206 | You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy 207 | adder succeeded (1.1s) → bin/Debug/net10.0/wasi-wasm/adder.dll 208 | 209 | Build succeeded in 2.5s 210 | ``` 211 | 212 | The component will be available at `bin/Debug/net10.0/wasi-wasm/native/adder.wasm`. 213 | 214 | ## Building a component that imports an interface 215 | 216 | So far, we've been dealing with library components. Now we will be creating a command component that 217 | implements the `hostapp` world. This component will import the `add` interface that is exported from 218 | our `adder` component and call the `add` function. We will later compose this command component with 219 | the `adder` library component we just built. 220 | 221 | Now we will be taking the `adder` component and executing it from another WebAssembly component. 222 | 223 | `dotnet new componentize.wasi.cli` creates a new project that creates an executable. 224 | 225 | Back out of the current project and create a new one: 226 | 227 | ```sh 228 | cd .. 229 | dotnet new componentize.wasi.cli -o host-app 230 | cd host-app 231 | ``` 232 | 233 | Copy the same WIT file as before into your project: 234 | 235 | ```wit 236 | // adder/world.wit 237 | package example:component; 238 | 239 | interface add { 240 | add: func(x: u32, y: u32) -> u32; 241 | } 242 | 243 | world example { 244 | export add; 245 | } 246 | 247 | world hostapp { 248 | import add; 249 | } 250 | ``` 251 | 252 | Add it to your `host-app.csproj` project file as a new `ItemGroup`: 253 | 254 | ```xml 255 | 256 | 257 | 258 | ``` 259 | 260 | Notice how the `World` changed from `example` to `hostapp`. The previous examples focused on 261 | implementing the class library for this WIT file - the `export` functions. Now we'll be focusing on 262 | the executable side of the application - the `hostapp` world. 263 | 264 | Modify `Program.cs` to look like this: 265 | 266 | ```csharp 267 | // Pull in all imports of the `hostapp` world, namely the `add` interface. 268 | // example.component refers to the package name defined in the WIT file. 269 | using HostappWorld.wit.imports.example.component; 270 | 271 | uint left = 1; 272 | uint right = 2; 273 | var result = AddInterop.Add(left, right); 274 | Console.WriteLine($"{left} + {right} = {result}"); 275 | ``` 276 | 277 | Once again, compile your component with `dotnet build`: 278 | 279 | ```sh 280 | $ dotnet build 281 | Restore complete (0.4s) 282 | You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy 283 | host-app succeeded (1.1s) → bin/Debug/net10.0/wasi-wasm/host-app.dll 284 | 285 | Build succeeded in 2.5s 286 | ``` 287 | 288 | At this point, you'll have two Webassembly components: 289 | 290 | 1. A component that implements the `example` world. 291 | 2. A component that implements the `hostapp` world. 292 | 293 | Since the `host-app` component depends on the `add` function which is defined in the `example` 294 | world, it needs to be composed the first component. You can compose your `host-app` component with 295 | your `adder` component by running [`wac plug`](https://github.com/bytecodealliance/wac): 296 | 297 | ```sh 298 | wac plug \ 299 | bin/Debug/net10.0/wasi-wasm/native/host-app.wasm \ 300 | --plug ../adder/bin/Debug/net10.0/wasi-wasm/native/adder.wasm \ 301 | -o main.wasm 302 | ``` 303 | 304 | You can also automate the process by adding the following to your `host-app.csproj`: 305 | 306 | ```xml 307 | 308 | 309 | bin/$(Configuration)/$(TargetFramework)/wasi-wasm/native/host-app.wasm 310 | ../adder/bin/$(Configuration)/$(TargetFramework)/wasi-wasm/native/adder.wasm 311 | 312 | 313 | 314 | 315 | ``` 316 | 317 | Run `dotnet build` again you will have a composed component in `./dist/main.wasm` 318 | 319 | Then you can run the composed component: 320 | 321 | ```sh 322 | wasmtime run ./dist/main.wasm 323 | 1 + 2 = 3 324 | ``` 325 | 326 | Check out the [componentize-dotnet docs][componentize-dotnet-docs] for more configurations options. 327 | 328 | [componentize-dotnet-docs]: https://github.com/bytecodealliance/componentize-dotnet 329 | 330 | [!NOTE]: # 331 | [!WARNING]: # 332 | -------------------------------------------------------------------------------- /component-model/src/language-support/go.md: -------------------------------------------------------------------------------- 1 | # Go Tooling 2 | 3 | The [TinyGo compiler](https://tinygo.org/) v0.34.0 and above has native support for the WebAssembly Component Model and WASI 0.2.0. 4 | 5 | This guide walks through building a component that implements `adder` world defined in the [`adder/world.wit` package][adder-wit]. 6 | The component will implement the `adder` world, which contains `add` interface with a `add` function. 7 | 8 | [adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit 9 | 10 | ## 1. Install the tools 11 | 12 | Follow the [TinyGo installation instructions](https://tinygo.org/getting-started/) to install the TinyGo compiler. 13 | 14 | Additionally, install the `wasm-tools` CLI tool from the [wasm-tools repository](https://github.com/bytecodealliance/wasm-tools/releases). 15 | 16 | > [!WARNING] 17 | > Due to some upstream issues, only `wasm-tools` versions 1.225.0 or earlier can be used with `wit-bindgen-go` 18 | > 19 | > If using the Rust toolchain to install `wasm-tools`, it can be installed like so: 20 | > `cargo install --locked wasm-tools@1.225.0 --force` 21 | 22 | To verify the installation, run the following commands: 23 | 24 | ``` 25 | $ tinygo version 26 | tinygo version 0.34.0 ... 27 | $ wasm-tools -V 28 | wasm-tools 1.255.0 ... 29 | ``` 30 | 31 | Optional: Install the [`wkg`][wkg] CLI tool to resolve the imports in the WIT file. The `wkg` CLI is a part of the [Wasm Component package manager](https://github.com/bytecodealliance/wasm-pkg-tools/releases) 32 | 33 | [wkg]: https://github.com/bytecodealliance/wasm-pkg-tools/tree/main/crates/wkg 34 | 35 | ## 2. Create your Go project 36 | 37 | Now, create your Go project: 38 | 39 | ```console 40 | mkdir add && cd add 41 | go mod init example.com 42 | ``` 43 | 44 | Ensure that the following `tool`s are installed: 45 | 46 | ``` 47 | tool ( 48 | go.bytecodealliance.org/cmd/wit-bindgen-go 49 | ) 50 | ``` 51 | 52 | > [!NOTE] 53 | > `go tool` was introduced in [Golang 1.24][go-1-24-release] and can be used to manage tooling in Go projects. 54 | 55 | Consider also running `go mod tidy` after adding the above tool. 56 | 57 | [go-1-24-release]: https://go.dev/blog/go1.24 58 | 59 | ## 2. Determine which World the Component will Implement 60 | 61 | Since we will be implementing the [`adder` world][adder-wit], we can copy the WIT to our project, 62 | under the `wit` folder (e.g. `wit/component.wit`): 63 | 64 | ```wit 65 | package docs:adder@0.1.0; 66 | 67 | interface add { 68 | add: func(x: u32, y: u32) -> u32; 69 | } 70 | 71 | world adder { 72 | export add; 73 | } 74 | ``` 75 | 76 | The `wasip2` target of TinyGo assumes that the component is targeting `wasi:cli/command@0.2.0` world 77 | (part of [`wasi:cli`][wasi-cli]) so it requires the imports of `wasi:cli/imports@0.2.0`. 78 | 79 | We need to include those interfaces as well in `component.wit`, by editing the `adder` world: 80 | 81 | ```wit 82 | world adder { 83 | include wasi:cli/imports@0.2.0; 84 | export add; 85 | } 86 | ``` 87 | 88 | ### Using `wkg` to automatically resolve and download imports 89 | 90 | Tools like [`wkg`][wkg] can be convenient to build a complete WIT package by resolving the imports. 91 | 92 | Running the `wkg wit fetch` command will resolve the imports and populate your `wit` folder with all relevant 93 | imported namespaces and packages. 94 | 95 | ``` 96 | $ wkg wit build 97 | WIT package written to docs:adder@0.1.0.wasm 98 | ``` 99 | 100 | [wasi-cli]: https://github.com/WebAssembly/wasi-cli 101 | 102 | ## 3. Generate bindings for the Wasm component 103 | 104 | Now that we have our WIT definitions bundled together into a WASM file, 105 | we can generate the bindings for our Wasm component, by adding a build directive: 106 | 107 | ```console 108 | go tool wit-bindgen-go generate --world adder --out internal ./docs:adder@0.1.0.wasm 109 | ``` 110 | 111 | > [!NOTE] 112 | > The `go tool` directive (added in [Golang 1.24][go-1-24-release]) installs and enables use of `wit-bindgen-go`, 113 | > part of the Bytecode Alliance suite of Golang tooling. 114 | 115 | The `internal` directory will contain the generated Go code that WIT package. 116 | 117 | ```console 118 | $ tree internal 119 | internal 120 | ├── docs 121 | │   └── adder 122 | │   ├── add 123 | │   │   ├── add.exports.go 124 | │   │   ├── add.wasm.go 125 | │   │   ├── add.wit.go 126 | │   │   └── empty.s 127 | │   └── adder 128 | │   └── adder.wit.go 129 | └── wasi 130 | ├── cli 131 | │   ├── environment 132 | │   │   ├── empty.s 133 | │   │   ├── environment.wasm.go 134 | │   │   └── environment.wit.go 135 | │   ├── exit 136 | │   │   ├── empty.s 137 | │   │   ├── exit.wasm.go 138 | │   │   └── exit.wit.go 139 | │   ├── stderr 140 | │   │   ├── empty.s 141 | │   │   ├── stderr.wasm.go 142 | │   │   └── stderr.wit.go 143 | │   ├── stdin 144 | │   │   ├── empty.s 145 | │   │   ├── stdin.wasm.go 146 | │   │   └── stdin.wit.go 147 | │   ├── stdout 148 | │   │   ├── empty.s 149 | │   │   ├── stdout.wasm.go 150 | │   │   └── stdout.wit.go 151 | │   ├── terminal-input 152 | │   │   ├── empty.s 153 | │   │   ├── terminal-input.wasm.go 154 | │   │   └── terminal-input.wit.go 155 | │   ├── terminal-output 156 | │   │   ├── empty.s 157 | │   │   ├── terminal-output.wasm.go 158 | │   │   └── terminal-output.wit.go 159 | │   ├── terminal-stderr 160 | │   │   ├── empty.s 161 | │   │   ├── terminal-stderr.wasm.go 162 | │   │   └── terminal-stderr.wit.go 163 | │   ├── terminal-stdin 164 | │   │   ├── empty.s 165 | │   │   ├── terminal-stdin.wasm.go 166 | │   │   └── terminal-stdin.wit.go 167 | │   └── terminal-stdout 168 | │   ├── empty.s 169 | │   ├── terminal-stdout.wasm.go 170 | │   └── terminal-stdout.wit.go 171 | ├── clocks 172 | │   ├── monotonic-clock 173 | │   │   ├── empty.s 174 | │   │   ├── monotonic-clock.wasm.go 175 | │   │   └── monotonic-clock.wit.go 176 | │   └── wall-clock 177 | │   ├── empty.s 178 | │   ├── wall-clock.wasm.go 179 | │   └── wall-clock.wit.go 180 | ├── filesystem 181 | │   ├── preopens 182 | │   │   ├── empty.s 183 | │   │   ├── preopens.wasm.go 184 | │   │   └── preopens.wit.go 185 | │   └── types 186 | │   ├── abi.go 187 | │   ├── empty.s 188 | │   ├── types.wasm.go 189 | │   └── types.wit.go 190 | ├── io 191 | │   ├── error 192 | │   │   ├── empty.s 193 | │   │   ├── error.wasm.go 194 | │   │   └── error.wit.go 195 | │   ├── poll 196 | │   │   ├── empty.s 197 | │   │   ├── poll.wasm.go 198 | │   │   └── poll.wit.go 199 | │   └── streams 200 | │   ├── empty.s 201 | │   ├── streams.wasm.go 202 | │   └── streams.wit.go 203 | ├── random 204 | │   ├── insecure 205 | │   │   ├── empty.s 206 | │   │   ├── insecure.wasm.go 207 | │   │   └── insecure.wit.go 208 | │   ├── insecure-seed 209 | │   │   ├── empty.s 210 | │   │   ├── insecure-seed.wasm.go 211 | │   │   └── insecure-seed.wit.go 212 | │   └── random 213 | │   ├── empty.s 214 | │   ├── random.wasm.go 215 | │   └── random.wit.go 216 | └── sockets 217 | ├── instance-network 218 | │   ├── empty.s 219 | │   ├── instance-network.wasm.go 220 | │   └── instance-network.wit.go 221 | ├── ip-name-lookup 222 | │   ├── abi.go 223 | │   ├── empty.s 224 | │   ├── ip-name-lookup.wasm.go 225 | │   └── ip-name-lookup.wit.go 226 | ├── network 227 | │   ├── abi.go 228 | │   ├── empty.s 229 | │   ├── network.wasm.go 230 | │   └── network.wit.go 231 | ├── tcp 232 | │   ├── abi.go 233 | │   ├── empty.s 234 | │   ├── tcp.wasm.go 235 | │   └── tcp.wit.go 236 | ├── tcp-create-socket 237 | │   ├── empty.s 238 | │   ├── tcp-create-socket.wasm.go 239 | │   └── tcp-create-socket.wit.go 240 | ├── udp 241 | │   ├── abi.go 242 | │   ├── empty.s 243 | │   ├── udp.wasm.go 244 | │   └── udp.wit.go 245 | └── udp-create-socket 246 | ├── empty.s 247 | ├── udp-create-socket.wasm.go 248 | └── udp-create-socket.wit.go 249 | 250 | 39 directories, 91 files 251 | ``` 252 | 253 | The `adder.exports.go` file contains the exported functions that need to be implemented in the Go code called `Exports`. 254 | 255 | ## 4. Implement the `add` Function 256 | 257 | ```Go 258 | //go:generate go tool wit-bindgen-go generate --world adder --out internal ./docs:adder@0.1.0.wasm 259 | 260 | package main 261 | 262 | import ( 263 | "example.com/internal/docs/adder/add" 264 | ) 265 | 266 | func init() { 267 | add.Exports.Add = func(x uint32, y uint32) uint32 { 268 | return x + y 269 | } 270 | } 271 | 272 | // main is required for the `wasi` target, even if it isn't used. 273 | func main() {} 274 | ``` 275 | 276 | Go's `init` functions are used to do initialization tasks that 277 | should be done before any other tasks. In this case, we are using it to export the `Add` function. 278 | 279 | ## 5. Build the Component 280 | 281 | We can build our component using TinyGo by specifying the wit-package to be `add.wit` and the WIT world to be `adder`. 282 | 283 | Under the hood, TinyGo invokes `wasm-tools` to embed the WIT file to the module and componentize it. 284 | 285 | ```console 286 | tinygo build -target=wasip2 -o add.wasm --wit-package docs:adder@0.1.0.wasm --wit-world adder main.go 287 | ``` 288 | 289 | > **WARNING:** By default, tinygo includes all debug-related information in your .wasm file. That is desirable when prototyping or testing locally to obtain useful backtraces in case of errors (for example, with `wasmtime::WasmBacktraceDetails::Enable`). To remove debug data and optimize your binary file, build with `-no-debug`. The resulting .wasm file will be considerably smaller (up to 75% reduction in size). 290 | 291 | We now have an add component that satisfies our `adder` world, exporting the `add` function, which 292 | 293 | We can confirm using the `wasm-tools component wit` command: 294 | 295 | ```console 296 | $ wasm-tools component wit add.wasm 297 | package root:component; 298 | 299 | world root { 300 | import wasi:io/error@0.2.0; 301 | import wasi:io/streams@0.2.0; 302 | import wasi:cli/stdout@0.2.0; 303 | import wasi:random/random@0.2.0; 304 | 305 | export add: func(x: s32, y: s32) -> s32; 306 | } 307 | ... 308 | ``` 309 | 310 | ## 5. Testing the `add` Component 311 | 312 | To run our add component, we need to use a host program with a WASI runtime that understands the 313 | `example` world -- we've provided an [`example-host`][example-host] that does just that. 314 | 315 | The example host calls the `add` function of a passed in component providing two operands. 316 | 317 | To use the example host, clone this repository and run the Rust program: 318 | 319 | ```console 320 | git clone git@github.com:bytecodealliance/component-docs.git 321 | cd component-docs/component-model/examples/example-host 322 | cargo run --release -- 1 2 /path/to/add.wasm 323 | ``` 324 | 325 | [example-host]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host 326 | 327 | [!NOTE]: # 328 | [!WARNING]: # 329 | -------------------------------------------------------------------------------- /component-model/src/language-support/python.md: -------------------------------------------------------------------------------- 1 | # Python Tooling 2 | 3 | ## Building a Component with `componentize-py` 4 | 5 | [`componentize-py`](https://github.com/bytecodealliance/componentize-py) is a tool that converts a Python 6 | application to a WebAssembly component. 7 | 8 | First, install [Python 3.10 or later](https://www.python.org/) and [pip](https://pypi.org/project/pip/) if you don't already have them. Then, install `componentize-py`: 9 | 10 | ```sh 11 | pip install componentize-py 12 | ``` 13 | 14 | Next, create or download the WIT world you would like to target. For this example we will use an [`adder` 15 | world][adder-wit] with an `add` function (e.g. `wit/component.wit`): 16 | 17 | ```wit 18 | package docs:adder@0.1.0; 19 | 20 | interface add { 21 | add: func(x: u32, y: u32) -> u32; 22 | } 23 | 24 | world adder { 25 | export add; 26 | } 27 | ``` 28 | 29 | If you want to generate bindings produced for the WIT world (for an IDE or typechecker), you can generate them using the `bindings` subcommand. Specify the path to the WIT interface with the world you are targeting: 30 | 31 | ```sh 32 | $ componentize-py --wit-path wit/component/wit --world adder bindings . 33 | ``` 34 | 35 | > [!NOTE] 36 | > You do not need to generate the bindings in order to `componentize` in the next step. `componentize` will generate bindings on-the-fly and bundle them into the produced component. 37 | 38 | Bindings were created in an `adder` package which contains an `Add` protocol with an `add` method that we can implement. 39 | 40 | To implement this interface, put the following code in a file called `app.py`: 41 | 42 | ```py 43 | import adder 44 | 45 | class Add(adder.Adder): 46 | def add(self, x: int, y: int) -> int: 47 | return x + y 48 | ``` 49 | 50 | We now can compile our application to a Wasm component using the `componentize` subcommand: 51 | 52 | ```console 53 | componentize-py \ 54 | --wit-path wit/component.wit \ 55 | --world adder \ 56 | componentize \ 57 | app \ 58 | -o add.wasm 59 | Component built successfully 60 | ``` 61 | 62 | To test the component, run it using the [Rust `add` host](./rust.md#creating-a-command-component-with-cargo-component): 63 | 64 | ```sh 65 | $ cd component-model/examples/add-host 66 | $ cargo run --release -- 1 2 ../path/to/add.wasm 67 | 1 + 2 = 3 68 | ``` 69 | 70 | See [`componentize-py`'s examples](https://github.com/bytecodealliance/componentize-py/tree/main/examples) to try out build HTTP, CLI, and TCP components from Python applications. 71 | 72 | ### Building a Component that Exports an Interface 73 | 74 | The [sample `add.wit` file](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/example-host/add.wit) exports a function. However, you'll often prefer to export an interface, either to comply with an existing specification or to capture a set of functions and types that tend to go together. Let's expand our example world to export an interface rather than directly export the function. 75 | 76 | ```wit 77 | // add-interface.wit 78 | package example:component; 79 | 80 | interface add { 81 | add: func(x: u32, y: u32) -> u32; 82 | } 83 | 84 | world example { 85 | export add; 86 | } 87 | ``` 88 | 89 | If you peek at the bindings, you'll notice that we now implement a class for the `add` interface rather than for the `example` world. This is a consistent pattern. As you export more interfaces from your world, you implement more classes. Our add example gets the slight update of: 90 | 91 | ```py 92 | # app.py 93 | import example 94 | 95 | class Add(example.Example): 96 | def add(self, a: int, b: int) -> int: 97 | return a + b 98 | ``` 99 | 100 | Once again, compile an application to a Wasm component using the `componentize` subcommand: 101 | 102 | ```sh 103 | $ componentize-py --wit-path add-interface.wit --world example componentize app -o add.wasm 104 | Component built successfully 105 | ``` 106 | 107 | ## Running components from Python Applications 108 | 109 | Wasm components can also be invoked from Python applications. This section walks through using tooling 110 | to call the [pre-built `app.wasm` component][add-wasm] in the examples. 111 | 112 | > `wasmtime-py` is only able to run components built with `componentize-py` when the `--stub-wasi` option is used at build time. This is because `wasmtime-py` does not yet support [resources](../design/wit.md#resources), and `componentize-py` by default generates components which use resources from the `wasi:cli` world. See [this example](https://github.com/bytecodealliance/componentize-py/tree/main/examples/sandbox) of using the `--stub-wasi` option to generate a `wasmtime-py`-compatible component. 113 | 114 | First, install [Python 3.11 or later](https://www.python.org/) and [pip](https://pypi.org/project/pip/) if you don't already have them. Then, install [`wasmtime-py`](https://github.com/bytecodealliance/wasmtime-py): 115 | 116 | ```sh 117 | $ pip install wasmtime 118 | ``` 119 | 120 | First, generate the bindings to be able to call the component from a Python host application. 121 | 122 | ```sh 123 | # Get an add component that does not import the WASI CLI world 124 | $ wget https://github.com/bytecodealliance/component-docs/raw/main/component-model/examples/example-host/add.wasm 125 | $ python3 -m wasmtime.bindgen add.wasm --out-dir add 126 | ``` 127 | 128 | The generated package `add` has all of the requisite exports/imports for the 129 | component and is annotated with types to assist with type-checking and 130 | self-documentation as much as possible. Inside the package is a `Root` class 131 | with an `add` function that calls the component's exported `add` function. We 132 | can now write a Python program that calls `add`: 133 | 134 | ```py 135 | from add import Root 136 | from wasmtime import Store 137 | 138 | def main(): 139 | store = Store() 140 | component = Root(store) 141 | print("1 + 2 =", component.add(store, 1, 2)) 142 | 143 | if __name__ == '__main__': 144 | main() 145 | ``` 146 | 147 | Run the Python host program: 148 | 149 | ```sh 150 | $ python3 host.py 151 | 1 + 2 = 3 152 | ``` 153 | 154 | [add-wasm]: https://github.com/bytecodealliance/component-docs/blob/main/component-model/examples/example-host/add.wasm 155 | 156 | [adder-wit]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit 157 | 158 | [!NOTE]: # 159 | -------------------------------------------------------------------------------- /component-model/src/language-support/rust.md: -------------------------------------------------------------------------------- 1 | # Components in Rust 2 | 3 | Rust has first-class support for the component model via the [`cargo-component` tool][cargo-component]. 4 | We will be using the `cargo component` subcommand to create WebAssembly components using Rust as 5 | the component's implementation language. 6 | 7 | > [!NOTE] 8 | > You can find more details about `cargo-component` on [crates.io](https://crates.io/crates/cargo-component). 9 | 10 | ## 1. Setup 11 | 12 | Install [`cargo-component`][cargo-component-install]: 13 | ```sh 14 | cargo install --locked cargo-component 15 | ``` 16 | Install [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools#installation): 17 | ```sh 18 | cargo install --locked wasm-tools 19 | ``` 20 | Install [`wasmtime`](https://github.com/bytecodealliance/wasmtime#installation): 21 | ```sh 22 | curl https://wasmtime.dev/install.sh -sSf | bash 23 | ``` 24 | 25 | ## 2. Scaffolding a Component 26 | 27 | We will create a component in Rust that implements the `add` function exported 28 | by the [`adder` world][docs-adder] world in the `docs:adder` 29 | [package](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#package-names). 30 | 31 | First, we will create a new WebAssembly component package called `add`: 32 | ```sh 33 | cargo component new add --lib && cd add 34 | ``` 35 | 36 | ## 3. Adding the WIT world 37 | 38 | We now need to change our generated `wit/world.wit` to match `docs:adder`: 39 | ```wit 40 | {{#include ../../examples/tutorial/wit/adder/world.wit}} 41 | ``` 42 | 43 | The `package.metadata.component` section of our `Cargo.toml` should be changed 44 | to the following: 45 | 46 | ```toml 47 | [package.metadata.component] 48 | package = "docs:adder" 49 | ``` 50 | 51 | ## 4. Generating bindings 52 | 53 | Now that we've updated our `world.wit` and `Cargo.toml`, we can re-generate 54 | bindings with the command below: 55 | 56 | ```sh 57 | cargo component bindings 58 | ``` 59 | 60 | `cargo-component` will generate bindings for our 61 | world and create a `Guest` trait that a component should 62 | implement. 63 | 64 | ## 5. Implementing the `Guest` trait 65 | 66 | Implement the `Guest` trait in `src/lib.rs`, using the scaffolded code. Your code should look something like the following: 67 | 68 | ```rs 69 | {{#include ../../examples/tutorial/adder/src/lib.rs}} 70 | ``` 71 | 72 | ## 6. Building a Component 73 | 74 | Now, let's build our component, being sure to optimize with a release build: 75 | 76 | ```sh 77 | cargo component build --release 78 | ``` 79 | 80 | > [!WARNING] 81 | > Building with `--release` removes all debug-related information from the resulting `.wasm` file. 82 | > 83 | > When prototyping or testing locally, you might want to avoid `--release` to 84 | > obtain useful backtraces in case of errors (for example, with 85 | > [`wasmtime::WasmBacktraceDetails::Enable`](https://docs.rs/wasmtime/latest/wasmtime/enum.WasmBacktraceDetails.html#variant.Enable)). 86 | > Note: the resulting `.wasm` file will be considerably larger (likely 4MB+). 87 | 88 | You can use `wasm-tools` to output the WIT package of the component: 89 | 90 | ```sh 91 | wasm-tools component wit target/wasm32-wasip1/release/add.wasm 92 | ``` 93 | 94 | The command above should produce the output below: 95 | 96 | ```wit 97 | package root:component; 98 | 99 | world root { 100 | export docs:adder/add@0.1.0; 101 | } 102 | package docs:adder@0.1.0 { 103 | interface add { 104 | add: func(x: u32, y: u32) -> u32; 105 | } 106 | } 107 | ``` 108 | 109 | 110 | ### Running a Component 111 | 112 | To verify that our component works, lets run it from a Rust application that knows how to run a 113 | component targeting the [`adder` world](#adding-the-wit-world). 114 | 115 | The application uses [`wasmtime`](https://github.com/bytecodealliance/wasmtime) crates to generate 116 | Rust bindings, bring in WASI worlds, and execute the component. 117 | 118 | ```console 119 | $ cd examples/example-host 120 | $ cargo run --release -- 1 2 ../add/target/wasm32-wasip1/release/adder.wasm 121 | 1 + 2 = 3 122 | ``` 123 | 124 | ## Importing an interface 125 | 126 | The world file (`wit/world.wit`) we generated doesn't specify any imports. 127 | If your component consumes other components, you can edit the `world.wit` file to import their interfaces. 128 | 129 | > [!NOTE] 130 | > This section is about importing custom WIT interfaces from library components. 131 | > By default, `cargo-component` imports any required [WASI interfaces](https://wasi.dev/interfaces) 132 | > for us without needing to explicitly declare them. 133 | 134 | 135 | For example, suppose you have created and built an adder component as explained in the [exporting an interface section](#exporting-an-interface-with-cargo-component) and want to use that component in a calculator component. Here is a partial example world for a calculator that imports the add interface: 136 | 137 | ```wit 138 | // in the 'calculator' project 139 | 140 | // wit/world.wit 141 | package docs:calculator; 142 | 143 | interface calculate { 144 | eval-expression: func(expr: string) -> u32; 145 | } 146 | 147 | world calculator { 148 | export calculate; 149 | import docs:adder/add@0.1.0; 150 | } 151 | ``` 152 | 153 | ### Referencing the package to import 154 | 155 | Because the `docs:adder` package is in a different project, we must first tell `cargo component` how to find it. To do this, add the following to the `Cargo.toml` file: 156 | 157 | ```toml 158 | [package.metadata.component.target.dependencies] 159 | "docs:adder" = { path = "../adder/wit" } # directory containing the WIT package 160 | ``` 161 | 162 | > [!NOTE] 163 | > The path for `docs:adder` is relative to the `wit` _directory_, not to the `world.wit` file. 164 | > 165 | > A WIT package may be spread across multiple files in the same directory; `cargo component` will search them all. 166 | 167 | ### Calling the import from Rust 168 | 169 | Now the declaration of `add` in the adder's WIT file is visible to the `calculator` project. To invoke the imported `add` interface from the `calculate` implementation: 170 | 171 | ```rust 172 | // src/lib.rs 173 | mod bindings; 174 | 175 | use bindings::exports::docs::calculator::calculate::Guest; 176 | 177 | // Bring the imported add function into scope 178 | use bindings::docs::calculator::add::add; 179 | 180 | struct Component; 181 | 182 | impl Guest for Component { 183 | fn eval_expression(expr: String) -> u32 { 184 | // Cleverly parse `expr` into values and operations, and evaluate 185 | // them meticulously. 186 | add(123, 456) 187 | } 188 | } 189 | ``` 190 | 191 | ### Fulfilling the import 192 | 193 | When you build this using `cargo component build`, the `add` interface remains imported. The calculator has taken a dependency on the `add` _interface_, but has not linked the `adder` implementation of that interface - this is not like referencing the `adder` crate. (Indeed, `calculator` could import the `add` interface even if there was no Rust project implementing the WIT file.) You can see this by running [`wasm-tools component wit`](https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component) to view the calculator's world: 194 | 195 | ``` 196 | # Do a release build to prune unused imports (e.g. WASI) 197 | $ cargo component build --release 198 | 199 | $ wasm-tools component wit ./target/wasm32-wasip1/release/calculator.wasm 200 | package root:component; 201 | 202 | world root { 203 | import docs:adder/add@0.1.0; 204 | 205 | export docs:calculator/calculate@0.1.0; 206 | } 207 | ``` 208 | 209 | As the import is unfulfilled, the `calculator.wasm` component could not run by itself in its current form. To fulfill the `add` import, so that 210 | only `calculate` is exported, you would need to [compose the `calculator.wasm` with some `adder.wasm` into a single, self-contained component](../creating-and-consuming/composing.md). 211 | 212 | ## Creating a command component with `cargo component` 213 | 214 | A _command_ is a component with a specific export that allows it to be executed directly by `wasmtime` (or other `wasi:cli` hosts). In Rust terms, it's the equivalent of an application (`bin`) package with a `main` function, instead of a library crate (`lib`) package. 215 | 216 | To create a command with cargo component, run: 217 | 218 | ```sh 219 | cargo component new 220 | ``` 221 | 222 | Unlike library components, this does _not_ have the `--lib` flag. You will see that the created project is different too: 223 | 224 | - It doesn't contain a `.wit` file. `cargo component build` will automatically export the `wasi:cli/run` interface for Rust `bin` packages, and hook it up to `main`. 225 | - Because there's no `.wit` file, `Cargo.toml` doesn't contain a `package.metadata.component.target` section. 226 | - The Rust file is called `main.rs` instead of `lib.rs`, and contains a `main` function instead of an interface implementation. 227 | 228 | You can write Rust in this project, just as you normally would, including importing your own or third-party crates. 229 | 230 | > All the crates that make up your project are linked together at build time, and compiled to a _single_ Wasm component. In this case, all the linking is happening at the Rust level: no WITs or component composition is involved. Only if you import Wasm interfaces do WIT and composition come into play. 231 | 232 | To run your command component: 233 | 234 | ```sh 235 | cargo component build 236 | wasmtime run ./target/wasm32-wasip1/debug/.wasm 237 | ``` 238 | 239 | > **WARNING:** If your program prints to standard out or error, you may not see the printed output! Some versions of `wasmtime` have a bug where they don't flush output streams before exiting. To work around this, add a `std::thread::sleep()` with a 10 millisecond delay before exiting `main`. 240 | 241 | ### Importing an interface into a command component 242 | 243 | As mentioned above, `cargo component build` doesn't generate a WIT file for a command component. If you want to import a Wasm interface, though, you'll need to create a WIT file and a world, plus reference the packages containing your imports: 244 | 245 | 1. Add a `wit/world.wit` to your project, and write a WIT world that imports the interface(s) you want to use. For example: 246 | 247 | ```wit 248 | package docs:app; 249 | 250 | world app { 251 | import docs:calculator/calculate@0.1.0; 252 | } 253 | ``` 254 | 255 | > `cargo component` sometimes fails to find packages if versions are not set explicitly. For example, if the calculator WIT declares `package docs:calculator` rather than `docs:calculator@0.1.0`, then you may get an error even though `cargo component build` automatically versions the binary export. 256 | 257 | 2. Edit `Cargo.toml` to tell `cargo component` about the new WIT file: 258 | 259 | ```toml 260 | [package.metadata.component.target] 261 | path = "wit" 262 | ``` 263 | 264 | (This entry is created automatically for library components but not for command components.) 265 | 266 | 3. Edit `Cargo.toml` to tell `cargo component` where to find external package WITs: 267 | 268 | ```toml 269 | [package.metadata.component.target.dependencies] 270 | "docs:calculator" = { path = "../calculator/wit" } 271 | "docs:adder" = { path = "../adder/wit" } 272 | ``` 273 | 274 | > If the external package refers to other packages, you need to provide the paths to them as well. 275 | 276 | 4. Use the imported interface in your Rust code: 277 | 278 | ```rust 279 | use bindings::docs::calculator::calculate::eval_expression; 280 | 281 | fn main() { 282 | let result = eval_expression("1 + 1"); 283 | println!("1 + 1 = {result}"); 284 | } 285 | ``` 286 | 287 | 5. [Compose the command component with the `.wasm` components that implement the imports.](../creating-and-consuming/composing.md) 288 | 289 | 6. Run the composed component: 290 | 291 | ```console 292 | $ wasmtime run ./my-composed-command.wasm 293 | 1 + 1 = 579 # might need to go back and do some work on the calculator implementation 294 | ``` 295 | 296 | ## Using user-defined types 297 | 298 | [User-defined types](../design/wit.md#user-defined-types) map to Rust types as follows. 299 | 300 | | WIT type | Rust binding | 301 | |------------|--------------| 302 | | `record` | `struct` with public fields corresponding to the record fields | 303 | | `variant` | `enum` with cases corresponding to the variant cases | 304 | | `enum` | `enum` with cases corresponding to the enum cases, with no data attached | 305 | | `resource` | [See below](#using-resources) | 306 | | `flags` | Opaque type supporting bit flag operations, with constants for flag values | 307 | 308 | For example, consider the following WIT: 309 | 310 | ```wit 311 | interface types { 312 | enum operation { 313 | add, 314 | sub, 315 | mul, 316 | div 317 | } 318 | 319 | record expression { 320 | left: u32, 321 | operation: operation, 322 | right: u32 323 | } 324 | 325 | eval: func(expr: expression) -> u32; 326 | } 327 | ``` 328 | 329 | When exported from a component, this could be implemented as: 330 | 331 | ```rust 332 | impl Guest for Implementation { 333 | fn eval(expr: Expression) -> u32 { 334 | // Record fields become public fields on a struct 335 | let (l, r) = (expr.left, expr.right); 336 | match expr.operation { 337 | // Enum becomes an enum with only unit cases 338 | Operation::Add => l + r, 339 | Operation::Sub => l - r, 340 | Operation::Mul => l * r, 341 | Operation::Div => l / r, 342 | } 343 | } 344 | } 345 | ``` 346 | 347 | ## Using resources 348 | 349 | [Resources](../design/wit.md#resources) are handles to entities that live outside the component, for example in a host, or in a different component. 350 | 351 | ### Example 352 | 353 | In this section, our example resource will be a [Reverse Polish Notation (RPN)](https://en.wikipedia.org/wiki/Reverse_Polish_notation) calculator. (Engineers of a certain vintage will remember this from handheld calculators of the 1970s.) A RPN calculator is a stateful entity: a consumer pushes operands and operations onto a stack maintained within the calculator, then evaluates the stack to produce a value. The resource in WIT looks like this: 354 | 355 | ```wit 356 | package docs:rpn@0.1.0; 357 | 358 | interface types { 359 | enum operation { 360 | add, 361 | sub, 362 | mul, 363 | div 364 | } 365 | 366 | resource engine { 367 | constructor(); 368 | push-operand: func(operand: u32); 369 | push-operation: func(operation: operation); 370 | execute: func() -> u32; 371 | } 372 | } 373 | 374 | world calculator { 375 | export types; 376 | } 377 | ``` 378 | 379 | ### Implementing and exporting a resource in a component 380 | 381 | To implement the calculator using `cargo component`: 382 | 383 | 1. Create a library component as shown in previous sections, with the WIT given above. 384 | 385 | 2. Define a Rust `struct` to represent the calculator state: 386 | 387 | ```rust 388 | use std::cell::RefCell; 389 | 390 | struct CalcEngine { 391 | stack: RefCell>, 392 | } 393 | ``` 394 | 395 | > Why is the stack wrapped in a `RefCell`? As we will see, the generated Rust trait for the calculator engine has _immutable_ references to `self`. But our implementation of that trait will need to mutate the stack. So we need a type that allows for interior mutability, such as `RefCell` or `Arc>`. 396 | 397 | 3. The generated bindings (`bindings.rs`) for an exported resource include a trait named `GuestX`, where `X` is the resource name. (You may need to run `cargo component build` to regenerate the bindings after updating the WIT.) For the calculator `engine` resource, the trait is `GuestEngine`. Implement this trait on the `struct` from step 2: 398 | 399 | ```rust 400 | use bindings::exports::docs::rpn::types::{GuestEngine, Operation}; 401 | 402 | impl GuestEngine for CalcEngine { 403 | fn new() -> Self { 404 | CalcEngine { 405 | stack: RefCell::new(vec![]) 406 | } 407 | } 408 | 409 | fn push_operand(&self, operand: u32) { 410 | self.stack.borrow_mut().push(operand); 411 | } 412 | 413 | fn push_operation(&self, operation: Operation) { 414 | let mut stack = self.stack.borrow_mut(); 415 | let right = stack.pop().unwrap(); // TODO: error handling! 416 | let left = stack.pop().unwrap(); 417 | let result = match operation { 418 | Operation::Add => left + right, 419 | Operation::Sub => left - right, 420 | Operation::Mul => left * right, 421 | Operation::Div => left / right, 422 | }; 423 | stack.push(result); 424 | } 425 | 426 | fn execute(&self) -> u32 { 427 | self.stack.borrow_mut().pop().unwrap() // TODO: error handling! 428 | } 429 | } 430 | ``` 431 | 432 | 4. We now have a working calculator type which implements the `engine` contract, but we must still connect that type to the `engine` resource type. This is done by implementing the generated `Guest` trait. For this WIT, the `Guest` trait contains nothing except an associated type. You can use an empty `struct` to implement the `Guest` trait on. Set the associated type for the resource - in our case, `Engine` - to the type which implements the resource trait - in our case, the `CalcEngine` `struct` which implements `GuestEngine`. Then use the `export!` macro to export the mapping: 433 | 434 | ```rust 435 | struct Implementation; 436 | impl Guest for Implementation { 437 | type Engine = CalcEngine; 438 | } 439 | 440 | bindings::export!(Implementation with_types_in bindings); 441 | ``` 442 | 443 | This completes the implementation of the calculator `engine` resource. Run `cargo component build` to create a component `.wasm` file. 444 | 445 | ### Importing and consuming a resource in a component 446 | 447 | To use the calculator engine in another component, that component must import the resource. 448 | 449 | 1. Create a command component as shown in previous sections. 450 | 451 | 2. Add a `wit/world.wit` to your project, and write a WIT world that imports the RPN calculator types: 452 | 453 | ```wit 454 | package docs:rpn-cmd; 455 | 456 | world app { 457 | import docs:rpn/types@0.1.0; 458 | } 459 | ``` 460 | 461 | 3. Edit `Cargo.toml` to tell `cargo component` about the new WIT file and the external RPN package file: 462 | 463 | ```toml 464 | [package.metadata.component] 465 | package = "docs:rpn-cmd" 466 | 467 | [package.metadata.component.target] 468 | path = "wit" 469 | 470 | [package.metadata.component.target.dependencies] 471 | "docs:rpn" = { path = "../wit" } # or wherever your resource WIT is 472 | ``` 473 | 474 | 4. The resource now appears in the generated bindings as a `struct`, with appropriate associated functions. Use these to construct a test app: 475 | 476 | ```rust 477 | #[allow(warnings)] 478 | mod bindings; 479 | use bindings::docs::rpn::types::{Engine, Operation}; 480 | 481 | fn main() { 482 | let calc = Engine::new(); 483 | calc.push_operand(1); 484 | calc.push_operand(2); 485 | calc.push_operation(Operation::Add); 486 | let sum = calc.execute(); 487 | println!("{sum}"); 488 | } 489 | ``` 490 | 491 | You can now build the command component and [compose it with the `.wasm` component that implements the resource.](../creating-and-consuming/composing.md). You can then run the composed command with `wasmtime run`. 492 | 493 | ### Implementing and exporting a resource implementation in a host 494 | 495 | If you are hosting a Wasm runtime, you can export a resource from your host for guests to consume. Hosting a runtime is outside the scope of this book, so we will give only a broad outline here. This is specific to the Wasmtime runtime; other runtimes may express things differently. 496 | 497 | 1. Use `wasmtime::component::bindgen!` to specify the WIT you are a host for: 498 | 499 | ```rust 500 | wasmtime::component::bindgen!({ 501 | path: "../wit" 502 | }); 503 | ``` 504 | 505 | 2. Tell `bindgen!` how you will represent the resource in the host via the `with` field. This can be any Rust type. For example, the RPN engine could be represented by a `CalcEngine` struct: 506 | 507 | ```rust 508 | wasmtime::component::bindgen!({ 509 | path: "../wit", 510 | with: { 511 | "docs:rpn/types/engine": CalcEngine, 512 | } 513 | }); 514 | ``` 515 | 516 | > If you don't specify the host representation for a resource, it defaults to an empty enum. This is rarely useful as resources are usually stateful. 517 | 518 | 3. If the representation type isn't a built-in type, define it: 519 | 520 | ```rust 521 | struct CalcEngine { /* ... */ } 522 | ``` 523 | 524 | 4. As a host, you will already be implementing a `Host` trait. You will now need to implement a `HostX` trait (where `X` is the resource name) _on the same type_ as the `Host` trait: 525 | 526 | ```rust 527 | impl docs::rpn::types::HostEngine for MyHost { 528 | fn new(&mut self) -> wasmtime::component::Resource { /* ... */ } 529 | fn push_operand(&mut self, self_: wasmtime::component::Resource) { /* ... */ } 530 | // etc. 531 | } 532 | ``` 533 | 534 | > **Important:** You implement this on the 'overall' host type, *not* on the resource representation! Therefore, the `self` reference in these functions is to the 'overall' host type. For instance methods of the resource, the instance is identified by a second parameter (`self_`), of type `wasmtime::component::Resource`. 535 | 536 | 5. Add a `wasmtime::component::ResourceTable` to the host: 537 | 538 | ```rust 539 | struct MyHost { 540 | calcs: wasmtime::component::ResourceTable, 541 | } 542 | ``` 543 | 544 | 6. In your resource method implementations, use this table to store and access instances of the resource representation: 545 | 546 | ```rust 547 | impl docs::rpn::types::HostEngine for MyHost { 548 | fn new(&mut self) -> wasmtime::component::Resource { 549 | self.calcs.push(CalcEngine::new()).unwrap() // TODO: error handling 550 | } 551 | fn push_operand(&mut self, self_: wasmtime::component::Resource) { 552 | let calc_engine = self.calcs.get(&self_).unwrap(); 553 | // calc_engine is a CalcEngine - call its functions 554 | } 555 | // etc. 556 | } 557 | ``` 558 | 559 | [cargo-component]: https://github.com/bytecodealliance/cargo-component 560 | [cargo-component-install]: https://github.com/bytecodealliance/cargo-component#install 561 | [docs-adder]: https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/adder/world.wit 562 | [!NOTE]: # 563 | [!WARNING]: # 564 | -------------------------------------------------------------------------------- /component-model/src/reference/useful-links.md: -------------------------------------------------------------------------------- 1 | # Useful links 2 | 3 | The following references are helpful in understanding the Component Model and related ecosystem/projects. 4 | 5 | - [WebAssembly Composition tool (`wac`)][wac] 6 | - [WebAssembly package tools (notably `wkg`)][wkg] 7 | - [WASI Preview 2][wasi-p2] 8 | - [Component Model internals][wasm-cm-repo] 9 | - [Component Model AST][ast-explainer] 10 | - [Canonical ABI][canonical-abi] 11 | 12 | [wasm-cm-repo]: https://github.com/WebAssembly/component-model 13 | [wasi-p2]: https://github.com/WebAssembly/WASI/tree/main/wasip2 14 | [ast-explainer]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md 15 | [canonical-abi]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md 16 | [wac]: https://github.com/bytecodealliance/wac 17 | [wkg]: https://github.com/bytecodealliance/wasm-pkg-tools 18 | -------------------------------------------------------------------------------- /component-model/src/reference/videos.md: -------------------------------------------------------------------------------- 1 | # Videos 2 | 3 | The WebAssembly ecosystem often gives talks about functionality and features of WebAssembly components. 4 | 5 | This page is a non-exhaustive list of some of the best talks and videos related to the component model. 6 | 7 | ## Overviews of the Component Model 8 | 9 | [Threading the needle with concurrency and parallelism in the Component Model - Luke Wagner](https://www.youtube.com/watch?v=mkkYNw8gTQg) 10 | 11 | [The Path to Components - Luke Wagner](https://www.youtube.com/watch?v=phodPLY8zNE) 12 | 13 | [What is a Component (and Why)? - Luke Wagner](https://www.youtube.com/watch?v=tAACYA1Mwv4) 14 | 15 | [Deconstructing WebAssembly Components - Ryan Levick](https://www.youtube.com/watch?v=zqfF7Ssa2QI) 16 | 17 | ## Full Conference playlists 18 | 19 | > [!WARNING] 20 | > Exercise caution when watching conference talks and videos older than ~2024. 21 | > 22 | > WebAssembly specifications have changed quite a bit since 2023 and proposed versus implemented 23 | > semantics (Component Model, WIT, etc) have seen considerable improvements. 24 | 25 | ### Wasm I/O 26 | 27 | [Wasm I/O 2025 Playlist](https://www.youtube.com/playlist?list=PLP3xGl7Eb-4OtFH1tcQm6u7_LRED7-3rg) 28 | 29 | [Wasm I/O 2024 Playlist](https://www.youtube.com/playlist?list=PLP3xGl7Eb-4Nmj4CJ5WLQZx5UAYvhH920) 30 | 31 | [Wasm I/O 2023 Playlist](https://www.youtube.com/playlist?list=PLP3xGl7Eb-4NtSX-wzOxUCjPsBcw2zrPA) 32 | -------------------------------------------------------------------------------- /component-model/src/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Sitemap: https://component-model.bytecodealliance.org/sitemap.xml 3 | -------------------------------------------------------------------------------- /component-model/src/runtimes/jco.md: -------------------------------------------------------------------------------- 1 | # jco 2 | 3 | [jco](https://github.com/bytecodealliance/jco) is a fully native JavaScript tool for working with components in JavaScript. It supports the [`wasi:cli/command` world](https://github.com/WebAssembly/wasi-cli/blob/main/wit/command.wit). `jco` also provides features for transpiling Wasm components to ES modules, and for building Wasm components from JavaScript and WIT. 4 | 5 | To run a component with `jco`, run: 6 | 7 | ```sh 8 | jco run 9 | ``` 10 | 11 | `jco`'s WASI implementation grants the component full access to the underlying system resources. For example, the component can read all environment variables of the `jco` process, or read and write files anywhere in the file system. 12 | -------------------------------------------------------------------------------- /component-model/src/runtimes/wasmtime.md: -------------------------------------------------------------------------------- 1 | # Wasmtime 2 | 3 | [Wasmtime](https://github.com/bytecodealliance/wasmtime/) is the reference implementation of the Component Model. It supports running components that implement the [`wasi:cli/command` world](https://github.com/WebAssembly/wasi-cli/blob/main/wit/command.wit) and serving components that implement the [`wasi:http/proxy` world](https://github.com/WebAssembly/wasi-http/blob/main/wit/proxy.wit). 4 | 5 | ## Running command components with Wasmtime 6 | To run a command component with Wasmtime, execute: 7 | 8 | ```sh 9 | wasmtime run 10 | ``` 11 | 12 | > If you are using an older version of `wasmtime`, you may need to add the `--wasm component-model` flag to specify that you are running a component rather than a core module. 13 | 14 | By default, Wasmtime denies the component access to all system resources. For example, the component cannot access the file system or environment variables. See the [Wasmtime guide](https://docs.wasmtime.dev/) for information on granting access, and for other Wasmtime features. 15 | 16 | ## Running HTTP components with Wasmtime 17 | 18 | You can now execute components that implement the [HTTP proxy world](https://github.com/WebAssembly/wasi-http/blob/main/wit/proxy.wit) with the `wasmtime serve` subcommand. [The Wasmtime CLI](https://github.com/bytecodealliance/wasmtime) supports serving these components as of `v14.0.3`. 19 | 20 | To run a HTTP component with Wasmtime, execute: 21 | ```sh 22 | wasmtime serve 23 | ``` 24 | 25 | Try out building and running HTTP components with one of these tutorials 26 | 27 | 1. [Hello WASI HTTP tutorial](https://github.com/sunfishcode/hello-wasi-http) - build and serve a simple Rust-based HTTP component 28 | 29 | 2. [HTTP Auth Middleware tutorial](https://github.com/fermyon/http-auth-middleware#running-with-wasmtime) - compose a HTTP authentication middleware component with a business logic component 30 | -------------------------------------------------------------------------------- /component-model/src/tutorial.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | If you like to learn by doing, this tutorial will walk through how to build, compose, and run 4 | components through a calculator example. Calculators can conduct many operations: add, subtract, 5 | multiply, and so on. 6 | 7 | In this example, each operation will be a component, that will be composed with 8 | an `eval-expression` component that will evaluate the expression using the expected operator. With 9 | one operation per component, this calculator is exaggeratedly granular to show how independent logic 10 | of an application can be contained in a component. 11 | 12 | In production, components will likely have a larger scope than a simple mathematical operation. 13 | 14 | Our eventual solution will involve three components: 15 | 16 | 1. A calculator engine, 17 | 2. An addition operation 18 | 3. A command-line interface. 19 | 20 | Once we have built these as separate Wasm components, we will compose them into a single runnable 21 | component, and test it using the [`wasmtime` CLI][wasmtime]. 22 | 23 | [wasmtime]: https://wasmtime.dev/ 24 | 25 | ## The calculator interface 26 | 27 | For tutorial purposes, we are going to put our "calculator engine" and "addition operation" interfaces into two separate WIT packages, each containing one WIT file. 28 | 29 | This setup may seem excessive, but it illustrates a real-world use case where components come 30 | from different authors and packages. 31 | 32 | These files can be found in the component book repository in the [`examples/tutorial/wit` directory](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit) under `wit/adder/world.wit` and `wit/calculator/world.wit`: 33 | 34 | ```wit 35 | // wit/adder/world.wit 36 | package docs:adder@0.1.0; 37 | 38 | interface add { 39 | add: func(x: u32, y: u32) -> u32; 40 | } 41 | 42 | world adder { 43 | export add; 44 | } 45 | ``` 46 | 47 | ```wit 48 | // wit/calculator/world.wit 49 | package docs:calculator@0.1.0; 50 | 51 | interface calculate { 52 | enum op { 53 | add, 54 | } 55 | eval-expression: func(op: op, x: u32, y: u32) -> u32; 56 | } 57 | 58 | world calculator { 59 | export calculate; 60 | import docs:adder/add@0.1.0; 61 | } 62 | 63 | world app { 64 | import calculate; 65 | } 66 | ``` 67 | 68 | These files define: 69 | * A world `adder` that exports the `add` interface. Again, components such as the calculator can call it when 70 | they need to add numbers. 71 | * A world `calculator` describing the calculator component. This world exports the calculator interface, meaning 72 | that other components can call it to perform calculations. It imports the operation interfaces 73 | (such as `add`), meaning it relies on other components to perform those operations. 74 | * An interface `calculate` that contains an evaluate function and an enum that delineates 75 | the operations that can be involved in a calculation. In this tutorial, the only operation is `add`. 76 | * A world `app` describing the "primary" app component, which imports the `calculate` interface. 77 | This component will take in command line arguments and pass them to the `eval-expression` function 78 | of the calculator component. 79 | 80 | ## Create an `add` component 81 | 82 | Reference the [language guide](language-support.md) and [authoring components 83 | documentation](creating-and-consuming/authoring.md) to create a component that implements the 84 | `adder` world of `adder/wit/world.wit`. 85 | 86 | For reference, see the completed 87 | [example](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/adder/). 88 | 89 | ## Create a `calculator` component 90 | 91 | Reference the [language guide](language-support.md) and [authoring components 92 | documentation](creating-and-consuming/authoring.md) to create a component that implements the 93 | `calculator` world of `wit/calculator/world.wit`. 94 | 95 | For reference, see the completed 96 | [example](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/calculator/). 97 | 98 | Once complete, the component should import the `add` function from the `adder` world and call it if the `op` enum matches `add`. 99 | 100 | ## Create a `command` component 101 | 102 | A _command_ is a component with a specific export that allows it to be executed directly by 103 | `wasmtime` (or other `wasi:cli` hosts). 104 | 105 | The WebAssembly host expects it to export the [`wasi:cli/run` 106 | interface](https://github.com/WebAssembly/wasi-cli/blob/main/wit/run.wit), which is the equivalent 107 | of the [`main` function][wiki-entrypoint] to WASI. 108 | 109 | [`cargo-component`][cargo-component] will automatically resolve a Rust `bin` package 110 | with a `main` function to a component with `wasi:cli/run` exported. Scaffold a new Wasm application 111 | with a `command` component: 112 | 113 | ```console 114 | cargo component new command --command 115 | ``` 116 | 117 | This component will implement the [`app`](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/wit/calculator.wit) world, which 118 | imports the `calculate` interface. 119 | 120 | In `Cargo.toml`, point `cargo-component` to the WIT file and specify that it should pull in bindings 121 | for the `app` world from the path to `calculator.wit`: 122 | 123 | ```toml 124 | [package.metadata.component.target] 125 | path = "../wit/calculator/world.wit" 126 | world = "app" 127 | ``` 128 | Since the calculator world imports the `add` interface, the command component needs to pull in the `adder` WIT as a dependency, as well. 129 | 130 | ```toml 131 | [package.metadata.component.target.dependencies] 132 | "docs:adder" = { path = "../wit/adder" } 133 | ``` 134 | Now, implement a command line application that: 135 | 136 | 1. Takes in three arguments: two operands and the name of an operator ("1 2 add") 137 | 2. Parses the operator name and ensures it is supported in the `op` enum 138 | 3. Calls the `calculate` interface's `eval_expression`, passing in the arguments. 139 | 140 | For reference, see a completed [example](https://github.com/bytecodealliance/component-docs/tree/main/component-model/examples/tutorial/command/). 141 | 142 | [wiki-entrypoint]: https://en.wikipedia.org/wiki/Entry_point 143 | [cargo-component]: https://crates.io/crates/cargo-component 144 | 145 | ## Composing the calculator 146 | 147 | Now, we are ready to bring our components together into one runnable calculator component, using 148 | `wac`. 149 | 150 | We will: 151 | 152 | 1. Compose the calculator component with the add component to satisfy the calculator component's `adder` import 153 | 2. Compose that resolved calculator component once more with the command component to satisfy the command component's `calculate` import. 154 | 155 | The result is a fully-formed command component that has all its imports satisfied and has a single 156 | export (the `wasi:cli/run` interface), which can be executed by [`wasmtime`][wasmtime]. 157 | 158 | ```sh 159 | wac plug calculator.wasm --plug adder.wasm -o composed.wasm 160 | wac plug command.wasm --plug composed.wasm -o final.wasm 161 | ``` 162 | 163 | > If you'd prefer to take a more visual approach to composing components, see the [documentation on composing components with wasmbuilder.app](creating-and-consuming/composing.md#composing-components-with-a-visual-interface). 164 | 165 | ## Running the calculator 166 | 167 | Now it all adds up! Run the final component with the `wasmtime` CLI, ensuring you are using a 168 | [recent release][wasmtime-releases] (`v14.0.0` or greater), as earlier releases of 169 | the `wasmtime` CLI do not include component model support. 170 | 171 | ``` 172 | wasmtime run final.wasm 1 2 add 173 | 1 + 2 = 3 174 | ``` 175 | 176 | [wasmtime-releases]: https://github.com/bytecodealliance/wasmtime/releases 177 | 178 | ## To infinity and beyond! 179 | 180 | To expand the exercise to add more components, modify `calculator.wit` to add another operator world 181 | and expand the `op` enum. Then, modify the `command` and `calculator` components to support the 182 | expanded enum. 183 | 184 | Another extension of this tutorial could be to remove the `op` enum and instead modify 185 | `eval-expression` to take in a string that can then be parsed to determine which operator component 186 | to call. Maybe this parser is a component of its own?! 187 | -------------------------------------------------------------------------------- /component-model/theme/head.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | just := env_var_or_default("JUST", "just") 2 | just_dir := env_var_or_default("JUST_DIR", justfile_directory()) 3 | 4 | python := env_var_or_default("PYTHON", "python3") 5 | cargo := env_var_or_default("CARGO", "cargo") 6 | mdbook := env_var_or_default("MDBOOK", "mdbook") 7 | 8 | publish_domain := env_var_or_default("PUBLISH_DOMAIN", "component-model.bytecodealliance.org") 9 | 10 | scripts_dir := env_var_or_default("SCRIPTS_DIR", "scripts") 11 | 12 | sitemap_output_path := env_var_or_default("SITEMAP_OUTPUT_PATH", absolute_path("./component-model/book/html/sitemap.xml")) 13 | book_output_dir := env_var_or_default("BOOK_OUTPUT_DIR", "./component-model/book/html") 14 | 15 | @_default: 16 | {{just}} --list 17 | 18 | # Print the directory the book is/would be output to 19 | [group('meta')] 20 | @print-book-dir: 21 | echo -n {{book_output_dir}} 22 | 23 | # Build the book 24 | [group('build')] 25 | @build-book: 26 | {{mdbook}} build component-model 27 | 28 | # Build the sitemap 29 | [group('build')] 30 | @build-sitemap: 31 | {{python}} {{scripts_dir}}/generate_sitemap.py --domain "{{publish_domain}}" --higher-priority "design" --output-path {{sitemap_output_path}} 32 | if [ ! -f "{{book_output_dir}}/index.html" ]; then \ 33 | echo "[error] index.html @ [{{book_output_dir}}] is missing. Build or path misconfigured"; \ 34 | exit 1; \ 35 | fi 36 | -------------------------------------------------------------------------------- /scripts/generate_sitemap.py: -------------------------------------------------------------------------------- 1 | import os 2 | from urllib.parse import urljoin 3 | from datetime import datetime 4 | from pathlib import Path 5 | 6 | import argparse 7 | 8 | def parse_summary(summary_file_path): 9 | """Parse URLs from the SUMMARY.md file.""" 10 | with open(summary_file_path, "r") as file: 11 | for line in file: 12 | if "](" in line: 13 | url = line.split("](")[1].split(")")[0] 14 | # Add .html extension if not the root URL 15 | if url.endswith(".md"): 16 | url = url[:-3] + ".html" 17 | yield url 18 | 19 | def determine_priority(url_path, higher_priority_section): 20 | """Determine the priority based on the URL path and specified higher priority section.""" 21 | if url_path.count("/") <= 1: # Pages directly under the base URL 22 | return "1.0" 23 | elif higher_priority_section and url_path.startswith(f"./{higher_priority_section}"): # Pages in the specified higher priority section 24 | return "0.8" 25 | else: 26 | return "0.5" # All other pages 27 | 28 | def generate_sitemap(domain, output_path, summary_file_path, higher_priority_section): 29 | """Generate a sitemap XML file from SUMMARY.md structure.""" 30 | domain = "https://" + domain 31 | urls = parse_summary(summary_file_path) # Add base URL to the list of URLs 32 | urls = [""] + list(urls) 33 | 34 | sitemap = '\n' 35 | sitemap += '\n' 36 | 37 | for url in urls: 38 | full_url = urljoin(domain, url) 39 | priority = determine_priority(url, higher_priority_section) 40 | 41 | sitemap += " \n" 42 | sitemap += f" {full_url}\n" 43 | sitemap += " weekly\n" 44 | sitemap += f" {priority}\n" 45 | sitemap += " \n" 46 | 47 | sitemap += "" 48 | 49 | # Write the sitemap to the specified output path 50 | with open(output_path, "w") as file: 51 | file.write(sitemap) 52 | 53 | DEFAULT_SUMMARY_MD_PATH = (Path(__file__).parent / "../component-model/src/SUMMARY.md").resolve() 54 | DEFAULT_SITEMAP_XML_PATH = (Path(__file__).parent / "../component-model/book/html/sitemap.sml").resolve() 55 | 56 | if __name__ == "__main__": 57 | parser = argparse.ArgumentParser(description="Generate a sitemap for mdBook") 58 | parser.add_argument("-d", "--domain", required=True, help="Domain for the mdBook site (e.g., 'component-model.bytecodealliance.org')") 59 | parser.add_argument("-o", "--output-path", default=DEFAULT_SITEMAP_XML_PATH, help="Output path for the sitemap file") 60 | parser.add_argument("-s", "--summary-md-path", default=DEFAULT_SUMMARY_MD_PATH, help="Path to SUMMARY.md") 61 | parser.add_argument("-p", "--higher-priority", help="Subsection path (e.g., 'design') to assign a higher priority of 0.8") 62 | args = parser.parse_args() 63 | 64 | summary_file_path = Path(args.summary_md_path).resolve() 65 | if not summary_file_path.exists(): 66 | raise FileNotFoundError(f"failed to find summary file [{summary_file_path}]") 67 | 68 | output_path = Path(args.output_path).resolve() 69 | if not output_path.parent.exists(): 70 | raise FileNotFoundError(f"failed to find output dir [{output_path.parent}]") 71 | 72 | generate_sitemap(args.domain, output_path, summary_file_path, args.higher_priority) 73 | --------------------------------------------------------------------------------