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/parameter.js CHANGED
@@ -19,7 +19,13 @@
19
19
 
20
20
  'use strict';
21
21
 
22
- const IsClose = require('is-close');
22
+ const { isClose } = require('./utils.js');
23
+ const {
24
+ TypeValidationError,
25
+ RangeValidationError,
26
+ ParameterError,
27
+ ParameterTypeError,
28
+ } = require('./errors.js');
23
29
 
24
30
  /**
25
31
  * The plus/minus tolerance for determining number equivalence.
@@ -184,17 +190,25 @@ class Parameter {
184
190
  typeof this.name !== 'string' ||
185
191
  this.name.trim().length === 0
186
192
  ) {
187
- throw new TypeError('Invalid name');
193
+ throw new TypeValidationError('name', this.name, 'non-empty string', {
194
+ entityType: 'parameter',
195
+ });
188
196
  }
189
197
 
190
198
  // validate type
191
199
  if (!validType(this.type)) {
192
- throw new TypeError('Invalid type');
200
+ throw new ParameterError('Invalid parameter type', this.name, {
201
+ details: { providedType: this.type },
202
+ });
193
203
  }
194
204
 
195
205
  // validate value
196
206
  if (!validValue(this.value, this.type)) {
197
- throw new TypeError('Incompatible value.');
207
+ throw new ParameterTypeError(this.name, this.type, typeof this.value, {
208
+ details: {
209
+ providedValue: this.value,
210
+ },
211
+ });
198
212
  }
199
213
 
200
214
  this._dirty = false;
@@ -383,10 +397,23 @@ class ParameterDescriptor {
383
397
  return;
384
398
  }
385
399
  if (!(range instanceof Range)) {
386
- throw TypeError('Expected instance of Range.');
400
+ throw new TypeValidationError('range', range, 'Range', {
401
+ entityType: 'parameter descriptor',
402
+ parameterName: this.name,
403
+ });
387
404
  }
388
405
  if (!range.isValidType(this.type)) {
389
- throw TypeError('Incompatible Range');
406
+ throw new ParameterError(
407
+ 'Incompatible Range for parameter type',
408
+ this.name,
409
+ {
410
+ entityType: 'parameter descriptor',
411
+ details: {
412
+ rangeType: range.constructor.name,
413
+ parameterType: this.type,
414
+ },
415
+ }
416
+ );
390
417
  }
391
418
 
392
419
  this._range = range;
@@ -405,22 +432,40 @@ class ParameterDescriptor {
405
432
  typeof this.name !== 'string' ||
406
433
  this.name.trim().length === 0
407
434
  ) {
408
- throw new TypeError('Invalid name');
435
+ throw new TypeValidationError('name', this.name, 'non-empty string', {
436
+ entityType: 'parameter descriptor',
437
+ });
409
438
  }
410
439
 
411
440
  // validate type
412
441
  if (!validType(this.type)) {
413
- throw new TypeError('Invalid type');
442
+ throw new ParameterError('Invalid parameter type', this.name, {
443
+ entityType: 'parameter descriptor',
444
+ details: { providedType: this.type },
445
+ });
414
446
  }
415
447
 
416
448
  // validate description
417
449
  if (this.description && typeof this.description !== 'string') {
418
- throw new TypeError('Invalid description');
450
+ throw new TypeValidationError('description', this.description, 'string', {
451
+ entityType: 'parameter descriptor',
452
+ parameterName: this.name,
453
+ });
419
454
  }
420
455
 
421
456
  // validate rangeConstraint
422
457
  if (this.hasRange() && !this.range.isValidType(this.type)) {
423
- throw new TypeError('Incompatible Range');
458
+ throw new ParameterError(
459
+ 'Incompatible Range for parameter type',
460
+ this.name,
461
+ {
462
+ entityType: 'parameter descriptor',
463
+ details: {
464
+ rangeType: this.range.constructor.name,
465
+ parameterType: this.type,
466
+ },
467
+ }
468
+ );
424
469
  }
425
470
  }
426
471
 
@@ -433,27 +478,66 @@ class ParameterDescriptor {
433
478
  */
434
479
  validateParameter(parameter) {
435
480
  if (!parameter) {
436
- throw new TypeError('Parameter is undefined');
481
+ throw new TypeValidationError('parameter', parameter, 'Parameter', {
482
+ entityType: 'parameter descriptor',
483
+ parameterName: this.name,
484
+ });
437
485
  }
438
486
 
439
487
  // ensure parameter is valid
440
488
  try {
441
489
  parameter.validate();
442
- } catch {
443
- throw new TypeError('Parameter is invalid');
490
+ } catch (err) {
491
+ throw new ParameterError('Parameter is invalid', parameter.name, {
492
+ cause: err,
493
+ details: { validationError: err.message },
494
+ });
444
495
  }
445
496
 
446
497
  // ensure this descriptor is valid
447
498
  try {
448
499
  this.validate();
449
- } catch {
450
- throw new Error('Descriptor is invalid.');
500
+ } catch (err) {
501
+ throw new ParameterError('Descriptor is invalid', this.name, {
502
+ entityType: 'parameter descriptor',
503
+ cause: err,
504
+ details: { validationError: err.message },
505
+ });
451
506
  }
452
507
 
453
- if (this.name !== parameter.name) throw new Error('Name mismatch');
454
- if (this.type !== parameter.type) throw new Error('Type mismatch');
508
+ if (this.name !== parameter.name) {
509
+ throw new ParameterError('Name mismatch', this.name, {
510
+ details: {
511
+ descriptorName: this.name,
512
+ parameterName: parameter.name,
513
+ },
514
+ });
515
+ }
516
+ if (this.type !== parameter.type) {
517
+ throw new ParameterTypeError(this.name, this.type, parameter.type, {
518
+ details: {
519
+ expectedType: this.type,
520
+ actualType: parameter.type,
521
+ },
522
+ });
523
+ }
455
524
  if (this.hasRange() && !this.range.inRange(parameter.value)) {
456
- throw new RangeError('Parameter value is not in descriptor range');
525
+ throw new RangeValidationError(
526
+ 'value',
527
+ parameter.value,
528
+ `${this.range.fromValue} to ${this.range.toValue}`,
529
+ {
530
+ entityType: 'parameter',
531
+ parameterName: parameter.name,
532
+ details: {
533
+ range: {
534
+ from: this.range.fromValue,
535
+ to: this.range.toValue,
536
+ step: this.range.step,
537
+ },
538
+ },
539
+ }
540
+ );
457
541
  }
458
542
  }
459
543
 
@@ -550,7 +634,9 @@ class Range {
550
634
  true
551
635
  );
552
636
  } else if (typeof value !== 'number' && typeof value !== 'bigint') {
553
- throw new TypeError('Value must be a number or bigint');
637
+ throw new TypeValidationError('value', value, 'number or bigint', {
638
+ entityType: 'range',
639
+ });
554
640
  }
555
641
 
556
642
  return true;
@@ -623,8 +709,8 @@ class FloatingPointRange extends Range {
623
709
  const max = Math.max(this.fromValue, this.toValue);
624
710
 
625
711
  if (
626
- IsClose.isClose(value, min, this.tolerance) ||
627
- IsClose.isClose(value, max, this.tolerance)
712
+ isClose(value, min, this.tolerance) ||
713
+ isClose(value, max, this.tolerance)
628
714
  ) {
629
715
  return true;
630
716
  }
@@ -633,13 +719,7 @@ class FloatingPointRange extends Range {
633
719
  }
634
720
  if (this.step != 0.0) {
635
721
  const distanceInSteps = Math.round((value - min) / this.step);
636
- if (
637
- !IsClose.isClose(
638
- min + distanceInSteps * this.step,
639
- value,
640
- this.tolerance
641
- )
642
- ) {
722
+ if (!isClose(min + distanceInSteps * this.step, value, this.tolerance)) {
643
723
  return false;
644
724
  }
645
725
  }
@@ -699,30 +779,80 @@ class IntegerRange extends Range {
699
779
  * @param {any} value - The value to infer it's ParameterType
700
780
  * @returns {ParameterType} - The ParameterType that best scribes the value.
701
781
  */
702
- // eslint-disable-next-line no-unused-vars
703
782
  function parameterTypeFromValue(value) {
704
- if (!value) return ParameterType.PARAMETER_NOT_SET;
705
- if (typeof value === 'boolean') return ParameterType.PARAMETER_BOOL;
706
- if (typeof value === 'string') return ParameterType.PARAMETER_STRING;
707
- if (typeof value === 'number') return ParameterType.PARAMETER_DOUBLE;
708
- if (Array.isArray(value)) {
783
+ if (value === null || value === undefined) {
784
+ return ParameterType.PARAMETER_NOT_SET;
785
+ }
786
+
787
+ if (typeof value === 'boolean') {
788
+ return ParameterType.PARAMETER_BOOL;
789
+ }
790
+
791
+ if (typeof value === 'string') {
792
+ return ParameterType.PARAMETER_STRING;
793
+ }
794
+
795
+ if (typeof value === 'bigint') {
796
+ return ParameterType.PARAMETER_INTEGER;
797
+ }
798
+
799
+ if (typeof value === 'number') {
800
+ // Distinguish between integer and double
801
+ return Number.isInteger(value)
802
+ ? ParameterType.PARAMETER_INTEGER
803
+ : ParameterType.PARAMETER_DOUBLE;
804
+ }
805
+
806
+ // Handle TypedArrays
807
+ if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
808
+ if (value instanceof Uint8Array) {
809
+ return ParameterType.PARAMETER_BYTE_ARRAY;
810
+ }
811
+ // For other typed arrays, infer from first element
709
812
  if (value.length > 0) {
710
- const elementType = parameterTypeFromValue(value[0]);
711
- switch (elementType) {
712
- case ParameterType.PARAMETER_BOOL:
713
- return ParameterType.PARAMETER_BOOL_ARRAY;
714
- case ParameterType.PARAMETER_DOUBLE:
715
- return ParameterType.PARAMETER_DOUBLE_ARRAY;
716
- case ParameterType.PARAMETER_STRING:
717
- return ParameterType.PARAMETER_STRING_ARRAY;
813
+ const firstType = parameterTypeFromValue(value[0]);
814
+ if (firstType === ParameterType.PARAMETER_INTEGER) {
815
+ return ParameterType.PARAMETER_INTEGER_ARRAY;
718
816
  }
817
+ return ParameterType.PARAMETER_DOUBLE_ARRAY;
719
818
  }
720
-
721
819
  return ParameterType.PARAMETER_NOT_SET;
722
820
  }
723
821
 
724
- // otherwise unrecognized value
725
- throw new TypeError('Unrecognized parameter type.');
822
+ if (Array.isArray(value)) {
823
+ if (value.length === 0) {
824
+ return ParameterType.PARAMETER_NOT_SET;
825
+ }
826
+
827
+ const elementType = parameterTypeFromValue(value[0]);
828
+ switch (elementType) {
829
+ case ParameterType.PARAMETER_BOOL:
830
+ return ParameterType.PARAMETER_BOOL_ARRAY;
831
+ case ParameterType.PARAMETER_INTEGER:
832
+ return ParameterType.PARAMETER_INTEGER_ARRAY;
833
+ case ParameterType.PARAMETER_DOUBLE:
834
+ return ParameterType.PARAMETER_DOUBLE_ARRAY;
835
+ case ParameterType.PARAMETER_STRING:
836
+ return ParameterType.PARAMETER_STRING_ARRAY;
837
+ default:
838
+ return ParameterType.PARAMETER_NOT_SET;
839
+ }
840
+ }
841
+
842
+ // Unrecognized value type
843
+ throw new TypeValidationError('value', value, 'valid parameter type', {
844
+ entityType: 'parameter',
845
+ details: {
846
+ supportedTypes: [
847
+ 'boolean',
848
+ 'string',
849
+ 'number',
850
+ 'boolean[]',
851
+ 'number[]',
852
+ 'string[]',
853
+ ],
854
+ },
855
+ });
726
856
  }
727
857
 
728
858
  /**
@@ -832,4 +962,5 @@ module.exports = {
832
962
  FloatingPointRange,
833
963
  IntegerRange,
834
964
  DEFAULT_NUMERIC_RANGE_TOLERANCE,
965
+ parameterTypeFromValue,
835
966
  };