Skip to content
Merged
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
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,27 @@ how much glue you want to write.
- **[`rclnodejs/web`](./web/README.md)** — a typed layer over your ROS 2 graph:
you allow-list capabilities in `web.json` or via CLI flags; anything else is
rejected before it reaches ROS 2. Best for typed web apps and HTTP clients.
- **Typed SDK** — `call`, `publish` and `subscribe`, typed end-to-end from
your generated message and service types.
- **Two transports** — WebSocket, plus an optional HTTP listener
(`--http-port`) so `call` and `publish` work from `curl`, Postman or
`fetch()`. `subscribe` needs WebSocket, or `--http-sse` to stream it as
Server-Sent Events.
- **OpenAPI 3.1** — `rclnodejs-web openapi` emits a machine-readable spec
for codegen, API explorers and agent tool-use.
- **Typed SDK** — `call`, `publish`, `subscribe`, and `action`, typed
from generated ROS interfaces.
- **Two transports** — WebSocket for all verbs, or HTTP (`--http-port`)
for calls, publishes, and SSE actions. Action cancellation requires
WebSocket and server acceptance. SDK subscriptions use WebSocket;
`--http-sse` enables raw HTTP subscriptions.
- **OpenAPI 3.1** — export service, topic, and action schemas with
`rclnodejs-web openapi`.

```ts
import type {} from 'rclnodejs';
import { connect } from 'rclnodejs/web';
const ros = await connect('ws://host:9000/capability');
const reply = await ros.call<'example_interfaces/srv/AddTwoInts'>(
'/add_two_ints', { a: '2n', b: '40n' }
); // reply.sum is typed as `${number}n`
```

See the [Fibonacci action demo](./demo/web/javascript/README.md#fibonacci-actions)
for a runnable example.

- **[`rosocket`](./rosocket/README.md)** — thin WebSocket gateway,
zero browser dependencies (just built-in `WebSocket` + `JSON`).
Best for quick prototypes and `roslibjs`-style apps.
Expand Down
61 changes: 56 additions & 5 deletions demo/web/javascript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ A single static HTML page that talks to a real ROS 2 graph — just

## Run it (two shells)

First, source ROS 2 and run `npm install` at the repository root to prepare
the runtime dependencies, native addon and generated interfaces. No separate
installation is needed for the static page.

```bash
cd demo/web/javascript
```
Expand All @@ -16,12 +20,12 @@ cd demo/web/javascript
source /opt/ros/<distro>/setup.bash
node runtime.mjs
# rclnodejs/web : ws://localhost:9000/capability
# also http://localhost:9001/capability (call/publish, curl-able)
# also http://localhost:9001/capability (call/publish/action, curl-able)
# also http://localhost:9001/capability/subscribe/<name> (SSE)
```

`runtime.mjs` exposes a tiny `/add_two_ints` service and the shared
`/web_demo_chatter` talker/listener topic.
`runtime.mjs` includes `/add_two_ints`, `/web_demo_chatter`, and a cancellable
`/fibonacci` server (`example_interfaces/action/Fibonacci`).

**Shell 2 — static-file server** (hosts `index.html`, maps `/sdk/*` to
the in-repo [`web/`](../../../web/) SDK):
Expand All @@ -31,6 +35,9 @@ node static.mjs
# Static files : http://localhost:8080/
```

For custom ports, set `RUNTIME_PORT`, `HTTP_PORT`, and `STATIC_PORT`;
match the page URL, e.g. `?wsPort=9010&httpPort=9011`.

## What the browser code looks like

```html
Expand All @@ -49,21 +56,58 @@ node static.mjs
The page also has a **transport toggle** (WebSocket vs. HTTP) so you
can flip the SDK between the two without restarting.

## Fibonacci actions

Send an order from 2 to 12 (default 8) to see feedback every half-second,
then the final sequence and status.

- WebSocket: **Cancel Goal** requests cancellation.
- HTTP/SSE: **Stop Streaming** disconnects without canceling the ROS goal.
- Switching transports or leaving the page closes the client and ignores late events.

```js
const client = await connect({ http: 'http://localhost:9001' });
try {
const goal = await client.action(
'/fibonacci',
{ order: 5 },
{
onFeedback: (feedback) => console.log(feedback.sequence),
}
);
console.log(await goal.result, goal.status);
} finally {
await client.close();
}
```

The panel's `{ http }` client uses `fetch()` for SSE actions. Topic
subscriptions remain on WebSocket; `sse: true` is not needed for actions.
Check `goal.status` after awaiting `goal.result`: canceled or aborted goals
can also return results.

## Same capability, no SDK

Every `call` / `publish` / `subscribe` is also reachable as plain HTTP —
Every `call` / `publish` / `subscribe` / `action` is also reachable as plain HTTP —
curl, Postman, or an AI agent, no JavaScript required:

```bash
curl -sS -X POST http://localhost:9001/capability/call/add_two_ints \
-H 'content-type: application/json' -d '{"a":"7n","b":"35n"}'
# => {"sum":"42n"}

curl --fail-with-body -sS -N http://localhost:9001/capability/action/fibonacci \
-H 'content-type: application/json' -d '{"order":3}'
# terminal data: {"status":"succeeded","payload":{"sequence":[0,1,1,2]}}

curl -N http://localhost:9001/capability/subscribe/web_demo_chatter
# event: message
# data: {"data":"hi from curl"}
```

The topic stream waits for messages; publish from the page or a ROS node in
another shell while curl is listening.

The demo enables SSE + CORS (`new HttpTransport({ sse: true, cors: true
})`), so the same endpoints also work from a plain browser
`EventSource` / `fetch()` — see the page's own panels 6 and 7 for live,
Expand Down Expand Up @@ -102,11 +146,18 @@ ros2 run demo_nodes_cpp add_two_ints_server
`web.json` already sets `sse`/`cors`, matching what `runtime.mjs` enables
in code.

For CLI mode, also run a `/fibonacci` server using
`example_interfaces/action/Fibonacci`. Wildcard CORS is for local testing only.

## OpenAPI — no server required

The same `web.json` also documents itself as an OpenAPI 3.1 document — a
one-shot subcommand that prints it and exits, without starting any
transport or calling `rclnodejs.init()`.
transport or calling `rclnodejs.init()`. It still requires the sourced ROS
environment and available interface definitions.

The export includes action schemas. Swagger UI may buffer SSE; use the
panel or curl for live feedback.

```bash
source /opt/ros/<distro>/setup.bash
Expand Down
Loading
Loading