rclnodejs 1.5.2 → 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.
Files changed (59) hide show
  1. package/index.js +79 -3
  2. package/lib/action/client.js +55 -9
  3. package/lib/action/deferred.js +8 -2
  4. package/lib/action/server.js +10 -1
  5. package/lib/action/uuid.js +4 -1
  6. package/lib/client.js +152 -3
  7. package/lib/clock.js +4 -1
  8. package/lib/context.js +12 -2
  9. package/lib/duration.js +37 -12
  10. package/lib/errors.js +571 -0
  11. package/lib/event_handler.js +21 -4
  12. package/lib/interface_loader.js +52 -12
  13. package/lib/lifecycle.js +8 -2
  14. package/lib/logging.js +12 -3
  15. package/lib/message_serialization.js +179 -0
  16. package/lib/native_loader.js +9 -4
  17. package/lib/node.js +283 -47
  18. package/lib/parameter.js +176 -45
  19. package/lib/parameter_client.js +506 -0
  20. package/lib/parameter_watcher.js +309 -0
  21. package/lib/qos.js +22 -5
  22. package/lib/rate.js +6 -1
  23. package/lib/serialization.js +7 -2
  24. package/lib/subscription.js +16 -1
  25. package/lib/time.js +136 -21
  26. package/lib/time_source.js +13 -4
  27. package/lib/utils.js +313 -0
  28. package/lib/validator.js +11 -12
  29. package/package.json +2 -7
  30. package/prebuilds/linux-arm64/humble-jammy-arm64-rclnodejs.node +0 -0
  31. package/prebuilds/linux-arm64/jazzy-noble-arm64-rclnodejs.node +0 -0
  32. package/prebuilds/linux-arm64/kilted-noble-arm64-rclnodejs.node +0 -0
  33. package/prebuilds/linux-x64/humble-jammy-x64-rclnodejs.node +0 -0
  34. package/prebuilds/linux-x64/jazzy-noble-x64-rclnodejs.node +0 -0
  35. package/prebuilds/linux-x64/kilted-noble-x64-rclnodejs.node +0 -0
  36. package/rosidl_convertor/idl_convertor.js +3 -2
  37. package/rosidl_gen/generate_worker.js +1 -1
  38. package/rosidl_gen/idl_generator.js +11 -24
  39. package/rosidl_gen/index.js +1 -1
  40. package/rosidl_gen/templates/action-template.js +68 -0
  41. package/rosidl_gen/templates/message-template.js +1113 -0
  42. package/rosidl_gen/templates/service-event-template.js +31 -0
  43. package/rosidl_gen/templates/service-template.js +44 -0
  44. package/rosidl_parser/rosidl_parser.js +2 -2
  45. package/third_party/ref-napi/lib/ref.js +0 -45
  46. package/types/base.d.ts +3 -0
  47. package/types/client.d.ts +36 -0
  48. package/types/errors.d.ts +447 -0
  49. package/types/index.d.ts +17 -0
  50. package/types/interfaces.d.ts +1910 -1
  51. package/types/node.d.ts +56 -1
  52. package/types/parameter_client.d.ts +252 -0
  53. package/types/parameter_watcher.d.ts +104 -0
  54. package/rosidl_gen/templates/CMakeLists.dot +0 -40
  55. package/rosidl_gen/templates/action.dot +0 -50
  56. package/rosidl_gen/templates/message.dot +0 -851
  57. package/rosidl_gen/templates/package.dot +0 -16
  58. package/rosidl_gen/templates/service.dot +0 -26
  59. package/rosidl_gen/templates/service_event.dot +0 -10
package/lib/node.js CHANGED
@@ -31,7 +31,15 @@ const {
31
31
  Parameter,
32
32
  ParameterDescriptor,
33
33
  } = require('./parameter.js');
34
+ const { isValidSerializationMode } = require('./message_serialization.js');
35
+ const {
36
+ TypeValidationError,
37
+ RangeValidationError,
38
+ ValidationError,
39
+ } = require('./errors.js');
34
40
  const ParameterService = require('./parameter_service.js');
41
+ const ParameterClient = require('./parameter_client.js');
42
+ const ParameterWatcher = require('./parameter_watcher.js');
35
43
  const Publisher = require('./publisher.js');
36
44
  const QoS = require('./qos.js');
37
45
  const Rates = require('./rate.js');
@@ -73,8 +81,11 @@ class Node extends rclnodejs.ShadowNode {
73
81
  ) {
74
82
  super();
75
83
 
76
- if (typeof nodeName !== 'string' || typeof namespace !== 'string') {
77
- throw new TypeError('Invalid argument.');
84
+ if (typeof nodeName !== 'string') {
85
+ throw new TypeValidationError('nodeName', nodeName, 'string');
86
+ }
87
+ if (typeof namespace !== 'string') {
88
+ throw new TypeValidationError('namespace', namespace, 'string');
78
89
  }
79
90
 
80
91
  this._init(nodeName, namespace, options, context, args, useGlobalArguments);
@@ -110,6 +121,8 @@ class Node extends rclnodejs.ShadowNode {
110
121
  this._events = [];
111
122
  this._actionClients = [];
112
123
  this._actionServers = [];
124
+ this._parameterClients = [];
125
+ this._parameterWatchers = [];
113
126
  this._rateTimerServer = null;
114
127
  this._parameterDescriptors = new Map();
115
128
  this._parameters = new Map();
@@ -132,8 +145,13 @@ class Node extends rclnodejs.ShadowNode {
132
145
  if (options.parameterOverrides.length > 0) {
133
146
  for (const parameter of options.parameterOverrides) {
134
147
  if ((!parameter) instanceof Parameter) {
135
- throw new TypeError(
136
- 'Parameter-override must be an instance of Parameter.'
148
+ throw new TypeValidationError(
149
+ 'parameterOverride',
150
+ parameter,
151
+ 'Parameter instance',
152
+ {
153
+ nodeName: name,
154
+ }
137
155
  );
138
156
  }
139
157
  this._parameterOverrides.set(parameter.name, parameter);
@@ -513,7 +531,9 @@ class Node extends rclnodejs.ShadowNode {
513
531
  options !== undefined &&
514
532
  (options === null || typeof options !== 'object')
515
533
  ) {
516
- throw new TypeError('Invalid argument of options');
534
+ throw new TypeValidationError('options', options, 'object', {
535
+ nodeName: this.name(),
536
+ });
517
537
  }
518
538
 
519
539
  if (options === undefined) {
@@ -532,6 +552,21 @@ class Node extends rclnodejs.ShadowNode {
532
552
  options = Object.assign(options, { isRaw: false });
533
553
  }
534
554
 
555
+ if (options.serializationMode === undefined) {
556
+ options = Object.assign(options, { serializationMode: 'default' });
557
+ } else if (!isValidSerializationMode(options.serializationMode)) {
558
+ throw new ValidationError(
559
+ `Invalid serializationMode: ${options.serializationMode}. Valid modes are: 'default', 'plain', 'json'`,
560
+ {
561
+ code: 'INVALID_SERIALIZATION_MODE',
562
+ argumentName: 'serializationMode',
563
+ providedValue: options.serializationMode,
564
+ expectedType: "'default' | 'plain' | 'json'",
565
+ nodeName: this.name(),
566
+ }
567
+ );
568
+ }
569
+
535
570
  return options;
536
571
  }
537
572
 
@@ -549,8 +584,15 @@ class Node extends rclnodejs.ShadowNode {
549
584
  clock = arguments[3];
550
585
  }
551
586
 
552
- if (typeof period !== 'bigint' || typeof callback !== 'function') {
553
- throw new TypeError('Invalid argument');
587
+ if (typeof period !== 'bigint') {
588
+ throw new TypeValidationError('period', period, 'bigint', {
589
+ nodeName: this.name(),
590
+ });
591
+ }
592
+ if (typeof callback !== 'function') {
593
+ throw new TypeValidationError('callback', callback, 'function', {
594
+ nodeName: this.name(),
595
+ });
554
596
  }
555
597
 
556
598
  const timerClock = clock || this._clock;
@@ -575,13 +617,20 @@ class Node extends rclnodejs.ShadowNode {
575
617
  */
576
618
  async createRate(hz = 1) {
577
619
  if (typeof hz !== 'number') {
578
- throw new TypeError('Invalid argument');
620
+ throw new TypeValidationError('hz', hz, 'number', {
621
+ nodeName: this.name(),
622
+ });
579
623
  }
580
624
 
581
625
  const MAX_RATE_HZ_IN_MILLISECOND = 1000.0;
582
626
  if (hz <= 0.0 || hz > MAX_RATE_HZ_IN_MILLISECOND) {
583
- throw new RangeError(
584
- `Hz must be between 0.0 and ${MAX_RATE_HZ_IN_MILLISECOND}`
627
+ throw new RangeValidationError(
628
+ 'hz',
629
+ hz,
630
+ `0.0 < hz <= ${MAX_RATE_HZ_IN_MILLISECOND}`,
631
+ {
632
+ nodeName: this.name(),
633
+ }
585
634
  );
586
635
  }
587
636
 
@@ -626,12 +675,32 @@ class Node extends rclnodejs.ShadowNode {
626
675
  }
627
676
  options = this._validateOptions(options);
628
677
 
678
+ if (typeof typeClass !== 'function') {
679
+ throw new TypeValidationError('typeClass', typeClass, 'function', {
680
+ nodeName: this.name(),
681
+ entityType: 'publisher',
682
+ });
683
+ }
684
+ if (typeof topic !== 'string') {
685
+ throw new TypeValidationError('topic', topic, 'string', {
686
+ nodeName: this.name(),
687
+ entityType: 'publisher',
688
+ });
689
+ }
629
690
  if (
630
- typeof typeClass !== 'function' ||
631
- typeof topic !== 'string' ||
632
- (eventCallbacks && !(eventCallbacks instanceof PublisherEventCallbacks))
691
+ eventCallbacks &&
692
+ !(eventCallbacks instanceof PublisherEventCallbacks)
633
693
  ) {
634
- throw new TypeError('Invalid argument');
694
+ throw new TypeValidationError(
695
+ 'eventCallbacks',
696
+ eventCallbacks,
697
+ 'PublisherEventCallbacks',
698
+ {
699
+ nodeName: this.name(),
700
+ entityType: 'publisher',
701
+ entityName: topic,
702
+ }
703
+ );
635
704
  }
636
705
 
637
706
  let publisher = publisherClass.createPublisher(
@@ -666,6 +735,10 @@ class Node extends rclnodejs.ShadowNode {
666
735
  * @param {boolean} options.enableTypedArray - The topic will use TypedArray if necessary, default: true.
667
736
  * @param {QoS} options.qos - ROS Middleware "quality of service" settings for the subscription, default: QoS.profileDefault.
668
737
  * @param {boolean} options.isRaw - The topic is serialized when true, default: false.
738
+ * @param {string} [options.serializationMode='default'] - Controls message serialization format:
739
+ * 'default': Use native rclnodejs behavior (respects enableTypedArray setting),
740
+ * 'plain': Convert TypedArrays to regular arrays,
741
+ * 'json': Fully JSON-safe (handles TypedArrays, BigInt, etc.).
669
742
  * @param {object} [options.contentFilter=undefined] - The content-filter, default: undefined.
670
743
  * Confirm that your RMW supports content-filtered topics before use.
671
744
  * @param {string} options.contentFilter.expression - Specifies the criteria to select the data samples of
@@ -691,14 +764,39 @@ class Node extends rclnodejs.ShadowNode {
691
764
  }
692
765
  options = this._validateOptions(options);
693
766
 
767
+ if (typeof typeClass !== 'function') {
768
+ throw new TypeValidationError('typeClass', typeClass, 'function', {
769
+ nodeName: this.name(),
770
+ entityType: 'subscription',
771
+ });
772
+ }
773
+ if (typeof topic !== 'string') {
774
+ throw new TypeValidationError('topic', topic, 'string', {
775
+ nodeName: this.name(),
776
+ entityType: 'subscription',
777
+ });
778
+ }
779
+ if (typeof callback !== 'function') {
780
+ throw new TypeValidationError('callback', callback, 'function', {
781
+ nodeName: this.name(),
782
+ entityType: 'subscription',
783
+ entityName: topic,
784
+ });
785
+ }
694
786
  if (
695
- typeof typeClass !== 'function' ||
696
- typeof topic !== 'string' ||
697
- typeof callback !== 'function' ||
698
- (eventCallbacks &&
699
- !(eventCallbacks instanceof SubscriptionEventCallbacks))
787
+ eventCallbacks &&
788
+ !(eventCallbacks instanceof SubscriptionEventCallbacks)
700
789
  ) {
701
- throw new TypeError('Invalid argument');
790
+ throw new TypeValidationError(
791
+ 'eventCallbacks',
792
+ eventCallbacks,
793
+ 'SubscriptionEventCallbacks',
794
+ {
795
+ nodeName: this.name(),
796
+ entityType: 'subscription',
797
+ entityName: topic,
798
+ }
799
+ );
702
800
  }
703
801
 
704
802
  let subscription = Subscription.createSubscription(
@@ -733,8 +831,17 @@ class Node extends rclnodejs.ShadowNode {
733
831
  }
734
832
  options = this._validateOptions(options);
735
833
 
736
- if (typeof typeClass !== 'function' || typeof serviceName !== 'string') {
737
- throw new TypeError('Invalid argument');
834
+ if (typeof typeClass !== 'function') {
835
+ throw new TypeValidationError('typeClass', typeClass, 'function', {
836
+ nodeName: this.name(),
837
+ entityType: 'client',
838
+ });
839
+ }
840
+ if (typeof serviceName !== 'string') {
841
+ throw new TypeValidationError('serviceName', serviceName, 'string', {
842
+ nodeName: this.name(),
843
+ entityType: 'client',
844
+ });
738
845
  }
739
846
 
740
847
  let client = Client.createClient(
@@ -788,12 +895,24 @@ class Node extends rclnodejs.ShadowNode {
788
895
  }
789
896
  options = this._validateOptions(options);
790
897
 
791
- if (
792
- typeof typeClass !== 'function' ||
793
- typeof serviceName !== 'string' ||
794
- typeof callback !== 'function'
795
- ) {
796
- throw new TypeError('Invalid argument');
898
+ if (typeof typeClass !== 'function') {
899
+ throw new TypeValidationError('typeClass', typeClass, 'function', {
900
+ nodeName: this.name(),
901
+ entityType: 'service',
902
+ });
903
+ }
904
+ if (typeof serviceName !== 'string') {
905
+ throw new TypeValidationError('serviceName', serviceName, 'string', {
906
+ nodeName: this.name(),
907
+ entityType: 'service',
908
+ });
909
+ }
910
+ if (typeof callback !== 'function') {
911
+ throw new TypeValidationError('callback', callback, 'function', {
912
+ nodeName: this.name(),
913
+ entityType: 'service',
914
+ entityName: serviceName,
915
+ });
797
916
  }
798
917
 
799
918
  let service = Service.createService(
@@ -810,6 +929,52 @@ class Node extends rclnodejs.ShadowNode {
810
929
  return service;
811
930
  }
812
931
 
932
+ /**
933
+ * Create a ParameterClient for accessing parameters on a remote node.
934
+ * @param {string} remoteNodeName - The name of the remote node whose parameters to access.
935
+ * @param {object} [options] - Options for parameter client.
936
+ * @param {number} [options.timeout=5000] - Default timeout in milliseconds for service calls.
937
+ * @return {ParameterClient} - An instance of ParameterClient.
938
+ */
939
+ createParameterClient(remoteNodeName, options = {}) {
940
+ if (typeof remoteNodeName !== 'string' || remoteNodeName.trim() === '') {
941
+ throw new TypeError('Remote node name must be a non-empty string');
942
+ }
943
+
944
+ const parameterClient = new ParameterClient(this, remoteNodeName, options);
945
+ debug(
946
+ 'Finish creating parameter client for remote node = %s.',
947
+ remoteNodeName
948
+ );
949
+ this._parameterClients.push(parameterClient);
950
+
951
+ return parameterClient;
952
+ }
953
+
954
+ /**
955
+ * Create a ParameterWatcher for watching parameter changes on a remote node.
956
+ * @param {string} remoteNodeName - The name of the remote node whose parameters to watch.
957
+ * @param {string[]} parameterNames - Array of parameter names to watch.
958
+ * @param {object} [options] - Options for parameter watcher.
959
+ * @param {number} [options.timeout=5000] - Default timeout in milliseconds for service calls.
960
+ * @return {ParameterWatcher} - An instance of ParameterWatcher.
961
+ */
962
+ createParameterWatcher(remoteNodeName, parameterNames, options = {}) {
963
+ const watcher = new ParameterWatcher(
964
+ this,
965
+ remoteNodeName,
966
+ parameterNames,
967
+ options
968
+ );
969
+ debug(
970
+ 'Finish creating parameter watcher for remote node = %s.',
971
+ remoteNodeName
972
+ );
973
+ this._parameterWatchers.push(watcher);
974
+
975
+ return watcher;
976
+ }
977
+
813
978
  /**
814
979
  * Create a guard condition.
815
980
  * @param {Function} callback - The callback to be called when the guard condition is triggered.
@@ -817,7 +982,10 @@ class Node extends rclnodejs.ShadowNode {
817
982
  */
818
983
  createGuardCondition(callback) {
819
984
  if (typeof callback !== 'function') {
820
- throw new TypeError('Invalid argument');
985
+ throw new TypeValidationError('callback', callback, 'function', {
986
+ nodeName: this.name(),
987
+ entityType: 'guard_condition',
988
+ });
821
989
  }
822
990
 
823
991
  let guard = GuardCondition.createGuardCondition(callback, this.context);
@@ -843,6 +1011,9 @@ class Node extends rclnodejs.ShadowNode {
843
1011
  this._actionClients.forEach((actionClient) => actionClient.destroy());
844
1012
  this._actionServers.forEach((actionServer) => actionServer.destroy());
845
1013
 
1014
+ this._parameterClients.forEach((paramClient) => paramClient.destroy());
1015
+ this._parameterWatchers.forEach((watcher) => watcher.destroy());
1016
+
846
1017
  this.context.onNodeDestroyed(this);
847
1018
 
848
1019
  this.handle.release();
@@ -855,6 +1026,8 @@ class Node extends rclnodejs.ShadowNode {
855
1026
  this._guards = [];
856
1027
  this._actionClients = [];
857
1028
  this._actionServers = [];
1029
+ this._parameterClients = [];
1030
+ this._parameterWatchers = [];
858
1031
 
859
1032
  if (this._rateTimerServer) {
860
1033
  this._rateTimerServer.shutdown();
@@ -869,7 +1042,14 @@ class Node extends rclnodejs.ShadowNode {
869
1042
  */
870
1043
  destroyPublisher(publisher) {
871
1044
  if (!(publisher instanceof Publisher)) {
872
- throw new TypeError('Invalid argument');
1045
+ throw new TypeValidationError(
1046
+ 'publisher',
1047
+ publisher,
1048
+ 'Publisher instance',
1049
+ {
1050
+ nodeName: this.name(),
1051
+ }
1052
+ );
873
1053
  }
874
1054
  if (publisher.events) {
875
1055
  publisher.events.forEach((event) => {
@@ -887,7 +1067,14 @@ class Node extends rclnodejs.ShadowNode {
887
1067
  */
888
1068
  destroySubscription(subscription) {
889
1069
  if (!(subscription instanceof Subscription)) {
890
- throw new TypeError('Invalid argument');
1070
+ throw new TypeValidationError(
1071
+ 'subscription',
1072
+ subscription,
1073
+ 'Subscription instance',
1074
+ {
1075
+ nodeName: this.name(),
1076
+ }
1077
+ );
891
1078
  }
892
1079
  if (subscription.events) {
893
1080
  subscription.events.forEach((event) => {
@@ -906,7 +1093,9 @@ class Node extends rclnodejs.ShadowNode {
906
1093
  */
907
1094
  destroyClient(client) {
908
1095
  if (!(client instanceof Client)) {
909
- throw new TypeError('Invalid argument');
1096
+ throw new TypeValidationError('client', client, 'Client instance', {
1097
+ nodeName: this.name(),
1098
+ });
910
1099
  }
911
1100
  this._destroyEntity(client, this._clients);
912
1101
  }
@@ -918,11 +1107,39 @@ class Node extends rclnodejs.ShadowNode {
918
1107
  */
919
1108
  destroyService(service) {
920
1109
  if (!(service instanceof Service)) {
921
- throw new TypeError('Invalid argument');
1110
+ throw new TypeValidationError('service', service, 'Service instance', {
1111
+ nodeName: this.name(),
1112
+ });
922
1113
  }
923
1114
  this._destroyEntity(service, this._services);
924
1115
  }
925
1116
 
1117
+ /**
1118
+ * Destroy a ParameterClient.
1119
+ * @param {ParameterClient} parameterClient - The ParameterClient to be destroyed.
1120
+ * @return {undefined}
1121
+ */
1122
+ destroyParameterClient(parameterClient) {
1123
+ if (!(parameterClient instanceof ParameterClient)) {
1124
+ throw new TypeError('Invalid argument');
1125
+ }
1126
+ this._removeEntityFromArray(parameterClient, this._parameterClients);
1127
+ parameterClient.destroy();
1128
+ }
1129
+
1130
+ /**
1131
+ * Destroy a ParameterWatcher.
1132
+ * @param {ParameterWatcher} watcher - The ParameterWatcher to be destroyed.
1133
+ * @return {undefined}
1134
+ */
1135
+ destroyParameterWatcher(watcher) {
1136
+ if (!(watcher instanceof ParameterWatcher)) {
1137
+ throw new TypeError('Invalid argument');
1138
+ }
1139
+ this._removeEntityFromArray(watcher, this._parameterWatchers);
1140
+ watcher.destroy();
1141
+ }
1142
+
926
1143
  /**
927
1144
  * Destroy a Timer.
928
1145
  * @param {Timer} timer - The Timer to be destroyed.
@@ -930,7 +1147,9 @@ class Node extends rclnodejs.ShadowNode {
930
1147
  */
931
1148
  destroyTimer(timer) {
932
1149
  if (!(timer instanceof Timer)) {
933
- throw new TypeError('Invalid argument');
1150
+ throw new TypeValidationError('timer', timer, 'Timer instance', {
1151
+ nodeName: this.name(),
1152
+ });
934
1153
  }
935
1154
  this._destroyEntity(timer, this._timers);
936
1155
  }
@@ -942,7 +1161,9 @@ class Node extends rclnodejs.ShadowNode {
942
1161
  */
943
1162
  destroyGuardCondition(guard) {
944
1163
  if (!(guard instanceof GuardCondition)) {
945
- throw new TypeError('Invalid argument');
1164
+ throw new TypeValidationError('guard', guard, 'GuardCondition instance', {
1165
+ nodeName: this.name(),
1166
+ });
946
1167
  }
947
1168
  this._destroyEntity(guard, this._guards);
948
1169
  }
@@ -989,7 +1210,7 @@ class Node extends rclnodejs.ShadowNode {
989
1210
 
990
1211
  /**
991
1212
  * Get the current time using the node's clock.
992
- * @returns {Time} - The current time.
1213
+ * @returns {Timer} - The current time.
993
1214
  */
994
1215
  now() {
995
1216
  return this.getClock().now();
@@ -1234,14 +1455,14 @@ class Node extends rclnodejs.ShadowNode {
1234
1455
  *
1235
1456
  * @param {Parameter} parameter - Parameter to declare.
1236
1457
  * @param {ParameterDescriptor} [descriptor] - Optional descriptor for parameter.
1237
- * @param {boolean} [ignoreOveride] - When true disregard any parameter-override that may be present.
1458
+ * @param {boolean} [ignoreOverride] - When true disregard any parameter-override that may be present.
1238
1459
  * @return {Parameter} - The newly declared parameter.
1239
1460
  */
1240
- declareParameter(parameter, descriptor, ignoreOveride = false) {
1461
+ declareParameter(parameter, descriptor, ignoreOverride = false) {
1241
1462
  const parameters = this.declareParameters(
1242
1463
  [parameter],
1243
1464
  descriptor ? [descriptor] : [],
1244
- ignoreOveride
1465
+ ignoreOverride
1245
1466
  );
1246
1467
  return parameters.length == 1 ? parameters[0] : null;
1247
1468
  }
@@ -1282,16 +1503,25 @@ class Node extends rclnodejs.ShadowNode {
1282
1503
  */
1283
1504
  declareParameters(parameters, descriptors = [], ignoreOverrides = false) {
1284
1505
  if (!Array.isArray(parameters)) {
1285
- throw new TypeError('Invalid parameter: expected array of Parameter');
1506
+ throw new TypeValidationError('parameters', parameters, 'Array', {
1507
+ nodeName: this.name(),
1508
+ });
1286
1509
  }
1287
1510
  if (!Array.isArray(descriptors)) {
1288
- throw new TypeError(
1289
- 'Invalid parameters: expected array of ParameterDescriptor'
1290
- );
1511
+ throw new TypeValidationError('descriptors', descriptors, 'Array', {
1512
+ nodeName: this.name(),
1513
+ });
1291
1514
  }
1292
1515
  if (descriptors.length > 0 && parameters.length !== descriptors.length) {
1293
- throw new TypeError(
1294
- 'Each parameter must have a cooresponding ParameterDescriptor'
1516
+ throw new ValidationError(
1517
+ 'Each parameter must have a corresponding ParameterDescriptor',
1518
+ {
1519
+ code: 'PARAMETER_DESCRIPTOR_MISMATCH',
1520
+ argumentName: 'descriptors',
1521
+ providedValue: descriptors.length,
1522
+ expectedType: `Array with length ${parameters.length}`,
1523
+ nodeName: this.name(),
1524
+ }
1295
1525
  );
1296
1526
  }
1297
1527
 
@@ -1731,7 +1961,9 @@ class Node extends rclnodejs.ShadowNode {
1731
1961
  */
1732
1962
  resolveTopicName(topicName, onlyExpand = false) {
1733
1963
  if (typeof topicName !== 'string') {
1734
- throw new TypeError('Invalid argument: expected string');
1964
+ throw new TypeValidationError('topicName', topicName, 'string', {
1965
+ nodeName: this.name(),
1966
+ });
1735
1967
  }
1736
1968
  return rclnodejs.resolveName(
1737
1969
  this.handle,
@@ -1749,7 +1981,9 @@ class Node extends rclnodejs.ShadowNode {
1749
1981
  */
1750
1982
  resolveServiceName(service, onlyExpand = false) {
1751
1983
  if (typeof service !== 'string') {
1752
- throw new TypeError('Invalid argument: expected string');
1984
+ throw new TypeValidationError('service', service, 'string', {
1985
+ nodeName: this.name(),
1986
+ });
1753
1987
  }
1754
1988
  return rclnodejs.resolveName(
1755
1989
  this.handle,
@@ -1920,6 +2154,7 @@ class Node extends rclnodejs.ShadowNode {
1920
2154
  * isRaw: false,
1921
2155
  * qos: QoS.profileDefault,
1922
2156
  * contentFilter: undefined,
2157
+ * serializationMode: 'default',
1923
2158
  * }
1924
2159
  */
1925
2160
  Node.getDefaultOptions = function () {
@@ -1928,6 +2163,7 @@ Node.getDefaultOptions = function () {
1928
2163
  isRaw: false,
1929
2164
  qos: QoS.profileDefault,
1930
2165
  contentFilter: undefined,
2166
+ serializationMode: 'default',
1931
2167
  };
1932
2168
  };
1933
2169