From d573f07c9811248c46ab29a7efbeea5e4ee93cec Mon Sep 17 00:00:00 2001 From: Lorenzo Venerandi Date: Tue, 7 Jan 2025 23:01:06 +0100 Subject: [PATCH] project init --- .gitignore | 3 + Dockerfile | 51 ++ README.md | 57 ++ go.mod | 25 + go.sum | 52 ++ main.go | 81 +++ task.go | 12 + tools.go | 8 + wadm.yaml | 87 +++ wasmcloud.toml | 13 + wit/deps/wasi-cli-0.2.0/package.wit | 58 ++ wit/deps/wasi-clocks-0.2.0/package.wit | 29 + wit/deps/wasi-config-0.2.0-draft/package.wit | 28 + wit/deps/wasi-filesystem-0.2.0/package.wit | 158 +++++ wit/deps/wasi-http-0.2.0/package.wit | 571 ++++++++++++++++++ wit/deps/wasi-io-0.2.0/package.wit | 48 ++ wit/deps/wasi-logging-0.1.0-draft/package.wit | 36 ++ wit/deps/wasi-random-0.2.0/package.wit | 18 + wit/deps/wasi-sockets-0.2.0/package.wit | 179 ++++++ wit/deps/wasmcloud-bus-1.0.0/package.wit | 10 + .../wasmcloud-component-go-0.1.0/package.wit | 85 +++ .../wasmcloud-messaging-0.2.0/package.wit | 29 + .../wasmcloud-secrets-0.1.0-draft/package.wit | 25 + wit/world.wit | 12 + 24 files changed, 1675 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 task.go create mode 100644 tools.go create mode 100644 wadm.yaml create mode 100644 wasmcloud.toml create mode 100644 wit/deps/wasi-cli-0.2.0/package.wit create mode 100644 wit/deps/wasi-clocks-0.2.0/package.wit create mode 100644 wit/deps/wasi-config-0.2.0-draft/package.wit create mode 100644 wit/deps/wasi-filesystem-0.2.0/package.wit create mode 100644 wit/deps/wasi-http-0.2.0/package.wit create mode 100644 wit/deps/wasi-io-0.2.0/package.wit create mode 100644 wit/deps/wasi-logging-0.1.0-draft/package.wit create mode 100644 wit/deps/wasi-random-0.2.0/package.wit create mode 100644 wit/deps/wasi-sockets-0.2.0/package.wit create mode 100644 wit/deps/wasmcloud-bus-1.0.0/package.wit create mode 100644 wit/deps/wasmcloud-component-go-0.1.0/package.wit create mode 100644 wit/deps/wasmcloud-messaging-0.2.0/package.wit create mode 100644 wit/deps/wasmcloud-secrets-0.1.0-draft/package.wit create mode 100644 wit/world.wit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..69050c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/* + +creds.json \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..94542c7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,51 @@ +FROM ubuntu:24.04 AS wash-build-image + +# Install dependencies and tools +RUN apt-get update && apt-get install -y \ + curl \ + wget \ + tar \ + git \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# ----------------- Install WasmCloud ----------------- +RUN curl -s "https://packagecloud.io/install/repositories/wasmcloud/core/script.deb.sh" | bash && \ + apt-get install -y wash + +# ----------------- Install Go 1.23 ----------------- +RUN wget https://go.dev/dl/go1.23.4.linux-amd64.tar.gz && \ + tar -C /usr/local -xzf go1.23.4.linux-amd64.tar.gz && \ + rm go1.23.4.linux-amd64.tar.gz + +# Set Go environment variables +ENV PATH="/usr/local/go/bin:${PATH}" +ENV GOPATH="/go" +ENV GOROOT="/usr/local/go" + +# ----------------- Install TinyGo 0.34.0 ----------------- +RUN wget https://github.com/tinygo-org/tinygo/releases/download/v0.34.0/tinygo_0.34.0_amd64.deb && \ + dpkg -i tinygo_0.34.0_amd64.deb && \ + rm tinygo_0.34.0_amd64.deb + +# ----------------- Install Rust ----------------- +# Install Rust +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y && \ + . "$HOME/.cargo/env" && \ + cargo install --locked wasm-tools + +# Set Rust environment variables +ENV PATH="/root/.cargo/bin:${PATH}" + +# Verify installations +RUN go version && tinygo version && cargo --version && wash --version && wasm-tools --version + + +# ----------------- Build the WasmCloud module ----------------- +FROM wash-build-image + +RUN mkdir /app +WORKDIR /app + +# Install go dependencies, build the wasm module, push it to the registry +CMD ["sh", "-c", "go env -w GOFLAGS=-buildvcs=false && go mod download && go mod verify && wash build && wash push $REGISTRY build/*.wasm"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1b1341e --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# Prerequisites + +- `go` 1.23 +- `tinygo` 0.33 +- [`wash`](https://wasmcloud.com/docs/installation) 0.35.0 +- `wasmtime` 25.0.0 (if running with wasmtime) + +## Building + +```bash +wash build +``` + +## Push OCI Artifact to Gitea +```bash +wash push gitea.rebus.ninja/lore/go-nats-client:1.x.x build/go_nats_client_s.wasm +``` + +## Build and push to the registry using the dockerfile + +### Build the cointainer +```bash +docker build -t wash-image-build . +``` +### Build the component and push to the registry + +First setup your credentials +```bash +export REG_USER=user && export REG_PASS=pass +``` + +Build and push +```bash +docker run --rm -e REGISTRY=gitea.rebus.ninja/lore/wasm-nats-sqldb-sync:1.0.x -e WASH_REG_USER=$REG_USER -e WASH_REG_PASSWORD=$REG_PASS -v "$(pwd):/app" wash-image-build +``` + +## Running with wasmtime + +You must have wasmtime 25.0.0 for this to work. Make sure to follow the build step above first. + +```bash +wasmtime serve -Scommon ./build/http_hello_world_s.wasm +``` + +## Running with wasmCloud + +Make sure to follow the build steps above, and replace the file path in [the wadm manifest](./wadm.yaml) with the absolute path to your local built component. + +```shell +wash up -d +wash app deploy ./wadm.yaml +curl http://localhost:8000 +``` + +## Adding Capabilities + +To learn how to extend this example with additional capabilities, see the [Adding Capabilities](https://wasmcloud.com/docs/tour/adding-capabilities?lang=tinygo) section of the wasmCloud documentation. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..81f282d --- /dev/null +++ b/go.mod @@ -0,0 +1,25 @@ +module gitea.rebus.ninja/lore/wasm-nats-producer-client + +go 1.23.0 + +require ( + github.com/bytecodealliance/wasm-tools-go v0.3.2 + go.wasmcloud.dev/component v0.0.5 + go.wasmcloud.dev/wadge v0.7.0 +) + +require ( + github.com/coreos/go-semver v0.3.1 // indirect + github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/regclient/regclient v0.8.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/ulikunitz/xz v0.5.12 // indirect + github.com/urfave/cli/v3 v3.0.0-beta1 // indirect + go.bytecodealliance.org v0.4.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/tools v0.28.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ae7ead7 --- /dev/null +++ b/go.sum @@ -0,0 +1,52 @@ +github.com/bytecodealliance/wasm-tools-go v0.3.2 h1:LKni9PS8yCG5/A79L8tcTKthgf7WN5RZD83W1m6wEE0= +github.com/bytecodealliance/wasm-tools-go v0.3.2/go.mod h1:fdysX1+SiPxcIhdpg8TLhoxz23k28/5cQ0/L9J4mgig= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/olareg/olareg v0.1.1 h1:Ui7q93zjcoF+U9U71sgqgZWByDoZOpqHitUXEu2xV+g= +github.com/olareg/olareg v0.1.1/go.mod h1:w8NP4SWrHHtxsFaUiv1lnCnYPm4sN1seCd2h7FK/dc0= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/regclient/regclient v0.8.0 h1:xNAMDlADcyMvFAlGXoqDOxlSUBG4mqWBFgjQqVTP8Og= +github.com/regclient/regclient v0.8.0/go.mod h1:h9+Y6dBvqBkdlrj6EIhbTOv0xUuIFl7CdI1bZvEB42g= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= +github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg= +github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= +go.bytecodealliance.org v0.4.0 h1:SRwgZIcXR54AmbJg9Y3AMgDlZlvD8dffteBYW+nCD3k= +go.bytecodealliance.org v0.4.0/go.mod h1:hkdjfgQ/bFZYUucnm9cn0Q8/SHO3iT0rzskYlkV4Jy0= +go.wasmcloud.dev/component v0.0.5 h1:z9+fq1CJKm/yom33ctd5wfo8TVunm5IaI7EJdY2i9hk= +go.wasmcloud.dev/component v0.0.5/go.mod h1:PvVHQ7Xp8D9kZnVB3fm8lqWAcC6Yxd7CI/snXZUAG8E= +go.wasmcloud.dev/wadge v0.7.0 h1:eUt0jODh6xQ5HEof1PFSgDp+evrNs+lD1LYCYeRR2Cg= +go.wasmcloud.dev/wadge v0.7.0/go.mod h1:SMnPSWZFTkXyUX0GJ11mcdc7ZoMITtbAlPLlpvGJd4M= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..de87d42 --- /dev/null +++ b/main.go @@ -0,0 +1,81 @@ +//go:generate go run github.com/bytecodealliance/wasm-tools-go/cmd/wit-bindgen-go generate --world hello --out gen ./wit +package main + +import ( + "fmt" + "net/http" + + logger "gitea.rebus.ninja/lore/wasm-nats-producer-client/gen/wasi/logging/logging" + "gitea.rebus.ninja/lore/wasm-nats-producer-client/gen/wasmcloud/messaging/consumer" + "gitea.rebus.ninja/lore/wasm-nats-producer-client/gen/wasmcloud/messaging/handler" + "gitea.rebus.ninja/lore/wasm-nats-producer-client/gen/wasmcloud/messaging/types" + "github.com/bytecodealliance/wasm-tools-go/cm" + "go.wasmcloud.dev/component/net/wasihttp" +) + +type messagingConsumerAdapter struct { + Publish func(msg types.BrokerMessage) (result cm.Result[string, struct{}, string]) +} + +// NOTE(lxf): this is overridden in tests +var messagingConsumer = &messagingConsumerAdapter{ + Publish: consumer.Publish, +} + + +func init() { + wasihttp.HandleFunc(handleHttp) + handler.Exports.HandleMessage = handleMessage +} + +func handleHttp(w http.ResponseWriter, r *http.Request) { + + // get body as string + if handleRequest(r.FormValue("data")) { + fmt.Fprintf(w, "Message sent!\n") + } else { + fmt.Fprintf(w, "Error\n") + } + + // send response +} + +func handleMessage(msg types.BrokerMessage) cm.Result[string, struct{}, string]{ + + logger.Log(logger.LevelInfo,"MessageHandler", "Received message on subject" + msg.Subject) + + arg := cm.LiftString[string, *uint8, uint8](msg.Body.Data(), uint8(msg.Body.Len())) + + if handleRequest(arg) { + cm.OK[cm.Result[string, struct{}, string]](struct{}{}) + } + + return cm.Err[cm.Result[string, struct{}, string]]("Couldn't send message to topic") +} + +func handleRequest(arg string) bool { + + // TODO implement the logic to get the destination topic from the config + // dest_topic := config.GetAll() + dest_topic := "streaming" + + result := exec_task(arg) + + // Send reply + reply := types.BrokerMessage{ + Subject: dest_topic, + Body: cm.ToList([]byte(result)), + ReplyTo: cm.None[string](), + } + res := messagingConsumer.Publish(reply) + if res.IsErr() { + logger.Log(logger.LevelError, "MessageHandler", "Failed to send reply, error: " + *res.Err()) + return false + } + + return true +} + +// Since we don't run this program like a CLI, the `main` function is empty. Instead, +// we call the `handleRequest` function when an HTTP request is received. +func main() {} diff --git a/task.go b/task.go new file mode 100644 index 0000000..b778dc4 --- /dev/null +++ b/task.go @@ -0,0 +1,12 @@ +package main + +import ( + +) + +func exec_task(arg string) string{ + + response := "" + arg + + return response +} \ No newline at end of file diff --git a/tools.go b/tools.go new file mode 100644 index 0000000..2b8df33 --- /dev/null +++ b/tools.go @@ -0,0 +1,8 @@ +//go:build tools + +package main + +import ( + _ "github.com/bytecodealliance/wasm-tools-go/cmd/wit-bindgen-go" + _ "go.wasmcloud.dev/wadge/cmd/wadge-bindgen-go" +) diff --git a/wadm.yaml b/wadm.yaml new file mode 100644 index 0000000..f42f4b6 --- /dev/null +++ b/wadm.yaml @@ -0,0 +1,87 @@ +apiVersion: core.oam.dev/v1beta1 +kind: Application +metadata: + name: go-sqldb-sync + annotations: + description: 'Webassembly component that syncs a NATS topic data to a database table (Postgres)' + wasmcloud.dev/authors: Lorenzo Venerandi + wasmcloud.dev/source-url: https://gitea.rebus.ninja/lore/wasm-nats-sqldb-sync.git/wadm.yaml + wasmcloud.dev/readme-md-url: https://gitea.rebus.ninja/lore/wasm-nats-sqldb-sync.git/README.md + wasmcloud.dev/homepage: https://gitea.rebus.ninja/lore/wasm-nats-sqldb-sync.git + wasmcloud.dev/categories: | + sqldb,postgres,nats-client,tinygo,golang +spec: + components: + - name: go_sqldb_sync + type: component + properties: + image: gitea.rebus.ninja/lore/wasm-nats-sqldb-sync:1.0.0 + id: producer + # config: + # - name: nats-topic + # properties: + # dest-topic: wasmcloud.echo.reply + traits: + - type: spreadscaler + properties: + instances: 1 + spread: + - name: cloud + weight: 0 + requirements: + host-type: cloud + - name: edge + weight: 100 + requirements: + host-type: edge + - type: link + properties: + target: + name: sqldb-postgres + config: + - name: default-postgres + namespace: wasmcloud + package: postgres + interfaces: [query] + + # Add a capability provider that implements `wasmcloud:messaging` using NATS + - name: nats-producer + type: capability + properties: + image: ghcr.io/wasmcloud/messaging-nats:0.23.1 + traits: + - type: link + properties: + target: go_data_producer + namespace: wasmcloud + package: messaging + interfaces: [handler] + source_config: + - name: simple-subscription + properties: + subscriptions: producer + - type: spreadscaler + properties: + instances: 1 + spread: + - name: cloud + weight: 0 + requirements: + host-type: cloud + - name: edge + weight: 100 + requirements: + host-type: edge + - name: sqldb-postgres + type: capability + properties: + image: ghcr.io/wasmcloud/sqldb-postgres:0.7.2 + config: + - name: default-postgres + properties: + POSTGRES_HOST: localhost + POSTGRES_PORT: 5432 + POSTGRES_USERNAME: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DATABASE: postgres + POSTGRES_TLS_REQUIRED: false \ No newline at end of file diff --git a/wasmcloud.toml b/wasmcloud.toml new file mode 100644 index 0000000..eafa61a --- /dev/null +++ b/wasmcloud.toml @@ -0,0 +1,13 @@ +name = "go_nats_sqldb_sync" +language = "tinygo" +type = "component" +version = "1.0.0" + +[component] +wit_world = "hello" +wasm_target = "wasm32-wasip2" +destination = "build/go_nats_sqldb_sync.wasm" + +[registry] +url = "gitea.rebus.ninja/lore" +credentials = "./creds.json" \ No newline at end of file diff --git a/wit/deps/wasi-cli-0.2.0/package.wit b/wit/deps/wasi-cli-0.2.0/package.wit new file mode 100644 index 0000000..f8f1976 --- /dev/null +++ b/wit/deps/wasi-cli-0.2.0/package.wit @@ -0,0 +1,58 @@ +package wasi:cli@0.2.0; + +interface stdout { + use wasi:io/streams@0.2.0.{output-stream}; + + get-stdout: func() -> output-stream; +} + +interface stderr { + use wasi:io/streams@0.2.0.{output-stream}; + + get-stderr: func() -> output-stream; +} + +interface stdin { + use wasi:io/streams@0.2.0.{input-stream}; + + get-stdin: func() -> input-stream; +} + +interface environment { + get-environment: func() -> list>; + + get-arguments: func() -> list; + + initial-cwd: func() -> option; +} + +interface exit { + exit: func(status: result); +} + +interface terminal-input { + resource terminal-input; +} + +interface terminal-output { + resource terminal-output; +} + +interface terminal-stdin { + use terminal-input.{terminal-input}; + + get-terminal-stdin: func() -> option; +} + +interface terminal-stdout { + use terminal-output.{terminal-output}; + + get-terminal-stdout: func() -> option; +} + +interface terminal-stderr { + use terminal-output.{terminal-output}; + + get-terminal-stderr: func() -> option; +} + diff --git a/wit/deps/wasi-clocks-0.2.0/package.wit b/wit/deps/wasi-clocks-0.2.0/package.wit new file mode 100644 index 0000000..9e0ba3d --- /dev/null +++ b/wit/deps/wasi-clocks-0.2.0/package.wit @@ -0,0 +1,29 @@ +package wasi:clocks@0.2.0; + +interface monotonic-clock { + use wasi:io/poll@0.2.0.{pollable}; + + type instant = u64; + + type duration = u64; + + now: func() -> instant; + + resolution: func() -> duration; + + subscribe-instant: func(when: instant) -> pollable; + + subscribe-duration: func(when: duration) -> pollable; +} + +interface wall-clock { + record datetime { + seconds: u64, + nanoseconds: u32, + } + + now: func() -> datetime; + + resolution: func() -> datetime; +} + diff --git a/wit/deps/wasi-config-0.2.0-draft/package.wit b/wit/deps/wasi-config-0.2.0-draft/package.wit new file mode 100644 index 0000000..7065b9b --- /dev/null +++ b/wit/deps/wasi-config-0.2.0-draft/package.wit @@ -0,0 +1,28 @@ +package wasi:config@0.2.0-draft; + +interface runtime { + /// An error type that encapsulates the different errors that can occur fetching config + variant config-error { + /// This indicates an error from an "upstream" config source. + /// As this could be almost _anything_ (such as Vault, Kubernetes ConfigMaps, KeyValue buckets, etc), + /// the error message is a string. + upstream(string), + /// This indicates an error from an I/O operation. + /// As this could be almost _anything_ (such as a file read, network connection, etc), + /// the error message is a string. + /// Depending on how this ends up being consumed, + /// we may consider moving this to use the `wasi:io/error` type instead. + /// For simplicity right now in supporting multiple implementations, it is being left as a string. + io(string), + } + + /// Gets a single opaque config value set at the given key if it exists + get: func(key: string) -> result, config-error>; + + /// Gets a list of all set config data + get-all: func() -> result>, config-error>; +} + +world imports { + import runtime; +} diff --git a/wit/deps/wasi-filesystem-0.2.0/package.wit b/wit/deps/wasi-filesystem-0.2.0/package.wit new file mode 100644 index 0000000..cb6a2be --- /dev/null +++ b/wit/deps/wasi-filesystem-0.2.0/package.wit @@ -0,0 +1,158 @@ +package wasi:filesystem@0.2.0; + +interface types { + use wasi:io/streams@0.2.0.{input-stream, output-stream, error}; + use wasi:clocks/wall-clock@0.2.0.{datetime}; + + type filesize = u64; + + enum descriptor-type { + unknown, + block-device, + character-device, + directory, + fifo, + symbolic-link, + regular-file, + socket, + } + + flags descriptor-flags { + read, + write, + file-integrity-sync, + data-integrity-sync, + requested-write-sync, + mutate-directory, + } + + flags path-flags { + symlink-follow, + } + + flags open-flags { + create, + directory, + exclusive, + truncate, + } + + type link-count = u64; + + record descriptor-stat { + %type: descriptor-type, + link-count: link-count, + size: filesize, + data-access-timestamp: option, + data-modification-timestamp: option, + status-change-timestamp: option, + } + + variant new-timestamp { + no-change, + now, + timestamp(datetime), + } + + record directory-entry { + %type: descriptor-type, + name: string, + } + + enum error-code { + access, + would-block, + already, + bad-descriptor, + busy, + deadlock, + quota, + exist, + file-too-large, + illegal-byte-sequence, + in-progress, + interrupted, + invalid, + io, + is-directory, + loop, + too-many-links, + message-size, + name-too-long, + no-device, + no-entry, + no-lock, + insufficient-memory, + insufficient-space, + not-directory, + not-empty, + not-recoverable, + unsupported, + no-tty, + no-such-device, + overflow, + not-permitted, + pipe, + read-only, + invalid-seek, + text-file-busy, + cross-device, + } + + enum advice { + normal, + sequential, + random, + will-need, + dont-need, + no-reuse, + } + + record metadata-hash-value { + lower: u64, + upper: u64, + } + + resource descriptor { + read-via-stream: func(offset: filesize) -> result; + write-via-stream: func(offset: filesize) -> result; + append-via-stream: func() -> result; + advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; + sync-data: func() -> result<_, error-code>; + get-flags: func() -> result; + get-type: func() -> result; + set-size: func(size: filesize) -> result<_, error-code>; + set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + read: func(length: filesize, offset: filesize) -> result, bool>, error-code>; + write: func(buffer: list, offset: filesize) -> result; + read-directory: func() -> result; + sync: func() -> result<_, error-code>; + create-directory-at: func(path: string) -> result<_, error-code>; + stat: func() -> result; + stat-at: func(path-flags: path-flags, path: string) -> result; + set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; + readlink-at: func(path: string) -> result; + remove-directory-at: func(path: string) -> result<_, error-code>; + rename-at: func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>; + unlink-file-at: func(path: string) -> result<_, error-code>; + is-same-object: func(other: borrow) -> bool; + metadata-hash: func() -> result; + metadata-hash-at: func(path-flags: path-flags, path: string) -> result; + } + + resource directory-entry-stream { + read-directory-entry: func() -> result, error-code>; + } + + filesystem-error-code: func(err: borrow) -> option; +} + +interface preopens { + use types.{descriptor}; + + get-directories: func() -> list>; +} + diff --git a/wit/deps/wasi-http-0.2.0/package.wit b/wit/deps/wasi-http-0.2.0/package.wit new file mode 100644 index 0000000..11f7ff4 --- /dev/null +++ b/wit/deps/wasi-http-0.2.0/package.wit @@ -0,0 +1,571 @@ +package wasi:http@0.2.0; + +/// This interface defines all of the types and methods for implementing +/// HTTP Requests and Responses, both incoming and outgoing, as well as +/// their headers, trailers, and bodies. +interface types { + use wasi:clocks/monotonic-clock@0.2.0.{duration}; + use wasi:io/streams@0.2.0.{input-stream, output-stream}; + use wasi:io/error@0.2.0.{error as io-error}; + use wasi:io/poll@0.2.0.{pollable}; + + /// This type corresponds to HTTP standard Methods. + variant method { + get, + head, + post, + put, + delete, + connect, + options, + trace, + patch, + other(string), + } + + /// This type corresponds to HTTP standard Related Schemes. + variant scheme { + HTTP, + HTTPS, + other(string), + } + + /// Defines the case payload type for `DNS-error` above: + record DNS-error-payload { + rcode: option, + info-code: option, + } + + /// Defines the case payload type for `TLS-alert-received` above: + record TLS-alert-received-payload { + alert-id: option, + alert-message: option, + } + + /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + record field-size-payload { + field-name: option, + field-size: option, + } + + /// These cases are inspired by the IANA HTTP Proxy Error Types: + /// https://www.iana.org/assignments/http-proxy-status/http-proxy-status.xhtml#table-http-proxy-error-types + variant error-code { + DNS-timeout, + DNS-error(DNS-error-payload), + destination-not-found, + destination-unavailable, + destination-IP-prohibited, + destination-IP-unroutable, + connection-refused, + connection-terminated, + connection-timeout, + connection-read-timeout, + connection-write-timeout, + connection-limit-reached, + TLS-protocol-error, + TLS-certificate-error, + TLS-alert-received(TLS-alert-received-payload), + HTTP-request-denied, + HTTP-request-length-required, + HTTP-request-body-size(option), + HTTP-request-method-invalid, + HTTP-request-URI-invalid, + HTTP-request-URI-too-long, + HTTP-request-header-section-size(option), + HTTP-request-header-size(option), + HTTP-request-trailer-section-size(option), + HTTP-request-trailer-size(field-size-payload), + HTTP-response-incomplete, + HTTP-response-header-section-size(option), + HTTP-response-header-size(field-size-payload), + HTTP-response-body-size(option), + HTTP-response-trailer-section-size(option), + HTTP-response-trailer-size(field-size-payload), + HTTP-response-transfer-coding(option), + HTTP-response-content-coding(option), + HTTP-response-timeout, + HTTP-upgrade-failed, + HTTP-protocol-error, + loop-detected, + configuration-error, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. It also includes an optional string for an + /// unstructured description of the error. Users should not depend on the + /// string for diagnosing errors, as it's not required to be consistent + /// between implementations. + internal-error(option), + } + + /// This type enumerates the different kinds of errors that may occur when + /// setting or appending to a `fields` resource. + variant header-error { + /// This error indicates that a `field-key` or `field-value` was + /// syntactically invalid when used with an operation that sets headers in a + /// `fields`. + invalid-syntax, + /// This error indicates that a forbidden `field-key` was used when trying + /// to set a header in a `fields`. + forbidden, + /// This error indicates that the operation on the `fields` was not + /// permitted because the fields are immutable. + immutable, + } + + /// Field keys are always strings. + type field-key = string; + + /// Field values should always be ASCII strings. However, in + /// reality, HTTP implementations often have to interpret malformed values, + /// so they are provided as a list of bytes. + type field-value = list; + + /// This following block defines the `fields` resource which corresponds to + /// HTTP standard Fields. Fields are a common representation used for both + /// Headers and Trailers. + /// + /// A `fields` may be mutable or immutable. A `fields` created using the + /// constructor, `from-list`, or `clone` will be mutable, but a `fields` + /// resource given by other means (including, but not limited to, + /// `incoming-request.headers`, `outgoing-request.headers`) might be be + /// immutable. In an immutable fields, the `set`, `append`, and `delete` + /// operations will fail with `header-error.immutable`. + resource fields { + /// Construct an empty HTTP Fields. + /// + /// The resulting `fields` is mutable. + constructor(); + /// Construct an HTTP Fields. + /// + /// The resulting `fields` is mutable. + /// + /// The list represents each key-value pair in the Fields. Keys + /// which have multiple values are represented by multiple entries in this + /// list with the same key. + /// + /// The tuple is a pair of the field key, represented as a string, and + /// Value, represented as a list of bytes. In a valid Fields, all keys + /// and values are valid UTF-8 strings. However, values are not always + /// well-formed, so they are represented as a raw list of bytes. + /// + /// An error result will be returned if any header or value was + /// syntactically invalid, or if a header was forbidden. + from-list: static func(entries: list>) -> result; + /// Get all of the values corresponding to a key. If the key is not present + /// in this `fields`, an empty list is returned. However, if the key is + /// present but empty, this is represented by a list with one or more + /// empty field-values present. + get: func(name: field-key) -> list; + /// Returns `true` when the key is present in this `fields`. If the key is + /// syntactically invalid, `false` is returned. + has: func(name: field-key) -> bool; + /// Set all of the values for a key. Clears any existing values for that + /// key, if they have been set. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + set: func(name: field-key, value: list) -> result<_, header-error>; + /// Delete all values for a key. Does nothing if no values for the key + /// exist. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + delete: func(name: field-key) -> result<_, header-error>; + /// Append a value for a key. Does not change or delete any existing + /// values for that key. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + append: func(name: field-key, value: field-value) -> result<_, header-error>; + /// Retrieve the full set of keys and values in the Fields. Like the + /// constructor, the list represents each key-value pair. + /// + /// The outer list represents each key-value pair in the Fields. Keys + /// which have multiple values are represented by multiple entries in this + /// list with the same key. + entries: func() -> list>; + /// Make a deep copy of the Fields. Equivelant in behavior to calling the + /// `fields` constructor on the return value of `entries`. The resulting + /// `fields` is mutable. + clone: func() -> fields; + } + + /// Headers is an alias for Fields. + type headers = fields; + + /// Trailers is an alias for Fields. + type trailers = fields; + + /// Represents an incoming HTTP Request. + resource incoming-request { + /// Returns the method of the incoming request. + method: func() -> method; + /// Returns the path with query parameters from the request, as a string. + path-with-query: func() -> option; + /// Returns the protocol scheme from the request. + scheme: func() -> option; + /// Returns the authority from the request, if it was present. + authority: func() -> option; + /// Get the `headers` associated with the request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// The `headers` returned are a child resource: it must be dropped before + /// the parent `incoming-request` is dropped. Dropping this + /// `incoming-request` before all children are dropped will trap. + headers: func() -> headers; + /// Gives the `incoming-body` associated with this request. Will only + /// return success at most once, and subsequent calls will return error. + consume: func() -> result; + } + + /// Represents an outgoing HTTP Request. + resource outgoing-request { + /// Construct a new `outgoing-request` with a default `method` of `GET`, and + /// `none` values for `path-with-query`, `scheme`, and `authority`. + /// + /// * `headers` is the HTTP Headers for the Request. + /// + /// It is possible to construct, or manipulate with the accessor functions + /// below, an `outgoing-request` with an invalid combination of `scheme` + /// and `authority`, or `headers` which are not permitted to be sent. + /// It is the obligation of the `outgoing-handler.handle` implementation + /// to reject invalid constructions of `outgoing-request`. + constructor(headers: headers); + /// Returns the resource corresponding to the outgoing Body for this + /// Request. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-request` can be retrieved at most once. Subsequent + /// calls will return error. + body: func() -> result; + /// Get the Method for the Request. + method: func() -> method; + /// Set the Method for the Request. Fails if the string present in a + /// `method.other` argument is not a syntactically valid method. + set-method: func(method: method) -> result; + /// Get the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. + path-with-query: func() -> option; + /// Set the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. Fails is the + /// string given is not a syntactically valid path and query uri component. + set-path-with-query: func(path-with-query: option) -> result; + /// Get the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. + scheme: func() -> option; + /// Set the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. Fails if the + /// string given is not a syntactically valid uri scheme. + set-scheme: func(scheme: option) -> result; + /// Get the HTTP Authority for the Request. A value of `none` may be used + /// with Related Schemes which do not require an Authority. The HTTP and + /// HTTPS schemes always require an authority. + authority: func() -> option; + /// Set the HTTP Authority for the Request. A value of `none` may be used + /// with Related Schemes which do not require an Authority. The HTTP and + /// HTTPS schemes always require an authority. Fails if the string given is + /// not a syntactically valid uri authority. + set-authority: func(authority: option) -> result; + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transfered to + /// another component by e.g. `outgoing-handler.handle`. + headers: func() -> headers; + } + + /// Parameters for making an HTTP Request. Each of these parameters is + /// currently an optional timeout applicable to the transport layer of the + /// HTTP protocol. + /// + /// These timeouts are separate from any the user may use to bound a + /// blocking call to `wasi:io/poll.poll`. + resource request-options { + /// Construct a default `request-options` value. + constructor(); + /// The timeout for the initial connect to the HTTP Server. + connect-timeout: func() -> option; + /// Set the timeout for the initial connect to the HTTP Server. An error + /// return value indicates that this timeout is not supported. + set-connect-timeout: func(duration: option) -> result; + /// The timeout for receiving the first byte of the Response body. + first-byte-timeout: func() -> option; + /// Set the timeout for receiving the first byte of the Response body. An + /// error return value indicates that this timeout is not supported. + set-first-byte-timeout: func(duration: option) -> result; + /// The timeout for receiving subsequent chunks of bytes in the Response + /// body stream. + between-bytes-timeout: func() -> option; + /// Set the timeout for receiving subsequent chunks of bytes in the Response + /// body stream. An error return value indicates that this timeout is not + /// supported. + set-between-bytes-timeout: func(duration: option) -> result; + } + + /// Represents the ability to send an HTTP Response. + /// + /// This resource is used by the `wasi:http/incoming-handler` interface to + /// allow a Response to be sent corresponding to the Request provided as the + /// other argument to `incoming-handler.handle`. + resource response-outparam { + /// Set the value of the `response-outparam` to either send a response, + /// or indicate an error. + /// + /// This method consumes the `response-outparam` to ensure that it is + /// called at most once. If it is never called, the implementation + /// will respond with an error. + /// + /// The user may provide an `error` to `response` to allow the + /// implementation determine how to respond with an HTTP error response. + set: static func(param: response-outparam, response: result); + } + + /// This type corresponds to the HTTP standard Status Code. + type status-code = u16; + + /// Represents an incoming HTTP Response. + resource incoming-response { + /// Returns the status code from the incoming response. + status: func() -> status-code; + /// Returns the headers from the incoming response. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `incoming-response` is dropped. + headers: func() -> headers; + /// Returns the incoming body. May be called at most once. Returns error + /// if called additional times. + consume: func() -> result; + } + + /// Represents an incoming HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, indicating that the full contents of the + /// body have been received. This resource represents the contents as + /// an `input-stream` and the delivery of trailers as a `future-trailers`, + /// and ensures that the user of this interface may only be consuming either + /// the body contents or waiting on trailers at any given time. + resource incoming-body { + /// Returns the contents of the body, as a stream of bytes. + /// + /// Returns success on first call: the stream representing the contents + /// can be retrieved at most once. Subsequent calls will return error. + /// + /// The returned `input-stream` resource is a child: it must be dropped + /// before the parent `incoming-body` is dropped, or consumed by + /// `incoming-body.finish`. + /// + /// This invariant ensures that the implementation can determine whether + /// the user is consuming the contents of the body, waiting on the + /// `future-trailers` to be ready, or neither. This allows for network + /// backpressure is to be applied when the user is consuming the body, + /// and for that backpressure to not inhibit delivery of the trailers if + /// the user does not read the entire body. + %stream: func() -> result; + /// Takes ownership of `incoming-body`, and returns a `future-trailers`. + /// This function will trap if the `input-stream` child is still alive. + finish: static func(this: incoming-body) -> future-trailers; + } + + /// Represents a future which may eventaully return trailers, or an error. + /// + /// In the case that the incoming HTTP Request or Response did not have any + /// trailers, this future will resolve to the empty set of trailers once the + /// complete Request or Response body has been received. + resource future-trailers { + /// Returns a pollable which becomes ready when either the trailers have + /// been received, or an error has occured. When this pollable is ready, + /// the `get` method will return `some`. + subscribe: func() -> pollable; + /// Returns the contents of the trailers, or an error which occured, + /// once the future is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the trailers or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the HTTP Request or Response + /// body, as well as any trailers, were received successfully, or that an + /// error occured receiving them. The optional `trailers` indicates whether + /// or not trailers were present in the body. + /// + /// When some `trailers` are returned by this method, the `trailers` + /// resource is immutable, and a child. Use of the `set`, `append`, or + /// `delete` methods will return an error, and the resource must be + /// dropped before the parent `future-trailers` is dropped. + get: func() -> option, error-code>>>; + } + + /// Represents an outgoing HTTP Response. + resource outgoing-response { + /// Construct an `outgoing-response`, with a default `status-code` of `200`. + /// If a different `status-code` is needed, it must be set via the + /// `set-status-code` method. + /// + /// * `headers` is the HTTP Headers for the Response. + constructor(headers: headers); + /// Get the HTTP Status Code for the Response. + status-code: func() -> status-code; + /// Set the HTTP Status Code for the Response. Fails if the status-code + /// given is not a valid http status code. + set-status-code: func(status-code: status-code) -> result; + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transfered to + /// another component by e.g. `outgoing-handler.handle`. + headers: func() -> headers; + /// Returns the resource corresponding to the outgoing Body for this Response. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-response` can be retrieved at most once. Subsequent + /// calls will return error. + body: func() -> result; + } + + /// Represents an outgoing HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, inducating the full contents of the body + /// have been sent. This resource represents the contents as an + /// `output-stream` child resource, and the completion of the body (with + /// optional trailers) with a static function that consumes the + /// `outgoing-body` resource, and ensures that the user of this interface + /// may not write to the body contents after the body has been finished. + /// + /// If the user code drops this resource, as opposed to calling the static + /// method `finish`, the implementation should treat the body as incomplete, + /// and that an error has occured. The implementation should propogate this + /// error to the HTTP protocol by whatever means it has available, + /// including: corrupting the body on the wire, aborting the associated + /// Request, or sending a late status code for the Response. + resource outgoing-body { + /// Returns a stream for writing the body contents. + /// + /// The returned `output-stream` is a child resource: it must be dropped + /// before the parent `outgoing-body` resource is dropped (or finished), + /// otherwise the `outgoing-body` drop or `finish` will trap. + /// + /// Returns success on the first call: the `output-stream` resource for + /// this `outgoing-body` may be retrieved at most once. Subsequent calls + /// will return error. + write: func() -> result; + /// Finalize an outgoing body, optionally providing trailers. This must be + /// called to signal that the response is complete. If the `outgoing-body` + /// is dropped without calling `outgoing-body.finalize`, the implementation + /// should treat the body as corrupted. + /// + /// Fails if the body's `outgoing-request` or `outgoing-response` was + /// constructed with a Content-Length header, and the contents written + /// to the body (via `write`) does not match the value given in the + /// Content-Length. + finish: static func(this: outgoing-body, trailers: option) -> result<_, error-code>; + } + + /// Represents a future which may eventaully return an incoming HTTP + /// Response, or an error. + /// + /// This resource is returned by the `wasi:http/outgoing-handler` interface to + /// provide the HTTP Response corresponding to the sent Request. + resource future-incoming-response { + /// Returns a pollable which becomes ready when either the Response has + /// been received, or an error has occured. When this pollable is ready, + /// the `get` method will return `some`. + subscribe: func() -> pollable; + /// Returns the incoming HTTP Response, or an error, once one is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the response or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the incoming HTTP Response + /// status and headers have recieved successfully, or that an error + /// occured. Errors may also occur while consuming the response body, + /// but those will be reported by the `incoming-body` and its + /// `output-stream` child. + get: func() -> option>>; + } + + /// Attempts to extract a http-related `error` from the wasi:io `error` + /// provided. + /// + /// Stream operations which return + /// `wasi:io/stream/stream-error::last-operation-failed` have a payload of + /// type `wasi:io/error/error` with more information about the operation + /// that failed. This payload can be passed through to this function to see + /// if there's http-related information about the error to return. + /// + /// Note that this function is fallible because not all io-errors are + /// http-related errors. + http-error-code: func(err: borrow) -> option; +} + +/// This interface defines a handler of incoming HTTP Requests. It should +/// be exported by components which can respond to HTTP Requests. +interface incoming-handler { + use types.{incoming-request, response-outparam}; + + /// This function is invoked with an incoming HTTP Request, and a resource + /// `response-outparam` which provides the capability to reply with an HTTP + /// Response. The response is sent by calling the `response-outparam.set` + /// method, which allows execution to continue after the response has been + /// sent. This enables both streaming to the response body, and performing other + /// work. + /// + /// The implementor of this function must write a response to the + /// `response-outparam` before returning, or else the caller will respond + /// with an error on its behalf. + handle: func(request: incoming-request, response-out: response-outparam); +} + +/// This interface defines a handler of outgoing HTTP Requests. It should be +/// imported by components which wish to make HTTP Requests. +interface outgoing-handler { + use types.{outgoing-request, request-options, future-incoming-response, error-code}; + + /// This function is invoked with an outgoing HTTP Request, and it returns + /// a resource `future-incoming-response` which represents an HTTP Response + /// which may arrive in the future. + /// + /// The `options` argument accepts optional parameters for the HTTP + /// protocol's transport layer. + /// + /// This function may return an error if the `outgoing-request` is invalid + /// or not allowed to be made. Otherwise, protocol errors are reported + /// through the `future-incoming-response`. + handle: func(request: outgoing-request, options: option) -> result; +} + +/// The `wasi:http/proxy` world captures a widely-implementable intersection of +/// hosts that includes HTTP forward and reverse proxies. Components targeting +/// this world may concurrently stream in and out any number of incoming and +/// outgoing HTTP requests. +world proxy { + import wasi:random/random@0.2.0; + import wasi:io/error@0.2.0; + import wasi:io/poll@0.2.0; + import wasi:io/streams@0.2.0; + import wasi:cli/stdout@0.2.0; + import wasi:cli/stderr@0.2.0; + import wasi:cli/stdin@0.2.0; + import wasi:clocks/monotonic-clock@0.2.0; + import types; + import outgoing-handler; + import wasi:clocks/wall-clock@0.2.0; + + export incoming-handler; +} diff --git a/wit/deps/wasi-io-0.2.0/package.wit b/wit/deps/wasi-io-0.2.0/package.wit new file mode 100644 index 0000000..962734d --- /dev/null +++ b/wit/deps/wasi-io-0.2.0/package.wit @@ -0,0 +1,48 @@ +package wasi:io@0.2.0; + +interface poll { + resource pollable { + ready: func() -> bool; + block: func(); + } + + poll: func(in: list>) -> list; +} + +interface error { + resource error { + to-debug-string: func() -> string; + } +} + +interface streams { + use error.{error}; + use poll.{pollable}; + + variant stream-error { + last-operation-failed(error), + closed, + } + + resource input-stream { + read: func(len: u64) -> result, stream-error>; + blocking-read: func(len: u64) -> result, stream-error>; + skip: func(len: u64) -> result; + blocking-skip: func(len: u64) -> result; + subscribe: func() -> pollable; + } + + resource output-stream { + check-write: func() -> result; + write: func(contents: list) -> result<_, stream-error>; + blocking-write-and-flush: func(contents: list) -> result<_, stream-error>; + flush: func() -> result<_, stream-error>; + blocking-flush: func() -> result<_, stream-error>; + subscribe: func() -> pollable; + write-zeroes: func(len: u64) -> result<_, stream-error>; + blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>; + splice: func(src: borrow, len: u64) -> result; + blocking-splice: func(src: borrow, len: u64) -> result; + } +} + diff --git a/wit/deps/wasi-logging-0.1.0-draft/package.wit b/wit/deps/wasi-logging-0.1.0-draft/package.wit new file mode 100644 index 0000000..164cb5b --- /dev/null +++ b/wit/deps/wasi-logging-0.1.0-draft/package.wit @@ -0,0 +1,36 @@ +package wasi:logging@0.1.0-draft; + +/// WASI Logging is a logging API intended to let users emit log messages with +/// simple priority levels and context values. +interface logging { + /// A log level, describing a kind of message. + enum level { + /// Describes messages about the values of variables and the flow of + /// control within a program. + trace, + /// Describes messages likely to be of interest to someone debugging a + /// program. + debug, + /// Describes messages likely to be of interest to someone monitoring a + /// program. + info, + /// Describes messages indicating hazardous situations. + warn, + /// Describes messages indicating serious errors. + error, + /// Describes messages indicating fatal errors. + critical, + } + + /// Emit a log message. + /// + /// A log message has a `level` describing what kind of message is being + /// sent, a context, which is an uninterpreted string meant to help + /// consumers group similar messages, and a string containing the message + /// text. + log: func(level: level, context: string, message: string); +} + +world imports { + import logging; +} diff --git a/wit/deps/wasi-random-0.2.0/package.wit b/wit/deps/wasi-random-0.2.0/package.wit new file mode 100644 index 0000000..58c179e --- /dev/null +++ b/wit/deps/wasi-random-0.2.0/package.wit @@ -0,0 +1,18 @@ +package wasi:random@0.2.0; + +interface random { + get-random-bytes: func(len: u64) -> list; + + get-random-u64: func() -> u64; +} + +interface insecure { + get-insecure-random-bytes: func(len: u64) -> list; + + get-insecure-random-u64: func() -> u64; +} + +interface insecure-seed { + insecure-seed: func() -> tuple; +} + diff --git a/wit/deps/wasi-sockets-0.2.0/package.wit b/wit/deps/wasi-sockets-0.2.0/package.wit new file mode 100644 index 0000000..0602b85 --- /dev/null +++ b/wit/deps/wasi-sockets-0.2.0/package.wit @@ -0,0 +1,179 @@ +package wasi:sockets@0.2.0; + +interface network { + resource network; + + enum error-code { + unknown, + access-denied, + not-supported, + invalid-argument, + out-of-memory, + timeout, + concurrency-conflict, + not-in-progress, + would-block, + invalid-state, + new-socket-limit, + address-not-bindable, + address-in-use, + remote-unreachable, + connection-refused, + connection-reset, + connection-aborted, + datagram-too-large, + name-unresolvable, + temporary-resolver-failure, + permanent-resolver-failure, + } + + enum ip-address-family { + ipv4, + ipv6, + } + + type ipv4-address = tuple; + + type ipv6-address = tuple; + + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } + + record ipv4-socket-address { + port: u16, + address: ipv4-address, + } + + record ipv6-socket-address { + port: u16, + flow-info: u32, + address: ipv6-address, + scope-id: u32, + } + + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } +} + +interface instance-network { + use network.{network}; + + instance-network: func() -> network; +} + +interface udp { + use wasi:io/poll@0.2.0.{pollable}; + use network.{network, error-code, ip-socket-address, ip-address-family}; + + record incoming-datagram { + data: list, + remote-address: ip-socket-address, + } + + record outgoing-datagram { + data: list, + remote-address: option, + } + + resource udp-socket { + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; + %stream: func(remote-address: option) -> result, error-code>; + local-address: func() -> result; + remote-address: func() -> result; + address-family: func() -> ip-address-family; + unicast-hop-limit: func() -> result; + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + subscribe: func() -> pollable; + } + + resource incoming-datagram-stream { + receive: func(max-results: u64) -> result, error-code>; + subscribe: func() -> pollable; + } + + resource outgoing-datagram-stream { + check-send: func() -> result; + send: func(datagrams: list) -> result; + subscribe: func() -> pollable; + } +} + +interface udp-create-socket { + use network.{network, error-code, ip-address-family}; + use udp.{udp-socket}; + + create-udp-socket: func(address-family: ip-address-family) -> result; +} + +interface tcp { + use wasi:io/streams@0.2.0.{input-stream, output-stream}; + use wasi:io/poll@0.2.0.{pollable}; + use wasi:clocks/monotonic-clock@0.2.0.{duration}; + use network.{network, error-code, ip-socket-address, ip-address-family}; + + enum shutdown-type { + receive, + send, + both, + } + + resource tcp-socket { + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + finish-bind: func() -> result<_, error-code>; + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + finish-connect: func() -> result, error-code>; + start-listen: func() -> result<_, error-code>; + finish-listen: func() -> result<_, error-code>; + accept: func() -> result, error-code>; + local-address: func() -> result; + remote-address: func() -> result; + is-listening: func() -> bool; + address-family: func() -> ip-address-family; + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; + keep-alive-enabled: func() -> result; + set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; + keep-alive-idle-time: func() -> result; + set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; + keep-alive-interval: func() -> result; + set-keep-alive-interval: func(value: duration) -> result<_, error-code>; + keep-alive-count: func() -> result; + set-keep-alive-count: func(value: u32) -> result<_, error-code>; + hop-limit: func() -> result; + set-hop-limit: func(value: u8) -> result<_, error-code>; + receive-buffer-size: func() -> result; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + subscribe: func() -> pollable; + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; + } +} + +interface tcp-create-socket { + use network.{network, error-code, ip-address-family}; + use tcp.{tcp-socket}; + + create-tcp-socket: func(address-family: ip-address-family) -> result; +} + +interface ip-name-lookup { + use wasi:io/poll@0.2.0.{pollable}; + use network.{network, error-code, ip-address}; + + resource resolve-address-stream { + resolve-next-address: func() -> result, error-code>; + subscribe: func() -> pollable; + } + + resolve-addresses: func(network: borrow, name: string) -> result; +} + diff --git a/wit/deps/wasmcloud-bus-1.0.0/package.wit b/wit/deps/wasmcloud-bus-1.0.0/package.wit new file mode 100644 index 0000000..2a7cd93 --- /dev/null +++ b/wit/deps/wasmcloud-bus-1.0.0/package.wit @@ -0,0 +1,10 @@ +package wasmcloud:bus@1.0.0; + +interface lattice { + resource call-target-interface { + constructor(namespace: string, %package: string, %interface: string); + } + + set-link-name: func(name: string, interfaces: list); +} + diff --git a/wit/deps/wasmcloud-component-go-0.1.0/package.wit b/wit/deps/wasmcloud-component-go-0.1.0/package.wit new file mode 100644 index 0000000..75caf04 --- /dev/null +++ b/wit/deps/wasmcloud-component-go-0.1.0/package.wit @@ -0,0 +1,85 @@ +package wasmcloud:component-go@0.1.0; + +world exports { + import wasi:io/poll@0.2.0; + import wasi:clocks/monotonic-clock@0.2.0; + import wasi:io/error@0.2.0; + import wasi:io/streams@0.2.0; + import wasi:http/types@0.2.0; + + export wasi:http/incoming-handler@0.2.0; +} +world imports { + import wasi:logging/logging@0.1.0-draft; + import wasi:config/runtime@0.2.0-draft; + import wasi:io/poll@0.2.0; + import wasi:clocks/monotonic-clock@0.2.0; + import wasi:io/error@0.2.0; + import wasi:io/streams@0.2.0; + import wasi:http/types@0.2.0; + import wasi:http/outgoing-handler@0.2.0; + import wasmcloud:bus/lattice@1.0.0; + import wasmcloud:secrets/store@0.1.0-draft; + import wasmcloud:secrets/reveal@0.1.0-draft; + import wasi:cli/environment@0.2.0; + import wasi:cli/exit@0.2.0; + import wasi:cli/stdin@0.2.0; + import wasi:cli/stdout@0.2.0; + import wasi:cli/stderr@0.2.0; + import wasi:cli/terminal-input@0.2.0; + import wasi:cli/terminal-output@0.2.0; + import wasi:cli/terminal-stdin@0.2.0; + import wasi:cli/terminal-stdout@0.2.0; + import wasi:cli/terminal-stderr@0.2.0; + import wasi:clocks/wall-clock@0.2.0; + import wasi:filesystem/types@0.2.0; + import wasi:filesystem/preopens@0.2.0; + import wasi:sockets/network@0.2.0; + import wasi:sockets/instance-network@0.2.0; + import wasi:sockets/udp@0.2.0; + import wasi:sockets/udp-create-socket@0.2.0; + import wasi:sockets/tcp@0.2.0; + import wasi:sockets/tcp-create-socket@0.2.0; + import wasi:sockets/ip-name-lookup@0.2.0; + import wasi:random/random@0.2.0; + import wasi:random/insecure@0.2.0; + import wasi:random/insecure-seed@0.2.0; +} +world sdk { + import wasi:io/poll@0.2.0; + import wasi:clocks/monotonic-clock@0.2.0; + import wasi:io/error@0.2.0; + import wasi:io/streams@0.2.0; + import wasi:http/types@0.2.0; + import wasi:logging/logging@0.1.0-draft; + import wasi:config/runtime@0.2.0-draft; + import wasi:http/outgoing-handler@0.2.0; + import wasmcloud:bus/lattice@1.0.0; + import wasmcloud:secrets/store@0.1.0-draft; + import wasmcloud:secrets/reveal@0.1.0-draft; + import wasi:cli/environment@0.2.0; + import wasi:cli/exit@0.2.0; + import wasi:cli/stdin@0.2.0; + import wasi:cli/stdout@0.2.0; + import wasi:cli/stderr@0.2.0; + import wasi:cli/terminal-input@0.2.0; + import wasi:cli/terminal-output@0.2.0; + import wasi:cli/terminal-stdin@0.2.0; + import wasi:cli/terminal-stdout@0.2.0; + import wasi:cli/terminal-stderr@0.2.0; + import wasi:clocks/wall-clock@0.2.0; + import wasi:filesystem/types@0.2.0; + import wasi:filesystem/preopens@0.2.0; + import wasi:sockets/network@0.2.0; + import wasi:sockets/instance-network@0.2.0; + import wasi:sockets/udp@0.2.0; + import wasi:sockets/udp-create-socket@0.2.0; + import wasi:sockets/tcp@0.2.0; + import wasi:sockets/tcp-create-socket@0.2.0; + import wasi:sockets/ip-name-lookup@0.2.0; + import wasi:random/random@0.2.0; + import wasi:random/insecure@0.2.0; + import wasi:random/insecure-seed@0.2.0; + + export wasi:http/incoming-handler@0.2.0; +} diff --git a/wit/deps/wasmcloud-messaging-0.2.0/package.wit b/wit/deps/wasmcloud-messaging-0.2.0/package.wit new file mode 100644 index 0000000..b037df7 --- /dev/null +++ b/wit/deps/wasmcloud-messaging-0.2.0/package.wit @@ -0,0 +1,29 @@ +package wasmcloud:messaging@0.2.0; + +/// Types common to message broker interactions +interface types { + /// A message sent to or received from a broker + record broker-message { + subject: string, + body: list, + reply-to: option, + } +} + +interface handler { + use types.{broker-message}; + + /// Callback handled to invoke a function when a message is received from a subscription + handle-message: func(msg: broker-message) -> result<_, string>; +} + +interface consumer { + use types.{broker-message}; + + /// Perform a request operation on a subject + request: func(subject: string, body: list, timeout-ms: u32) -> result; + + /// Publish a message to a subject without awaiting a response + publish: func(msg: broker-message) -> result<_, string>; +} + diff --git a/wit/deps/wasmcloud-secrets-0.1.0-draft/package.wit b/wit/deps/wasmcloud-secrets-0.1.0-draft/package.wit new file mode 100644 index 0000000..3a93ea4 --- /dev/null +++ b/wit/deps/wasmcloud-secrets-0.1.0-draft/package.wit @@ -0,0 +1,25 @@ +package wasmcloud:secrets@0.1.0-draft; + +interface store { + variant secrets-error { + upstream(string), + io(string), + not-found, + } + + variant secret-value { + %string(string), + bytes(list), + } + + resource secret; + + get: func(key: string) -> result; +} + +interface reveal { + use store.{secret, secret-value}; + + reveal: func(s: borrow) -> secret-value; +} + diff --git a/wit/world.wit b/wit/world.wit new file mode 100644 index 0000000..efa3209 --- /dev/null +++ b/wit/world.wit @@ -0,0 +1,12 @@ +package wasmcloud:hello; + +world hello { + include wasmcloud:component-go/imports@0.1.0; + + import wasi:config/runtime@0.2.0-draft; + + import wasmcloud:postgres/query@0.1.1-draft; + + export wasmcloud:messaging/handler@0.2.0; + import wasi:logging/logging@0.1.0-draft; +}