Skip to content

Commit 6e18cd5

Browse files
mahmoud-ghalayiniminggangw
authored andcommitted
Add TypeScript definitions and non-throwing variants for validator (#1345)
- Add complete TypeScript definitions for all validator methods (types/validator.d.ts) - Add isValidFullTopicName(), isValidNodeName(), isValidTopicName(), isValidNamespace() methods that return boolean instead of throwing - Non-throwing variants enable simple conditional checks without try/catch blocks
1 parent ec9b521 commit 6e18cd5

4 files changed

Lines changed: 273 additions & 7 deletions

File tree

lib/validator.js

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ let validator = {
2828

2929
/**
3030
* Validate a given topic or service name, and throw an error if invalid.
31-
* @param {string} topic - The name of topic/service. and it must be fully-qualified and already expanded.
32-
* @return {boolean} - True if it is valid.
31+
* @param {string} topic - The name of topic/service. Must be fully-qualified and already expanded.
32+
* @returns {true} Always returns true if valid.
33+
* @throws {TypeValidationError} If topic is not a string.
34+
* @throws {NameValidationError} If the topic name is invalid.
3335
*/
3436
validateFullTopicName(topic) {
3537
if (typeof topic !== 'string') {
@@ -43,10 +45,24 @@ let validator = {
4345
throw this._createErrorFromValidation(result, topic, 'topic');
4446
},
4547

48+
/**
49+
* Check if a fully-qualified topic name is valid without throwing.
50+
* @param {string} topic - The name of topic/service. Must be fully-qualified and already expanded.
51+
* @returns {boolean} True if valid, false otherwise.
52+
*/
53+
isValidFullTopicName(topic) {
54+
if (typeof topic !== 'string') {
55+
return false;
56+
}
57+
return rclnodejs.validateFullTopicName(topic) === null;
58+
},
59+
4660
/**
4761
* Validate a given node name, and throw an error if invalid.
4862
* @param {string} name - The name of node.
49-
* @return {boolean} - True if it is valid.
63+
* @returns {true} Always returns true if valid.
64+
* @throws {TypeValidationError} If name is not a string.
65+
* @throws {NameValidationError} If the node name is invalid.
5066
*/
5167
validateNodeName(name) {
5268
if (typeof name !== 'string') {
@@ -60,10 +76,24 @@ let validator = {
6076
throw this._createErrorFromValidation(result, name, 'node');
6177
},
6278

79+
/**
80+
* Check if a node name is valid without throwing.
81+
* @param {string} name - The name of node.
82+
* @returns {boolean} True if valid, false otherwise.
83+
*/
84+
isValidNodeName(name) {
85+
if (typeof name !== 'string') {
86+
return false;
87+
}
88+
return rclnodejs.validateNodeName(name) === null;
89+
},
90+
6391
/**
6492
* Validate a given topic or service name, and throw an error if invalid.
65-
* @param {string} topic - The name of topic/service and does not have to be fully-qualified and is not expanded.
66-
* @return {boolean} - True if it is valid.
93+
* @param {string} topic - The name of topic/service. Does not have to be fully-qualified.
94+
* @returns {true} Always returns true if valid.
95+
* @throws {TypeValidationError} If topic is not a string.
96+
* @throws {NameValidationError} If the topic name is invalid.
6797
*/
6898
validateTopicName(topic) {
6999
if (typeof topic !== 'string') {
@@ -77,10 +107,24 @@ let validator = {
77107
throw this._createErrorFromValidation(result, topic, 'topic');
78108
},
79109

110+
/**
111+
* Check if a topic name is valid without throwing.
112+
* @param {string} topic - The name of topic/service. Does not have to be fully-qualified.
113+
* @returns {boolean} True if valid, false otherwise.
114+
*/
115+
isValidTopicName(topic) {
116+
if (typeof topic !== 'string') {
117+
return false;
118+
}
119+
return rclnodejs.validateTopicName(topic) === null;
120+
},
121+
80122
/**
81123
* Validate a given namespace, and throw an error if invalid.
82-
* @param {string} namespace - The namespace to be validated
83-
* @return {boolean} - True if it is valid.
124+
* @param {string} namespace - The namespace to be validated.
125+
* @returns {true} Always returns true if valid.
126+
* @throws {TypeValidationError} If namespace is not a string.
127+
* @throws {NameValidationError} If the namespace is invalid.
84128
*/
85129
validateNamespace(namespace) {
86130
if (typeof namespace !== 'string') {
@@ -93,6 +137,18 @@ let validator = {
93137
}
94138
throw this._createErrorFromValidation(result, namespace, 'namespace');
95139
},
140+
141+
/**
142+
* Check if a namespace is valid without throwing.
143+
* @param {string} namespace - The namespace to be validated.
144+
* @returns {boolean} True if valid, false otherwise.
145+
*/
146+
isValidNamespace(namespace) {
147+
if (typeof namespace !== 'string') {
148+
return false;
149+
}
150+
return rclnodejs.validateNamespace(namespace) === null;
151+
},
96152
};
97153

98154
module.exports = validator;

test/test-validator.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,127 @@ describe('rclnodejs validator testing', function () {
178178
'invalid namespace!'
179179
);
180180
});
181+
182+
describe('isValidFullTopicName', function () {
183+
it('should return true for valid fully-qualified topics', function () {
184+
assert.strictEqual(
185+
rclnodejs.validator.isValidFullTopicName('/chatter'),
186+
true
187+
);
188+
assert.strictEqual(
189+
rclnodejs.validator.isValidFullTopicName('/node_name/chatter'),
190+
true
191+
);
192+
assert.strictEqual(
193+
rclnodejs.validator.isValidFullTopicName('/ns/node_name/chatter'),
194+
true
195+
);
196+
});
197+
198+
it('should return false for invalid topics', function () {
199+
assert.strictEqual(
200+
rclnodejs.validator.isValidFullTopicName('/invalid_topic?'),
201+
false
202+
);
203+
assert.strictEqual(
204+
rclnodejs.validator.isValidFullTopicName('relative_topic'),
205+
false
206+
);
207+
});
208+
209+
it('should return false for non-string input', function () {
210+
assert.strictEqual(rclnodejs.validator.isValidFullTopicName(123), false);
211+
assert.strictEqual(rclnodejs.validator.isValidFullTopicName(null), false);
212+
assert.strictEqual(
213+
rclnodejs.validator.isValidFullTopicName(undefined),
214+
false
215+
);
216+
});
217+
});
218+
219+
describe('isValidNodeName', function () {
220+
it('should return true for valid node names', function () {
221+
assert.strictEqual(rclnodejs.validator.isValidNodeName('my_node'), true);
222+
assert.strictEqual(rclnodejs.validator.isValidNodeName('node123'), true);
223+
});
224+
225+
it('should return false for invalid node names', function () {
226+
assert.strictEqual(rclnodejs.validator.isValidNodeName(''), false);
227+
assert.strictEqual(
228+
rclnodejs.validator.isValidNodeName('invalid_node?'),
229+
false
230+
);
231+
assert.strictEqual(
232+
rclnodejs.validator.isValidNodeName('/invalid_node'),
233+
false
234+
);
235+
});
236+
237+
it('should return false for non-string input', function () {
238+
assert.strictEqual(rclnodejs.validator.isValidNodeName(123), false);
239+
assert.strictEqual(rclnodejs.validator.isValidNodeName(null), false);
240+
});
241+
});
242+
243+
describe('isValidTopicName', function () {
244+
it('should return true for valid topic names', function () {
245+
assert.strictEqual(rclnodejs.validator.isValidTopicName('chatter'), true);
246+
assert.strictEqual(
247+
rclnodejs.validator.isValidTopicName('{node}/chatter'),
248+
true
249+
);
250+
assert.strictEqual(
251+
rclnodejs.validator.isValidTopicName('~/chatter'),
252+
true
253+
);
254+
assert.strictEqual(
255+
rclnodejs.validator.isValidTopicName('/my/topic'),
256+
true
257+
);
258+
});
259+
260+
it('should return false for invalid topic names', function () {
261+
assert.strictEqual(rclnodejs.validator.isValidTopicName(''), false);
262+
assert.strictEqual(
263+
rclnodejs.validator.isValidTopicName('/invalid_topic?'),
264+
false
265+
);
266+
assert.strictEqual(
267+
rclnodejs.validator.isValidTopicName('invalid/42topic'),
268+
false
269+
);
270+
});
271+
272+
it('should return false for non-string input', function () {
273+
assert.strictEqual(rclnodejs.validator.isValidTopicName(123), false);
274+
});
275+
});
276+
277+
describe('isValidNamespace', function () {
278+
it('should return true for valid namespaces', function () {
279+
assert.strictEqual(rclnodejs.validator.isValidNamespace('/my_ns'), true);
280+
assert.strictEqual(rclnodejs.validator.isValidNamespace('/'), true);
281+
assert.strictEqual(
282+
rclnodejs.validator.isValidNamespace('/deep/namespace'),
283+
true
284+
);
285+
});
286+
287+
it('should return false for invalid namespaces', function () {
288+
assert.strictEqual(rclnodejs.validator.isValidNamespace(''), false);
289+
assert.strictEqual(
290+
rclnodejs.validator.isValidNamespace('invalid_namespace'),
291+
false
292+
);
293+
assert.strictEqual(
294+
rclnodejs.validator.isValidNamespace('invalid namespace'),
295+
false
296+
);
297+
});
298+
299+
it('should return false for non-string input', function () {
300+
assert.strictEqual(rclnodejs.validator.isValidNamespace(123), false);
301+
assert.strictEqual(rclnodejs.validator.isValidNamespace(null), false);
302+
});
303+
});
181304
});

types/base.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@
2828
/// <reference path="./time_source.d.ts" />
2929
/// <reference path="./time.d.ts" />
3030
/// <reference path="./timer.d.ts" />
31+
/// <reference path="./validator.d.ts" />

types/validator.d.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright (c) 2025 Mahmoud Alghalayini. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
declare module 'rclnodejs' {
16+
/**
17+
* Validator for ROS 2 names (topics, services, nodes, namespaces).
18+
*/
19+
namespace validator {
20+
/**
21+
* Validate a fully-qualified topic or service name.
22+
* The name must be fully-qualified and already expanded.
23+
* @param topic - The topic/service name to validate.
24+
* @returns Always returns true if valid.
25+
* @throws TypeValidationError if topic is not a string.
26+
* @throws NameValidationError if the name is invalid.
27+
*/
28+
function validateFullTopicName(topic: string): true;
29+
30+
/**
31+
* Check if a fully-qualified topic name is valid without throwing.
32+
* @param topic - The topic/service name to check.
33+
* @returns True if valid, false otherwise.
34+
*/
35+
function isValidFullTopicName(topic: string): boolean;
36+
37+
/**
38+
* Validate a node name.
39+
* @param name - The node name to validate.
40+
* @returns Always returns true if valid.
41+
* @throws TypeValidationError if name is not a string.
42+
* @throws NameValidationError if the name is invalid.
43+
*/
44+
function validateNodeName(name: string): true;
45+
46+
/**
47+
* Check if a node name is valid without throwing.
48+
* @param name - The node name to check.
49+
* @returns True if valid, false otherwise.
50+
*/
51+
function isValidNodeName(name: string): boolean;
52+
53+
/**
54+
* Validate a topic or service name.
55+
* The name does not have to be fully-qualified and is not expanded.
56+
* @param topic - The topic/service name to validate.
57+
* @returns Always returns true if valid.
58+
* @throws TypeValidationError if topic is not a string.
59+
* @throws NameValidationError if the name is invalid.
60+
*/
61+
function validateTopicName(topic: string): true;
62+
63+
/**
64+
* Check if a topic name is valid without throwing.
65+
* @param topic - The topic/service name to check.
66+
* @returns True if valid, false otherwise.
67+
*/
68+
function isValidTopicName(topic: string): boolean;
69+
70+
/**
71+
* Validate a namespace.
72+
* @param namespace - The namespace to validate.
73+
* @returns Always returns true if valid.
74+
* @throws TypeValidationError if namespace is not a string.
75+
* @throws NameValidationError if the namespace is invalid.
76+
*/
77+
function validateNamespace(namespace: string): true;
78+
79+
/**
80+
* Check if a namespace is valid without throwing.
81+
* @param namespace - The namespace to check.
82+
* @returns True if valid, false otherwise.
83+
*/
84+
function isValidNamespace(namespace: string): boolean;
85+
}
86+
}

0 commit comments

Comments
 (0)