rclnodejs 1.6.0 → 1.7.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.
@@ -19,6 +19,7 @@ const { Clock, ROSClock } = require('./clock.js');
19
19
  const { ClockType } = Clock;
20
20
  const { Parameter, ParameterType } = require('./parameter.js');
21
21
  const Time = require('./time.js');
22
+ const { TypeValidationError, OperationError } = require('./errors.js');
22
23
 
23
24
  const USE_SIM_TIME_PARAM = 'use_sim_time';
24
25
  const CLOCK_TOPIC = '/clock';
@@ -102,7 +103,9 @@ class TimeSource {
102
103
  */
103
104
  attachNode(node) {
104
105
  if ((!node) instanceof rclnodejs.ShadowNode) {
105
- throw new TypeError('Invalid argument, must be type of Node');
106
+ throw new TypeValidationError('node', node, 'Node', {
107
+ entityType: 'time source',
108
+ });
106
109
  }
107
110
 
108
111
  if (this._node) {
@@ -150,8 +153,12 @@ class TimeSource {
150
153
  detachNode() {
151
154
  if (this._clockSubscription) {
152
155
  if (!this._node) {
153
- throw new Error(
154
- 'Unable to destroy previously created clock subscription'
156
+ throw new OperationError(
157
+ 'Unable to destroy previously created clock subscription',
158
+ {
159
+ code: 'NO_NODE_ATTACHED',
160
+ entityType: 'time source',
161
+ }
155
162
  );
156
163
  }
157
164
  this._node.destroySubscription(this._clockSubscription);
@@ -167,7 +174,9 @@ class TimeSource {
167
174
  */
168
175
  attachClock(clock) {
169
176
  if (!(clock instanceof ROSClock)) {
170
- throw new TypeError('Only clocks with type ROS_TIME can be attached.');
177
+ throw new TypeValidationError('clock', clock, 'ROSClock', {
178
+ entityType: 'time source',
179
+ });
171
180
  }
172
181
  clock.rosTimeOverride = this._lastTimeSet;
173
182
  clock.isRosTimeActive = this._isRosTimeActive;
package/lib/utils.js CHANGED
@@ -15,6 +15,7 @@
15
15
  const fs = require('fs');
16
16
  const fsPromises = require('fs/promises');
17
17
  const path = require('path');
18
+ const { ValidationError } = require('./errors.js');
18
19
 
19
20
  /**
20
21
  * Ensure directory exists, create recursively if needed (async)
@@ -182,6 +183,25 @@ function detectUbuntuCodename() {
182
183
  }
183
184
  }
184
185
 
186
+ /**
187
+ * Normalize a ROS 2 node name by removing the leading slash if present.
188
+ *
189
+ * ROS 2 node names may be specified with or without a leading slash depending
190
+ * on the context. This utility ensures consistent representation without the
191
+ * leading slash, which is the standard format for most ROS 2 APIs.
192
+ *
193
+ * @param {string} nodeName - The node name to normalize
194
+ * @returns {string} The normalized node name without leading slash
195
+ *
196
+ * @example
197
+ * normalizeNodeName('my_node') // 'my_node'
198
+ * normalizeNodeName('/my_node') // 'my_node'
199
+ * normalizeNodeName('/ns/my_node') // 'ns/my_node'
200
+ */
201
+ function normalizeNodeName(nodeName) {
202
+ return nodeName.startsWith('/') ? nodeName.substring(1) : nodeName;
203
+ }
204
+
185
205
  /**
186
206
  * Check if two numbers are equal within a given tolerance.
187
207
  *
@@ -294,7 +314,12 @@ function compareVersions(version1, version2, operator) {
294
314
  case '!==':
295
315
  return cmp !== 0;
296
316
  default:
297
- throw new Error(`Invalid operator: ${operator}`);
317
+ throw new ValidationError(`Invalid operator: ${operator}`, {
318
+ code: 'INVALID_OPERATOR',
319
+ argumentName: 'operator',
320
+ providedValue: operator,
321
+ expectedType: "'eq' | 'ne' | 'lt' | 'lte' | 'gt' | 'gte'",
322
+ });
298
323
  }
299
324
  }
300
325
 
@@ -302,6 +327,7 @@ module.exports = {
302
327
  // General utilities
303
328
  detectUbuntuCodename,
304
329
  isClose,
330
+ normalizeNodeName,
305
331
 
306
332
  // File system utilities (async)
307
333
  ensureDir,
package/lib/validator.js CHANGED
@@ -15,16 +15,15 @@
15
15
  'use strict';
16
16
 
17
17
  const rclnodejs = require('./native_loader.js');
18
+ const { TypeValidationError, NameValidationError } = require('./errors.js');
18
19
 
19
20
  /**
20
21
  * An object - Representing a validator in ROS.
21
22
  * @exports validator
22
23
  */
23
24
  let validator = {
24
- _createErrorFromValidation: function (result) {
25
- let err = new Error(result[0]);
26
- err.invalidIndex = result[1];
27
- return err;
25
+ _createErrorFromValidation: function (result, nameValue, nameType) {
26
+ return new NameValidationError(nameValue, nameType, result[0], result[1]);
28
27
  },
29
28
 
30
29
  /**
@@ -34,14 +33,14 @@ let validator = {
34
33
  */
35
34
  validateFullTopicName(topic) {
36
35
  if (typeof topic !== 'string') {
37
- throw new TypeError('Invalid argument');
36
+ throw new TypeValidationError('topic', topic, 'string');
38
37
  }
39
38
 
40
39
  let result = rclnodejs.validateFullTopicName(topic);
41
40
  if (result === null) {
42
41
  return true;
43
42
  }
44
- throw this._createErrorFromValidation(result);
43
+ throw this._createErrorFromValidation(result, topic, 'topic');
45
44
  },
46
45
 
47
46
  /**
@@ -51,14 +50,14 @@ let validator = {
51
50
  */
52
51
  validateNodeName(name) {
53
52
  if (typeof name !== 'string') {
54
- throw new TypeError('Invalid argument');
53
+ throw new TypeValidationError('name', name, 'string');
55
54
  }
56
55
 
57
56
  let result = rclnodejs.validateNodeName(name);
58
57
  if (result === null) {
59
58
  return true;
60
59
  }
61
- throw this._createErrorFromValidation(result);
60
+ throw this._createErrorFromValidation(result, name, 'node');
62
61
  },
63
62
 
64
63
  /**
@@ -68,14 +67,14 @@ let validator = {
68
67
  */
69
68
  validateTopicName(topic) {
70
69
  if (typeof topic !== 'string') {
71
- throw new TypeError('Invalid argument');
70
+ throw new TypeValidationError('topic', topic, 'string');
72
71
  }
73
72
 
74
73
  let result = rclnodejs.validateTopicName(topic);
75
74
  if (result === null) {
76
75
  return true;
77
76
  }
78
- throw this._createErrorFromValidation(result);
77
+ throw this._createErrorFromValidation(result, topic, 'topic');
79
78
  },
80
79
 
81
80
  /**
@@ -85,14 +84,14 @@ let validator = {
85
84
  */
86
85
  validateNamespace(namespace) {
87
86
  if (typeof namespace !== 'string') {
88
- throw new TypeError('Invalid argument');
87
+ throw new TypeValidationError('namespace', namespace, 'string');
89
88
  }
90
89
 
91
90
  let result = rclnodejs.validateNamespace(namespace);
92
91
  if (result === null) {
93
92
  return true;
94
93
  }
95
- throw this._createErrorFromValidation(result);
94
+ throw this._createErrorFromValidation(result, namespace, 'namespace');
96
95
  },
97
96
  };
98
97
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rclnodejs",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "ROS2.0 JavaScript client with Node.js",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
package/types/base.d.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  /// <reference path="./distro.d.ts" />
9
9
  /// <reference path="./duration.d.ts" />
10
10
  /// <reference path="./entity.d.ts" />
11
+ /// <reference path="./errors.d.ts" />
11
12
  /// <reference path="./guard_condition.d.ts" />
12
13
  /// <reference path="./interfaces.d.ts" />
13
14
  /// <reference path="./lifecycle.d.ts" />
@@ -16,6 +17,8 @@
16
17
  /// <reference path="./node.d.ts" />
17
18
  /// <reference path="./node_options.d.ts" />
18
19
  /// <reference path="./parameter.d.ts" />
20
+ /// <reference path="./parameter_client.d.ts" />
21
+ /// <reference path="./parameter_watcher.d.ts" />
19
22
  /// <reference path="./publisher.d.ts" />
20
23
  /// <reference path="./qos.d.ts" />
21
24
  /// <reference path="./rate.d.ts" />
package/types/client.d.ts CHANGED
@@ -14,6 +14,21 @@ declare module 'rclnodejs' {
14
14
  callback: Client.ResponseCallback<T>
15
15
  ): void;
16
16
 
17
+ /**
18
+ * Make a service request and return a Promise that resolves with the response.
19
+ *
20
+ * @param request - Request to be submitted.
21
+ * @param options - Optional parameters for the request.
22
+ * @returns Promise that resolves with the service response.
23
+ * @throws TimeoutError if the request times out (when options.timeout is exceeded).
24
+ * @throws AbortError if the request is manually aborted (via options.signal).
25
+ * @throws Error if the request fails for other reasons.
26
+ */
27
+ sendRequestAsync(
28
+ request: ServiceRequestMessage<T>,
29
+ options?: Client.AsyncRequestOptions
30
+ ): Promise<ServiceResponseMessage<T>>;
31
+
17
32
  /**
18
33
  * Checks if the service is ready.
19
34
  *
@@ -74,5 +89,26 @@ declare module 'rclnodejs' {
74
89
  export type ResponseCallback<T extends TypeClass<ServiceTypeClassName>> = (
75
90
  response: ServiceResponseMessage<T>
76
91
  ) => void;
92
+
93
+ /**
94
+ * Options for async service requests
95
+ */
96
+ export interface AsyncRequestOptions {
97
+ /**
98
+ * Timeout in milliseconds for the request.
99
+ * Internally uses AbortSignal.timeout() for standards compliance.
100
+ */
101
+ timeout?: number;
102
+
103
+ /**
104
+ * AbortSignal to cancel the request.
105
+ * When the signal is aborted, the request will be cancelled
106
+ * and the promise will reject with an AbortError.
107
+ *
108
+ * Can be combined with timeout parameter - whichever happens first
109
+ * will abort the request.
110
+ */
111
+ signal?: AbortSignal;
112
+ }
77
113
  }
78
114
  }