Skip to content

Updating JS host payloads and simple examples #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ dist/*
index.html
!examples/**/*index.html*
*.sh*
wasmcloud-rs-js/pkg/
wasmcloud-rs-js/pkg/

host_config.json
out.js
*.wasm
100 changes: 56 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,44 @@
# wasmCloud Host in JavaScript/Browser

This is the JavaScript implementation of the wasmCloud host for the browser (NodeJS support in progress). The library runs a host inside a web browser/application that connects to a remote lattice via NATS and can run wasmCloud actors in the browser. The host will automatically listen for actor start/stop from NATS and will initialize the actor using `wapcJS`. Any invocations will be handled by the browser actor and returned to the requesting host via NATS. Users can pass callbacks to handle invocation and event data.
This is the JavaScript implementation of the wasmCloud host for the browser (NodeJS support in progress). The library runs a host inside a web browser/application that connects to a remote [lattice](https://wasmcloud.com/docs/reference/lattice/) via NATS and can run [wasmCloud actors](https://wasmcloud.com/docs/app-dev/create-actor/) in the browser. The host will automatically listen for actor start/stop from NATS and will initialize the actor using `wapcJS`. Any invocations will be handled by the browser actor and returned to the requesting host via NATS. Users can pass callbacks to handle invocation and event data.

## Running the JavaScript host

This repository contains two examples:

1. [console](./examples/console) which includes examples for how to utilize the wasmCloud host as a library or through a bundler like webpack or esbuild. All interaction with this host occurs through the browser console
1. [simple](./examples/simple/) which includes a very simple web UI, error handling, and scripts for starting the host through the web browser. **It's recommended to use this example to get started**

## Demonstration Video

In this demonstration video we will demonstration the following:

* Load an HTTP Server capability into a wasmCloud Host running on a machine
* Load an the wasmcloud-js host into a web browser
* Load an 'echo' actor into the web browser
* **seamlessly** bind the actor to the capability provider through Lattice
* Access the webserver, which in turn delivers the request to the actor, processes it, and returns it to the requestion client via the capability
* Unload the actor
- Load an HTTP Server capability into a wasmCloud Host running on a machine
- Load an the wasmcloud-js host into a web browser
- Load an 'echo' actor into the web browser
- **seamlessly** bind the actor to the capability provider through Lattice
- Access the webserver, which in turn delivers the request to the actor, processes it, and returns it to the requestion client via the capability
- Unload the actor

https://user-images.githubusercontent.com/1530656/130013412-b9a9daa6-fc71-424b-814c-2ca400926794.mp4



## Prerequisities

* NATS with WebSockets enabled
- NATS with WebSockets enabled

* wasmCloud lattice (OTP Version)
- wasmCloud lattice (OTP Version)

* (OPTIONAL) Docker Registry with CORS configured
- (OPTIONAL) Docker Registry with CORS configured

* If launching actors from remote registries in the browser host, CORS must be configured on the registry server
- If launching actors from remote registries in the browser host, CORS must be configured on the registry server

## Development Prerequisities

* NodeJS, npm

* rust, cargo, wasm-pack
- NodeJS, npm

* Used to port the rust versions of wascap, nkeys to JS
- rust, cargo, wasm-pack

- Used to port the rust versions of wascap, nkeys to JS

## Installation

Expand All @@ -44,16 +48,16 @@ $ npm install @wasmcloud/wasmcloud-js

## Usage

More examples can be found in the [examples](examples/) directory, including sample `webpack` and `esbuild` configurations
More examples can be found in the [examples](examples/) directory, including sample `webpack` and `esbuild` configurations

**Browser**

```html
````html
<script src="https://unpkg.com/@wasmcloud/wasmcloud-js@<VERSION>/dist/wasmcloud.js"></script>
<script>
(async () => {
// Start the host passing the name, registry tls enabled, a list of nats ws/wss hosts or the natsConnection object, and an optional host heartbeat interval (default is 30 seconds)
const host = await wasmcloudjs.startHost("default", false, ["ws://localhost:4222"], 30000);
const host = await wasmcloudjs.startHost('default', false, ['ws://localhost:4222'], 30000);
// The host will automatically listen for actors start & stop messages, to manually listen for these messages the following methods are exposed
// only call these methods if your host is not listening for actor start/stop
// actor invocations are automatically returned to the host. if a user wants to handle the data, they can pass a map of callbacks using the actor ref/wasm file name as the key with a callback(data, result) function. The data contains the invocation data and the result contains the invocation result
Expand All @@ -72,17 +76,25 @@ More examples can be found in the [examples](examples/) directory, including sam
// return Uint8Array(payload);
// })
// ```
await host.launchActor("registry.com/actor:0.1.1", (data) => { /* handle data */})
await host.launchActor('registry.com/actor:0.1.1', data => {
/* handle data */
});
// Launch an actor with the hostCallback
await host.launchActor("registry.com/actor:0.1.1", (data) => { /* handle data */}, (binding, namespace, operation, payload) => {
await host.launchActor(
'registry.com/actor:0.1.1',
data => {
/* handle data */
},
(binding, namespace, operation, payload) => {
// decode payload via messagepack
// const decoded = decode(payload);
return new Uint8Array(payload);
})
}
);
// To launch an actrom manually from local disk (note the .wasm is required)
await host.launchActor("./actor.wasm");
await host.launchActor('./actor.wasm');
// To listen for events, you can call the subscribeToEvents and pass an optional callback to handle the event data
await host.subscribeToEvents((eventData) => console.log(eventData, eventData.source));
await host.subscribeToEvents(eventData => console.log(eventData, eventData.source));
// To unsubscribe, call the unsubscribeEvents
await host.unsubscribeEvents();
// To start & stop the heartbeat events
Expand All @@ -98,15 +110,15 @@ More examples can be found in the [examples](examples/) directory, including sam
await host.startHost();
})();
</script>
```
````

**With a bundler**

There are some caveats to using with a bundler:
There are some caveats to using with a bundler:

* The module contains `.wasm` files that need to be present alongside the final build output. Using `webpack-copy-plugin` (or `fs.copyFile` with other bundlers) can solve this issue.
- The module contains `.wasm` files that need to be present alongside the final build output. Using `webpack-copy-plugin` (or `fs.copyFile` with other bundlers) can solve this issue.

* If using with `create-react-app`, the webpack config will need to be ejected via `npm run eject` OR an npm library like `react-app-rewired` can handle the config injection.
- If using with `create-react-app`, the webpack config will need to be ejected via `npm run eject` OR an npm library like `react-app-rewired` can handle the config injection.

```javascript
// as esm -- this will grant you access to the types/params
Expand All @@ -115,13 +127,13 @@ import { startHost } from '@wasmcloud/wasmcloud-js';
// const wasmcloudjs = require('@wasmcloud/wasmcloud-js);

async function cjsHost() {
const host = await wasmcloudjs.startHost('default', false, ['ws://localhost:4222'])
console.log(host);
const host = await wasmcloudjs.startHost('default', false, ['ws://localhost:4222']);
console.log(host);
}

async function esmHost() {
const host = await startHost('default', false, ['ws://localhost:4222'])
console.log(host);
const host = await startHost('default', false, ['ws://localhost:4222']);
console.log(host);
}

cjsHost();
Expand All @@ -131,20 +143,20 @@ esmHost();
```javascript
// webpack config, add this to the plugin section
plugins: [
new CopyPlugin({
patterns: [
{
from: 'node_modules/@wasmcloud/wasmcloud-js/dist/wasmcloud-rs-js/pkg/*.wasm',
to: '[name].wasm'
}
]
}),
]
new CopyPlugin({
patterns: [
{
from: 'node_modules/@wasmcloud/wasmcloud-js/dist/wasmcloud-rs-js/pkg/*.wasm',
to: '[name].wasm'
}
]
})
];
```

**Node**
**Node**

*IN PROGRESS* - NodeJS does not support WebSockets natively (required by nats.ws)
_IN PROGRESS_ - NodeJS does not support WebSockets natively (required by nats.ws)

## Contributing

Expand Down
50 changes: 50 additions & 0 deletions examples/console/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# wasmcloud-js Console Example

This directory contains examples of using the `wasmcloud-js` library with sample `webpack` and `esbuild` configurations. All interaction with the wasmCloud host is done through the console.

## Prerequisities

- NATS with WebSockets enabled

- There is sample infra via docker in the `test/infra` directory of this repo, `cd test/infra && docker-compose up`

- wasmCloud lattice (OTP Version)

- (OPTIONAL) Docker Registry with CORS configured

- If launching actors from remote registries in the browser host, CORS must be configured on the registry server

- NodeJS, NPM, npx

## Build

```sh
$ npm install # this will run and build the rust deps
$ npm install webpack esbuild copy-webpack-plugin fs
$ #### if you want to use esbuild, follow these next 2 steps ####
$ node esbuild.js # this produces the esbuild version
$ mv out-esbuild.js out.js # rename the esbuild version
$ #### if you want to use webpack, follow the steps below ####
$ npx webpack --config=example-webpack.config.js # this produces the webpack output
$ mv out-webpack.js out.js #rename the webpack version to out.js
```

## Usage

1. Build the code with esbuild or webpack

2. Rename the output file to `out.js`

3. Start a web server inside this directory (e.g `python3 -m http.server` or `npx serve`)

4. Navigate to a browser `localhost:<PORT>`

5. Open the developer console to view the host output

6. In the dev tools run `host` and you will get the full host object and methods

7. Start an actor with `host.launchActor('registry:5000/image', (data) => console.log(data))`. Echo actor is recommended (the 2nd parameter here is an invocation callback to handle the data from an invocation)

8. Link the actor with a provider running in Wasmcloud (eg `httpserver`)

9. Run a `curl localhost:port/echo` to see the response in the console (based off the invocation callback).
18 changes: 18 additions & 0 deletions examples/console/esbuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Use this path in projects using the node import
let defaultWasmFileLocation = './node_modules/@wasmcloud/wasmcloud-js/dist/wasmcloud-rs-js/pkgwasmcloud.wasm'
let wasmFileLocationForLocal = '../../dist/wasmcloud-rs-js/pkg/wasmcloud.wasm'

let copyPlugin = {
name: 'copy',
setup(build) {
require('fs').copyFile(wasmFileLocationForLocal, `${process.cwd()}/wasmcloud.wasm`, (err) => {
if (err) throw err;
});
}
}
require('esbuild').build({
entryPoints: ['main.js'],
bundle: true,
outfile: 'out-esbuild.js',
plugins: [copyPlugin]
}).catch(() => process.exit(1))
22 changes: 22 additions & 0 deletions examples/console/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World - wasmcloudjs</title>
<!-- <script src="https://unpkg.com/@wasmcloud/wasmcloud-js@1.0.4/dist/wasmcloud.js"></script> -->
<script src="./out.js"></script>

<!-- this uses the npm library-->
<!-- <script src="./out-webpack.js"></script> -->
<!-- <script src="./out-esbuild.js"></script> -->
<script>
// (async() => {
// console.log("USING THE BROWSER BUNDLE")
// const host = await wasmcloudjs.startHost("default", false, ["ws://localhost:4222"])
// console.log(host);
// })()
</script>
</head>
<body>
</body>
</html>
12 changes: 12 additions & 0 deletions examples/console/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { startHost } from '../../dist/src'

// Importing inside of a project
// import { startHost } from '@wasmcloud/wasmcloud-js';
// const { startHost } = require('@wasmcloud/wasmcloud-js');

(async () => {
console.log('USING A JS BUNDLER')
const host = await startHost('default', false, ['ws://localhost:6222'])
window.host = host;
console.log(host);
})()
14 changes: 14 additions & 0 deletions examples/simple/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
help: ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_\-.*]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

install:
npm install

infra: ## Launch required infrastructure using Docker
cd ../../test/infra && docker compose up -d

build: install ## Install NPM dependencies and compile the JS library and wasmcloud.wasm file
node esbuild.js

run: build ## Build and run wasmCloud with esbuild, then run python3's httpserver
python3 -m http.server
58 changes: 25 additions & 33 deletions examples/simple/README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,42 @@
# wasmcloud-js Examples
# Simple HTML/JS example

This directory contains examples of using the `wasmcloud-js` library with sample `webpack` and `esbuild` configurations.
This directory contains examples of using the `wasmcloud-js` library with an `esbuild` configuration.

## Prerequisities

* NATS with WebSockets enabled
- `make`
- `npm`
- `cargo` and a Rust `wasm32-unknown-unknown` toolchain installed
- `python3` or an equivalent way to serve static assets from local files
- `nats-server` with JetStream and WebSockets enabled

* There is sample infra via docker in the `test/infra` directory of this repo, `cd test/infra && docker-compose up`
## Build and Run

* wasmCloud lattice (OTP Version)

* (OPTIONAL) Docker Registry with CORS configured

* If launching actors from remote registries in the browser host, CORS must be configured on the registry server

* NodeJS, NPM, npx

## Build
This example is bundled with `esbuild` and runs locally with use of the `python3` http server. As mentioned above you will need a NATS server running with JetStream and WebSockets. You can either launch a NATS server yourself with `nats-server -js -c ../../test/infra/nats.conf`, or use the included Docker compose file with `make infra`

```sh
$ npm install # this will run and build the rust deps
$ npm install webpack esbuild copy-webpack-plugin fs
$ #### if you want to use esbuild, follow these next 2 steps ####
$ node esbuild.js # this produces the esbuild version
$ mv out-esbuild.js out.js # rename the esbuild version
$ #### if you want to use webpack, follow the steps below ####
$ npx webpack --config=example-webpack.config.js # this produces the webpack output
$ mv out-webpack.js out.js #rename the webpack version to out.js
make run
```

## Usage

1. Build the code with esbuild or webpack

2. Rename the output file to `out.js`
In another terminal you'll need to run a NATS server with websockets enabled, which you can do with:

3. Start a web server inside this directory (e.g `python3 -m http.server` or `npx serve`)
```
cd ../../test/infra && docker compose up
```

3. Navigate to a browser `localhost:<PORT>`
## Starting Actors

4. Open the developer console to view the host output
For this section you should install [`wash`, the wasmCloud shell](https://wasmcloud.com/docs/installation).
You can start actors on this host by dragging the `.wasm` file into the browser window after you launch the host. To run the `echo` example, in your terminal, download the sample actor with `wash`:

5. In the dev tools run `host` and you will get the full host object and methods
```
wash reg pull wasmcloud.azurecr.io/echo:0.3.4
```

6. Start an actor with `host.launchActor('registry:5000/image', (data) => console.log(data))`. Echo actor is recommended (the 2nd parameter here is an invocation callback to handle the data from an invocation)
Then, drag that `echo.wasm` file into the browser. You should see a single `Echo` actor running. You can directly call this actor's HTTP handler using `wash`:

7. Link the actor with a provider running in Wasmcloud (eg `httpserver`)
```
wash call MBCFOPM6JW2APJLXJD3Z5O4CN7CPYJ2B4FTKLJUR5YR5MITIU7HD3WD5 HttpServer.HandleRequest '{"method": "GET", "path": "/echo", "body": "", "queryString":"","header":{}}'
```

8. Run a `curl localhost:port/echo` to see the response in the console (based off the invocation callback).
And you'll get back a raw response like: `Call response (raw): ��statusCode�Ȧheader��body�;{"body":[],"method":"GET","path":"/echo","query_string":""}`
Loading