foxglove-ros-adapter 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Noah Wardlow
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # foxglove-ros-adapter
2
+
3
+ A drop-in replacement for [`roslib`](https://github.com/RobotWebTools/roslibjs) that speaks the
4
+ [Foxglove WebSocket protocol](https://github.com/foxglove/ws-protocol) to
5
+ [`foxglove_bridge`](https://github.com/foxglove/ros-foxglove-bridge) instead of `rosbridge_server`.
6
+
7
+ Same `Ros` / `Topic` / `Service` / `Param` / `ROS2TFClient` API — your existing code keeps working,
8
+ but you get CDR-native messages over the actively-maintained Foxglove bridge.
9
+
10
+ ## Why
11
+
12
+ - `roslibjs` targets `rosbridge_server`, which speaks the rosbridge JSON protocol and pays
13
+ JSON-encoding overhead on every message.
14
+ - `foxglove_bridge` is the recommended ROS 2 web bridge and uses binary CDR framing natively.
15
+ - Rewriting every `new Ros()` / `new Topic()` call site in a large app is painful.
16
+
17
+ This package gives you the Foxglove wire format with the `roslib` API, so the switch is a bundler
18
+ alias instead of a refactor.
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pnpm add foxglove-ros-adapter
24
+ # or
25
+ npm install foxglove-ros-adapter
26
+ # or
27
+ yarn add foxglove-ros-adapter
28
+ ```
29
+
30
+ `zod`, `@foxglove/rosmsg`, and `@foxglove/rosmsg2-serialization` are peer dependencies — install
31
+ alongside the adapter so they dedupe with anything else in your tree:
32
+
33
+ ```bash
34
+ pnpm add zod @foxglove/rosmsg @foxglove/rosmsg2-serialization
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### As a direct import
40
+
41
+ ```ts
42
+ import { Ros, Topic, Service } from "foxglove-ros-adapter";
43
+
44
+ const ros = new Ros({ url: "ws://localhost:8765" });
45
+
46
+ ros.on("connection", () => console.log("connected"));
47
+ ros.on("close", () => console.log("disconnected"));
48
+
49
+ const jointStates = new Topic({
50
+ ros,
51
+ name: "/joint_states",
52
+ messageType: "sensor_msgs/msg/JointState"
53
+ });
54
+
55
+ jointStates.subscribe((msg) => console.log(msg));
56
+ ```
57
+
58
+ Optional runtime validation:
59
+
60
+ ```ts
61
+ import { z } from "zod";
62
+
63
+ const stringTopic = new Topic({
64
+ ros,
65
+ name: "/status",
66
+ messageType: "std_msgs/msg/String",
67
+ messageSchema: z.object({ data: z.string() })
68
+ });
69
+ ```
70
+
71
+ ### As a drop-in alias for `roslib`
72
+
73
+ If your codebase imports from `"roslib"` in many places, alias the module at build time:
74
+
75
+ **Vite**
76
+
77
+ ```ts
78
+ // vite.config.ts
79
+ import { defineConfig } from "vite";
80
+
81
+ export default defineConfig({
82
+ resolve: {
83
+ alias: {
84
+ roslib: "foxglove-ros-adapter"
85
+ }
86
+ }
87
+ });
88
+ ```
89
+
90
+ **Webpack**
91
+
92
+ ```js
93
+ // webpack.config.js
94
+ module.exports = {
95
+ resolve: {
96
+ alias: {
97
+ roslib: "foxglove-ros-adapter"
98
+ }
99
+ }
100
+ };
101
+ ```
102
+
103
+ Now every `import { Topic } from "roslib"` resolves to this adapter with zero source changes.
104
+
105
+ ## What's supported
106
+
107
+ - `new Ros({ url })` — connect, `on("connection" | "close" | "error")`, `close()`, `getTopicsForType()`
108
+ - `new Topic({ ros, name, messageType, messageSchema? })` — `subscribe()`, `unsubscribe()`,
109
+ `publish()`
110
+ - `new Service({ ros, name, serviceType })` — `callService()` (callback API)
111
+ - `new Param({ ros, name })` — `get()`, `set()`
112
+ - `new ROS2TFClient({ ros, fixedFrame })` — `subscribe(frameId, cb)`, `unsubscribe()`,
113
+ `getFrameIds()`, `addFramesListener(cb)`, `removeFramesListener(cb)`
114
+ - Both `foxglove.sdk.v1` (ros-humble-foxglove-bridge 3.2.x / foxglove-sdk-cpp) and legacy
115
+ `foxglove.websocket.v1` subprotocols are advertised on the handshake.
116
+
117
+ ## What's different from `roslib`
118
+
119
+ - Client-side **service advertising** is not supported — `foxglove_bridge` only exposes server-side
120
+ services. The `actionlib` / `ActionClient` APIs are not provided (actions are exposed as services by
121
+ `foxglove_bridge`, so wrap a goal service + feedback topic yourself if you need them).
122
+ - `Topic#advertise()` / `Topic#unadvertise()` are accepted but are no-ops — the adapter advertises
123
+ lazily on first `publish()`.
124
+ - `throttle_rate` is enforced **client-side** (leading-edge, minimum ms between delivered messages)
125
+ since `foxglove_bridge` has no per-subscription rate limiter. When unset or `0`, the subscribe path
126
+ registers the user callback directly — no wrapper, no clock read, no branch per message.
127
+ - `messageSchema` on `Topic` is adapter-specific and optional. When supplied, decoded messages are
128
+ parsed with the provided Zod schema before subscriber callbacks run.
129
+ - `compression`, `queue_size`, `queue_length`, `latch`, `reconnect_on_close` options on `Topic` are
130
+ accepted for API compatibility but ignored; `foxglove_bridge` negotiates transport concerns on its
131
+ own.
132
+
133
+ ## Requirements
134
+
135
+ - A browser-like environment: the adapter uses `WebSocket`, `TextEncoder`, `TextDecoder`, and
136
+ `DataView` from the global scope. Works in any modern browser and in Node.js 18+ with the built-in
137
+ `WebSocket` (Node 22+) or a polyfill (`ws`, `undici`).
138
+ - A `foxglove_bridge` instance on the ROS 2 side:
139
+ ```bash
140
+ sudo apt install ros-$ROS_DISTRO-foxglove-bridge
141
+ ros2 launch foxglove_bridge foxglove_bridge_launch.xml port:=8765
142
+ ```
143
+
144
+ ## TF resolution
145
+
146
+ `ROS2TFClient` subscribes to `/tf` and `/tf_static`, builds a parent→child transform graph, and
147
+ resolves `fixedFrame → frameId` across any connected frames by walking both sides to their lowest
148
+ common ancestor, matching RViz-style sibling and ancestor lookups. Cycles in the tree return `null`
149
+ rather than crashing, non-unit quaternions are normalized at the subscription edge, and subscribers
150
+ receive an updated transform any time a link in their chain changes.
151
+
152
+ `rate` on `ROS2TFClient` limits `/tf` processing client-side in Hz. `/tf_static` is never throttled
153
+ because static transforms are latched and should not be dropped. `getFrameIds()` and
154
+ `addFramesListener()` expose the sorted set of parent and child frames seen so far for frame-selection
155
+ UIs.
156
+
157
+ ## License
158
+
159
+ MIT © Noah Wardlow