Skip to content

Commit 9f56947

Browse files
authored
Enforce member privacy using Private Elements (#1021)
1 parent 26c1107 commit 9f56947

File tree

9 files changed

+48
-71
lines changed

9 files changed

+48
-71
lines changed

src/core/Action.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,8 @@ export default class Action<
2222
TResult = unknown,
2323
> extends EventEmitter {
2424
isAdvertised = false;
25-
/**
26-
* @private
27-
*/
28-
_actionCallback: ((goal: TGoal, id: string) => void) | null = null;
29-
/**
30-
* @private
31-
*/
32-
_cancelCallback: ((id: string) => void) | null = null;
25+
#actionCallback: ((goal: TGoal, id: string) => void) | null = null;
26+
#cancelCallback: ((id: string) => void) | null = null;
3327
ros: Ros;
3428
name: string;
3529
actionType: string;
@@ -141,9 +135,9 @@ export default class Action<
141135
return;
142136
}
143137

144-
this._actionCallback = actionCallback;
145-
this._cancelCallback = cancelCallback;
146-
this.ros.on(this.name, this._executeAction.bind(this));
138+
this.#actionCallback = actionCallback;
139+
this.#cancelCallback = cancelCallback;
140+
this.ros.on(this.name, this.#executeAction.bind(this));
147141
this.ros.callOnConnection({
148142
op: "advertise_action",
149143
type: this.actionType,
@@ -175,24 +169,24 @@ export default class Action<
175169
* @param rosbridgeRequest.id - The ID of the action goal.
176170
* @param rosbridgeRequest.args - The arguments of the action goal.
177171
*/
178-
_executeAction(rosbridgeRequest: { id: string; args: TGoal }) {
172+
#executeAction(rosbridgeRequest: { id: string; args: TGoal }) {
179173
const id = rosbridgeRequest.id;
180174

181175
// If a cancellation callback exists, call it when a cancellation event is emitted.
182176
if (typeof id === "string") {
183177
this.ros.on(id, (message) => {
184178
if (
185179
isRosbridgeCancelActionGoalMessage(message) &&
186-
typeof this._cancelCallback === "function"
180+
typeof this.#cancelCallback === "function"
187181
) {
188-
this._cancelCallback(id);
182+
this.#cancelCallback(id);
189183
}
190184
});
191185
}
192186

193187
// Call the action goal execution function provided.
194-
if (typeof this._actionCallback === "function") {
195-
this._actionCallback(rosbridgeRequest.args, id);
188+
if (typeof this.#actionCallback === "function") {
189+
this.#actionCallback(rosbridgeRequest.args, id);
196190
}
197191
}
198192

src/core/Ros.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,8 @@ export default class Ros extends EventEmitter {
7676
* Create the appropriate transport based on transport library configuration
7777
* @param url - WebSocket URL or RTCDataChannel label for rosbridge.
7878
* @returns The created transport
79-
* @private
8079
*/
81-
async createTransport(
80+
async #createTransport(
8281
url: string,
8382
): Promise<WebSocket | RTCDataChannel | import("ws").WebSocket | null> {
8483
if (this.transportLibrary.constructor.name === "RTCPeerConnection") {
@@ -116,7 +115,7 @@ export default class Ros extends EventEmitter {
116115
* @param url - WebSocket URL or RTCDataChannel label for rosbridge.
117116
*/
118117
async connect(url: string) {
119-
const transport = await this.createTransport(url);
118+
const transport = await this.#createTransport(url);
120119

121120
if (!transport) {
122121
return; // Already connected
@@ -135,7 +134,7 @@ export default class Ros extends EventEmitter {
135134
this.emit("error", event);
136135
},
137136
onMessage: (message) => {
138-
this.handleMessage(message);
137+
this.#handleMessage(message);
139138
},
140139
decoder: this.transportOptions.decoder,
141140
});
@@ -144,9 +143,8 @@ export default class Ros extends EventEmitter {
144143
/**
145144
* Handle processed messages from SocketAdapter
146145
* @param message
147-
* @private
148146
*/
149-
handleMessage(message: RosbridgeMessage) {
147+
#handleMessage(message: RosbridgeMessage) {
150148
if (isRosbridgePublishMessage(message)) {
151149
this.emit(message.topic, message.msg);
152150
} else if (isRosbridgeServiceResponseMessage(message)) {

src/core/Service.ts

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,21 @@ import Ros from "./Ros.js";
1616
export default class Service<TRequest, TResponse> extends EventEmitter {
1717
/**
1818
* Stores a reference to the most recent service callback advertised so it can be removed from the EventEmitter during un-advertisement
19-
* @private
2019
*/
21-
_serviceCallback:
20+
#serviceCallback:
2221
| ((
2322
rosbridgeRequest: RosbridgeCallServiceMessage<TRequest>,
2423
) => void | Promise<void>)
2524
| null = null;
2625
isAdvertised = false;
2726
/**
2827
* Queue for serializing advertise/unadvertise operations to prevent race conditions
29-
* @private
3028
*/
31-
_operationQueue = Promise.resolve();
29+
#operationQueue = Promise.resolve();
3230
/**
3331
* Track if an unadvertise operation is pending to prevent double operations
34-
* @private
3532
*/
36-
_pendingUnadvertise = false;
33+
#pendingUnadvertise = false;
3734
ros: Ros;
3835
name: string;
3936
serviceType: string;
@@ -115,15 +112,15 @@ export default class Service<TRequest, TResponse> extends EventEmitter {
115112
callback: (request: TRequest, response: Partial<TResponse>) => boolean,
116113
): Promise<void> {
117114
// Queue this operation to prevent race conditions
118-
this._operationQueue = this._operationQueue
115+
this.#operationQueue = this.#operationQueue
119116
.then(async () => {
120117
// If already advertised, unadvertise first
121118
if (this.isAdvertised) {
122-
await this._doUnadvertise();
119+
await this.#doUnadvertise();
123120
}
124121

125122
// Store the new callback for removal during un-advertisement
126-
this._serviceCallback = (rosbridgeRequest) => {
123+
this.#serviceCallback = (rosbridgeRequest) => {
127124
const response = {};
128125
let success: boolean;
129126
try {
@@ -150,7 +147,7 @@ export default class Service<TRequest, TResponse> extends EventEmitter {
150147
}
151148
};
152149

153-
this.ros.on(this.name, this._serviceCallback);
150+
this.ros.on(this.name, this.#serviceCallback);
154151
this.ros.callOnConnection({
155152
op: "advertise_service",
156153
type: this.serviceType,
@@ -163,19 +160,18 @@ export default class Service<TRequest, TResponse> extends EventEmitter {
163160
throw err;
164161
});
165162

166-
return this._operationQueue;
163+
return this.#operationQueue;
167164
}
168165

169166
/**
170167
* Internal method to perform unadvertisement without queueing
171-
* @private
172168
*/
173-
async _doUnadvertise() {
174-
if (!this.isAdvertised || this._pendingUnadvertise) {
169+
async #doUnadvertise() {
170+
if (!this.isAdvertised || this.#pendingUnadvertise) {
175171
return;
176172
}
177173

178-
this._pendingUnadvertise = true;
174+
this.#pendingUnadvertise = true;
179175

180176
try {
181177
/*
@@ -185,9 +181,9 @@ export default class Service<TRequest, TResponse> extends EventEmitter {
185181
this.isAdvertised = false;
186182

187183
// Remove the registered callback to stop processing new requests
188-
if (this._serviceCallback) {
189-
this.ros.off(this.name, this._serviceCallback);
190-
this._serviceCallback = null;
184+
if (this.#serviceCallback) {
185+
this.ros.off(this.name, this.#serviceCallback);
186+
this.#serviceCallback = null;
191187
}
192188

193189
/*
@@ -200,22 +196,22 @@ export default class Service<TRequest, TResponse> extends EventEmitter {
200196
service: this.name,
201197
});
202198
} finally {
203-
this._pendingUnadvertise = false;
199+
this.#pendingUnadvertise = false;
204200
}
205201
}
206202

207203
async unadvertise(): Promise<void> {
208204
// Queue this operation to prevent race conditions
209-
this._operationQueue = this._operationQueue
205+
this.#operationQueue = this.#operationQueue
210206
.then(async () => {
211-
await this._doUnadvertise();
207+
await this.#doUnadvertise();
212208
})
213209
.catch((err) => {
214210
this.emit("error", err);
215211
throw err;
216212
});
217213

218-
return this._operationQueue;
214+
return this.#operationQueue;
219215
}
220216

221217
/**
@@ -226,14 +222,14 @@ export default class Service<TRequest, TResponse> extends EventEmitter {
226222
callback: (request: TRequest) => Promise<TResponse>,
227223
): Promise<void> {
228224
// Queue this operation to prevent race conditions
229-
this._operationQueue = this._operationQueue
225+
this.#operationQueue = this.#operationQueue
230226
.then(async () => {
231227
// If already advertised, unadvertise first
232228
if (this.isAdvertised) {
233-
await this._doUnadvertise();
229+
await this.#doUnadvertise();
234230
}
235231

236-
this._serviceCallback = async (rosbridgeRequest) => {
232+
this.#serviceCallback = async (rosbridgeRequest) => {
237233
try {
238234
this.ros.callOnConnection({
239235
op: "service_response",
@@ -252,7 +248,7 @@ export default class Service<TRequest, TResponse> extends EventEmitter {
252248
} satisfies RosbridgeServiceResponseMessage<TResponse>);
253249
}
254250
};
255-
this.ros.on(this.name, this._serviceCallback);
251+
this.ros.on(this.name, this.#serviceCallback);
256252
this.ros.callOnConnection({
257253
op: "advertise_service",
258254
type: this.serviceType,
@@ -265,6 +261,6 @@ export default class Service<TRequest, TResponse> extends EventEmitter {
265261
throw err;
266262
});
267263

268-
return this._operationQueue;
264+
return this.#operationQueue;
269265
}
270266
}

src/core/Topic.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export default class Topic<T> extends EventEmitter {
125125
}
126126
}
127127

128-
_messageCallback = (data: T) => {
128+
#messageCallback = (data: T) => {
129129
this.emit("message", data);
130130
};
131131
/**
@@ -142,7 +142,7 @@ export default class Topic<T> extends EventEmitter {
142142
if (this.subscribeId) {
143143
return;
144144
}
145-
this.ros.on(this.name, this._messageCallback);
145+
this.ros.on(this.name, this.#messageCallback);
146146
this.subscribeId =
147147
"subscribe:" + this.name + ":" + (++this.ros.idCounter).toString();
148148

@@ -177,7 +177,7 @@ export default class Topic<T> extends EventEmitter {
177177
return;
178178
}
179179
// Note: Don't call this.removeAllListeners, allow client to handle that themselves
180-
this.ros.off(this.name, this._messageCallback);
180+
this.ros.off(this.name, this.#messageCallback);
181181
if (this.reconnect_on_close) {
182182
this.ros.off("close", this.reconnectFunc);
183183
}

src/tf/BaseTFClient.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export default class BaseTFClient extends EventEmitter {
1212
{ transform?: Transform; cbs: ((tf: Transform) => void)[] }
1313
> = {};
1414
republisherUpdateRequested = false;
15-
_isDisposed = false;
1615
ros: Ros;
1716
fixedFrame: string;
1817
angularThres: number;
@@ -159,12 +158,4 @@ export default class BaseTFClient extends EventEmitter {
159158
delete this.frameInfos[frameID];
160159
}
161160
}
162-
163-
/**
164-
* Basic dispose functionality. Subclasses should override to add
165-
* their specific cleanup logic.
166-
*/
167-
dispose() {
168-
this._isDisposed = true;
169-
}
170161
}

src/tf/ROS2TFClient.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ export default class ROS2TFClient extends BaseTFClient {
6363
* Unsubscribe and unadvertise all topics associated with this TFClient.
6464
*/
6565
dispose() {
66-
super.dispose();
6766
if (this.goal_id !== "") {
6867
this.actionClient.cancelGoal(this.goal_id);
6968
}

src/tf/TFClient.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ export default class TFClient extends BaseTFClient {
2626
tf2_web_republisher.RepublishTFsRequest,
2727
tf2_web_republisher.RepublishTFsResponse
2828
>;
29-
_subscribeCB: ((tf: tf2_msgs.TFMessage) => void) | undefined = undefined;
29+
#subscribeCB: ((tf: tf2_msgs.TFMessage) => void) | undefined = undefined;
30+
#isDisposed = false;
3031

3132
/**
3233
* @param options
@@ -130,7 +131,7 @@ export default class TFClient extends BaseTFClient {
130131
* Do not setup a topic subscription if already disposed. Prevents a race condition where
131132
* The dispose() function is called before the service call receives a response.
132133
*/
133-
if (this._isDisposed) {
134+
if (this.#isDisposed) {
134135
return;
135136
}
136137

@@ -139,27 +140,27 @@ export default class TFClient extends BaseTFClient {
139140
* the republisher stops publishing it
140141
*/
141142
if (this.currentTopic) {
142-
this.currentTopic.unsubscribe(this._subscribeCB);
143+
this.currentTopic.unsubscribe(this.#subscribeCB);
143144
}
144145

145146
this.currentTopic = new Topic<tf2_msgs.TFMessage>({
146147
ros: this.ros,
147148
name: response.topic_name,
148149
messageType: "tf2_web_republisher/TFArray",
149150
});
150-
this._subscribeCB = this.processTFArray.bind(this);
151+
this.#subscribeCB = this.processTFArray.bind(this);
151152
// @ts-expect-error Function was bound above
152-
this.currentTopic.subscribe(this._subscribeCB);
153+
this.currentTopic.subscribe(this.#subscribeCB);
153154
}
154155

155156
/**
156157
* Unsubscribe and unadvertise all topics associated with this TFClient.
157158
*/
158159
dispose() {
159-
super.dispose();
160+
this.#isDisposed = true;
160161
this.actionClient.dispose();
161162
if (this.currentTopic) {
162-
this.currentTopic.unsubscribe(this._subscribeCB);
163+
this.currentTopic.unsubscribe(this.#subscribeCB);
163164
}
164165
}
165166
}

src/util/decompressPng.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import pngparse from "pngparse";
1010
* gzipping over WebSockets * is not supported yet), this function decodes
1111
* the "image" as a Base64 string.
1212
*
13-
* @private
1413
* @param data - An object containing the PNG data.
1514
* @param callback - Function with the following params:
1615
*/

src/util/shim/decompressPng.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
* gzipping over WebSockets * is not supported yet), this function places the
99
* "image" in a canvas element then decodes the * "image" as a Base64 string.
1010
*
11-
* @private
1211
* @param data - A string containing the PNG data.
1312
* @param callback - Function with the following params:
1413
*/

0 commit comments

Comments
 (0)