• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RobotWebTools / rclnodejs / 16044464595

03 Jul 2025 07:38AM UTC coverage: 84.526% (-0.1%) from 84.625%
16044464595

Pull #1158

github

web-flow
Merge 90720ad95 into 82bbc82ff
Pull Request #1158: Enable npm test on Windows workflow

775 of 1009 branches covered (76.81%)

Branch coverage included in aggregate %.

1907 of 2164 relevant lines covered (88.12%)

3222.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.7
/lib/parameter.js
1
// Copyright (c) 2020 Wayne Parrott. 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
// Note: parameter api and function based on
16
// https://design.ros2.org/articles/ros_parameters.html
17
// https://github.com/ros2/rcl and
18
// https://github.com/ros2/rclpy
19

20
'use strict';
21

22
const IsClose = require('is-close');
208✔
23

24
/**
25
 * The plus/minus tolerance for determining number equivalence.
26
 * @constant {number}
27
 *
28
 *  @see [FloatingPointRange]{@link FloatingPointRange}
29
 *  @see [IntegerRange]{@link IntegerRange}
30
 */
31
const DEFAULT_NUMERIC_RANGE_TOLERANCE = 1e-6;
208✔
32

33
const PARAMETER_SEPARATOR = '.';
208✔
34
const PARAMETER_BYTE = 10;
208✔
35

36
/**
37
 * Enum for ParameterType
38
 * @readonly
39
 * @enum {number}
40
 */
41
const ParameterType = {
208✔
42
  /** @member {number} */
43
  PARAMETER_NOT_SET: 0,
44
  /** @member {number} */
45
  PARAMETER_BOOL: 1,
46
  /** @member {number} */
47
  PARAMETER_INTEGER: 2,
48
  /** @member {number} */
49
  PARAMETER_DOUBLE: 3,
50
  /** @member {number} */
51
  PARAMETER_STRING: 4,
52
  /** @member {number} */
53
  PARAMETER_BYTE_ARRAY: 5,
54
  /** @member {number} */
55
  PARAMETER_BOOL_ARRAY: 6,
56
  /** @member {number} */
57
  PARAMETER_INTEGER_ARRAY: 7,
58
  /** @member {number} */
59
  PARAMETER_DOUBLE_ARRAY: 8,
60
  /** @member {number} */
61
  PARAMETER_STRING_ARRAY: 9,
62
};
63

64
/**
65
 * A node parameter.
66
 * @class
67
 */
68
class Parameter {
69
  /**
70
   * Create a Parameter instance from an rlc_interfaces/msg/Parameter message.
71
   * @constructs
72
   * @param {rcl_interfaces/msg/Parameter} parameterMsg - The message to convert to a parameter.
73
   * @return {Parameter} - The new instance.
74
   */
75
  static fromParameterMessage(parameterMsg) {
76
    const name = parameterMsg.name;
136✔
77
    const type = parameterMsg.value.type;
136✔
78
    let value;
79

80
    switch (type) {
136✔
81
      case ParameterType.PARAMETER_NOT_SET:
82
        break;
16✔
83
      case ParameterType.PARAMETER_BOOL:
84
        value = parameterMsg.value.bool_value;
16✔
85
        break;
16✔
86
      case ParameterType.PARAMETER_BOOL_ARRAY:
87
        value = parameterMsg.value.bool_array_value;
8✔
88
        break;
8✔
89
      case ParameterType.PARAMETER_BYTE_ARRAY:
90
        value = Array.from(parameterMsg.value.byte_array_value);
8✔
91
        break;
8✔
92
      case ParameterType.PARAMETER_DOUBLE:
93
        value = parameterMsg.value.double_value;
8✔
94
        break;
8✔
95
      case ParameterType.PARAMETER_DOUBLE_ARRAY:
96
        value = Array.from(parameterMsg.value.double_array_value);
8✔
97
        break;
8✔
98
      case ParameterType.PARAMETER_INTEGER:
99
        value = parameterMsg.value.integer_value;
8✔
100
        break;
8✔
101
      case ParameterType.PARAMETER_INTEGER_ARRAY:
102
        value = Array.from(parameterMsg.value.integer_array_value);
8✔
103
        break;
8✔
104
      case ParameterType.PARAMETER_STRING:
105
        value = parameterMsg.value.string_value;
48✔
106
        break;
48✔
107
      case ParameterType.PARAMETER_STRING_ARRAY:
108
        value = Array.from(parameterMsg.value.string_array_value);
8✔
109
        break;
8✔
110
    }
111

112
    return new Parameter(name, type, value);
136✔
113
  }
114

115
  /**
116
   * Create new parameter instances.
117
   * @constructor
118
   *
119
   * @param {string} name - The parameter name, must be a valid name.
120
   * @param {ParameterType} type - The type identifier.
121
   * @param {any} value - The parameter value.
122
   */
123
  constructor(name, type, value) {
124
    this._name = name;
8,810✔
125
    this._type = type;
8,810✔
126
    // Convert to bigint if it's type of `PARAMETER_INTEGER`.
127
    this._value =
8,810✔
128
      this._type == ParameterType.PARAMETER_INTEGER ? BigInt(value) : value;
8,810✔
129
    this._isDirty = true;
8,810✔
130

131
    this.validate();
8,810✔
132
  }
133

134
  /**
135
   * Get name
136
   *
137
   * @return {string} - The parameter name.
138
   */
139
  get name() {
140
    return this._name;
154,988✔
141
  }
142

143
  /**
144
   * Get type
145
   *
146
   * @return {ParameterType} - The parameter type.
147
   */
148
  get type() {
149
    return this._type;
108,678✔
150
  }
151

152
  /**
153
   * Get value.
154
   *
155
   * @return {any} - The parameter value.
156
   */
157
  get value() {
158
    return this._value;
43,028✔
159
  }
160

161
  /**
162
   * Set value.
163
   * Value must be compatible with the type property.
164
   * @param {any} newValue - The parameter name.
165
   */
166
  set value(newValue) {
167
    // no empty array value allowed
168
    this._value =
64✔
169
      Array.isArray(newValue) && newValue.length === 0 ? null : newValue;
128!
170

171
    this._dirty = true;
64✔
172
    this.validate();
64✔
173
  }
174

175
  /**
176
   * Check the state of this property.
177
   * Throw TypeError on first property with invalid type.
178
   * @return {undefined}
179
   */
180
  validate() {
181
    // validate name
182
    if (
25,964!
183
      !this.name ||
77,892✔
184
      typeof this.name !== 'string' ||
185
      this.name.trim().length === 0
186
    ) {
187
      throw new TypeError('Invalid name');
×
188
    }
189

190
    // validate type
191
    if (!validType(this.type)) {
25,964!
192
      throw new TypeError('Invalid type');
×
193
    }
194

195
    // validate value
196
    if (!validValue(this.value, this.type)) {
25,964✔
197
      throw new TypeError('Incompatible value.');
8✔
198
    }
199

200
    this._dirty = false;
25,956✔
201
  }
202

203
  /**
204
   * Create a rcl_interfaces.msg.Parameter from this instance.
205
   *
206
   * @return {rcl_interfaces.msg.Parameter} - The new instance.
207
   */
208
  toParameterMessage() {
209
    const msg = {
8,490✔
210
      name: this.name,
211
      value: this.toParameterValueMessage(),
212
    };
213
    return msg;
8,490✔
214
  }
215

216
  /**
217
   * Create a rcl_interfaces.msg.ParameterValue from this instance.
218
   *
219
   * @return {rcl_interfaces.msg.ParameterValue} - The new instance.
220
   */
221
  toParameterValueMessage() {
222
    const msg = {};
8,512✔
223
    msg.type = this.type;
8,512✔
224
    switch (this.type) {
8,512!
225
      case ParameterType.PARAMETER_NOT_SET:
226
        break;
8✔
227
      case ParameterType.PARAMETER_BOOL:
228
        msg.bool_value = this.value;
8,200✔
229
        break;
8,200✔
230
      case ParameterType.PARAMETER_BOOL_ARRAY:
231
        msg.bool_array_value = this.value;
×
232
        break;
×
233
      case ParameterType.PARAMETER_BYTE_ARRAY:
234
        msg.byte_array_value = this.value.map((val) => Math.trunc(val));
×
235
        break;
×
236
      case ParameterType.PARAMETER_DOUBLE:
237
        msg.double_value = this.value;
×
238
        break;
×
239
      case ParameterType.PARAMETER_DOUBLE_ARRAY:
240
        msg.double_array_value = this.value;
×
241
        break;
×
242
      case ParameterType.PARAMETER_INTEGER:
243
        msg.integer_value = this.value;
72✔
244
        break;
72✔
245
      case ParameterType.PARAMETER_INTEGER_ARRAY:
246
        msg.integer_array_value = this.value;
72✔
247
        break;
72✔
248
      case ParameterType.PARAMETER_STRING:
249
        msg.string_value = this.value;
160✔
250
        break;
160✔
251
      case ParameterType.PARAMETER_STRING_ARRAY:
252
        msg.string_array_value = this.value;
×
253
        break;
×
254
    }
255

256
    return msg;
8,512✔
257
  }
258
}
259

260
/**
261
 * A node parameter descriptor.
262
 * @class
263
 */
264
class ParameterDescriptor {
265
  /**
266
   * Create a new instance from a parameter.
267
   * @constructs
268
   * @param {Parameter} parameter - The parameter from which new instance is constructed.
269
   * @return {ParameterDescriptor} - The new instance.
270
   */
271
  static fromParameter(parameter) {
272
    const name = parameter.name;
4,724✔
273
    const type = parameter.type;
4,724✔
274
    return new ParameterDescriptor(name, type, 'Created from parameter.');
4,724✔
275
  }
276

277
  /**
278
   * Create new instances.
279
   * @constructor
280
   * @param {string} name - The descriptor name, must be a valid name.
281
   * @param {ParameterType} type - The type identifier.
282
   * @param {string} [description] - A descriptive string.
283
   * @param {boolean} [readOnly] - True indicates a parameter of this type can not be modified. Default = false.
284
   * @param {Range} [range] - An optional IntegerRange or FloatingPointRange.
285
   */
286
  constructor(
287
    name,
288
    type = ParameterType.PARAMETER_NOT_SET,
×
289
    description = 'no description',
232✔
290
    readOnly = false,
4,884✔
291
    range = null
8,600✔
292
  ) {
293
    this._name = name; // string
8,600✔
294
    this._type = type; // ParameterType
8,600✔
295
    this._description = description;
8,600✔
296
    this._readOnly = readOnly;
8,600✔
297
    this._additionalConstraints = '';
8,600✔
298
    this._range = range;
8,600✔
299

300
    this.validate();
8,600✔
301
  }
302

303
  /**
304
   * Get name.
305
   *
306
   * @return {string} - The name property.
307
   */
308
  get name() {
309
    return this._name;
93,720✔
310
  }
311

312
  /**
313
   * Get type.
314
   *
315
   * @return {ParameterType} - The type property.
316
   */
317
  get type() {
318
    return this._type;
34,238✔
319
  }
320

321
  /**
322
   * Get description.
323
   *
324
   * @return {string} - A descriptive string property.
325
   */
326
  get description() {
327
    return this._description;
51,176✔
328
  }
329

330
  /**
331
   * Get readOnly property.
332
   *
333
   * @return {boolean} - The readOnly property.
334
   */
335
  get readOnly() {
336
    return this._readOnly;
88✔
337
  }
338

339
  /**
340
   * Get additionalConstraints property.
341
   *
342
   * @return {string} - The additionalConstraints property.
343
   */
344
  get additionalConstraints() {
345
    return this._additionalConstraints;
16✔
346
  }
347

348
  /**
349
   * Set additionalConstraints property. .
350
   *
351
   * @param {string} constraintDescription - The new value.
352
   */
353
  set additionalConstraints(constraintDescription) {
354
    this._additionalConstraints = constraintDescription;
×
355
  }
356

357
  /**
358
   * Determine if rangeConstraint property has been set.
359
   *
360
   * @return {boolean} - The rangeConstraint property.
361
   */
362
  hasRange() {
363
    return !!this._range;
34,110✔
364
  }
365

366
  /**
367
   * Get range.
368
   *
369
   * @return {FloatingPointRange|IntegerRange} - The range property.
370
   */
371
  get range() {
372
    return this._range;
128✔
373
  }
374

375
  /**
376
   * Set range.
377
   * The range must be compatible with the type property.
378
   * @param {FloatingPointRange|IntegerRange} range - The new range.
379
   */
380
  set range(range) {
381
    if (!range) {
16!
382
      this._range = null;
×
383
      return;
×
384
    }
385
    if (!(range instanceof Range)) {
16!
386
      throw TypeError('Expected instance of Range.');
×
387
    }
388
    if (!range.isValidType(this.type)) {
16!
389
      throw TypeError('Incompatible Range');
×
390
    }
391

392
    this._range = range;
16✔
393
  }
394

395
  /**
396
   * Check the state and ensure it is valid.
397
   * Throw a TypeError if invalid state is detected.
398
   *
399
   * @return {undefined}
400
   */
401
  validate() {
402
    // validate name
403
    if (
25,572!
404
      !this.name ||
76,716✔
405
      typeof this.name !== 'string' ||
406
      this.name.trim().length === 0
407
    ) {
408
      throw new TypeError('Invalid name');
×
409
    }
410

411
    // validate type
412
    if (!validType(this.type)) {
25,572!
413
      throw new TypeError('Invalid type');
×
414
    }
415

416
    // validate description
417
    if (this.description && typeof this.description !== 'string') {
25,572!
418
      throw new TypeError('Invalid description');
×
419
    }
420

421
    // validate rangeConstraint
422
    if (this.hasRange() && !this.range.isValidType(this.type)) {
25,572!
423
      throw new TypeError('Incompatible Range');
×
424
    }
425
  }
426

427
  /**
428
   * Check a parameter for consistency with this descriptor.
429
   * Throw an Error if an inconsistent state is detected.
430
   *
431
   * @param {Parameter} parameter - The parameter to test for consistency.
432
   * @return {undefined}
433
   */
434
  validateParameter(parameter) {
435
    if (!parameter) {
8,538!
436
      throw new TypeError('Parameter is undefined');
×
437
    }
438

439
    // ensure parameter is valid
440
    try {
8,538✔
441
      parameter.validate();
8,538✔
442
    } catch {
443
      throw new TypeError('Parameter is invalid');
×
444
    }
445

446
    // ensure this descriptor is valid
447
    try {
8,538✔
448
      this.validate();
8,538✔
449
    } catch {
450
      throw new Error('Descriptor is invalid.');
×
451
    }
452

453
    if (this.name !== parameter.name) throw new Error('Name mismatch');
8,538!
454
    if (this.type !== parameter.type) throw new Error('Type mismatch');
8,538!
455
    if (this.hasRange() && !this.range.inRange(parameter.value)) {
8,538✔
456
      throw new RangeError('Parameter value is not in descriptor range');
32✔
457
    }
458
  }
459

460
  /**
461
   * Create a rcl_interfaces.msg.ParameterDescriptor from this descriptor.
462
   *
463
   * @return {rcl_interfaces.msg.ParameterDescriptor} - The new message.
464
   */
465
  toMessage() {
466
    const msg = {
16✔
467
      name: this.name,
468
      type: this.type,
469
      description: this.description,
470
      additional_constraints: this.additionalConstraints,
471
      read_only: this.readOnly,
472
    };
473
    if (
16!
474
      (this._type === ParameterType.PARAMETER_INTEGER ||
32✔
475
        this._type === ParameterType.PARAMETER_INTEGER_ARRAY) &&
476
      this._rangeConstraint instanceof IntegerRange
477
    ) {
478
      msg.integer_range = [this._rangeConstraint];
×
479
    } else if (
16!
480
      (this._type === ParameterType.PARAMETER_DOUBLE ||
32!
481
        this._type === ParameterType.PARAMETER_DOUBLE_ARRAY) &&
482
      this._rangeConstraint instanceof FloatingPointRange
483
    ) {
484
      msg.floating_point_range = [this._rangeConstraint];
×
485
    }
486

487
    return msg;
16✔
488
  }
489
}
490

491
/**
492
 * An abstract class defining a range of numbers between 2 points inclusively
493
 * divided by a step value.
494
 * @class
495
 */
496
class Range {
497
  /**
498
   * Create a new instance.
499
   * @constructor
500
   * @param {number} fromValue - The lowest inclusive value in range
501
   * @param {number} toValue - The highest inclusive value in range
502
   * @param {number} step - The internal unit size.
503
   */
504
  constructor(fromValue, toValue, step = 1) {
×
505
    this._fromValue = fromValue;
32✔
506
    this._toValue = toValue;
32✔
507
    this._step = step;
32✔
508
  }
509

510
  /**
511
   * Get fromValue.
512
   *
513
   * @return {number} - The lowest inclusive value in range.
514
   */
515
  get fromValue() {
516
    return this._fromValue;
280✔
517
  }
518

519
  /**
520
   * Get toValue.
521
   *
522
   * @return {number} - The highest inclusive value in range.
523
   */
524
  get toValue() {
525
    return this._toValue;
280✔
526
  }
527

528
  /**
529
   * Get step unit.
530
   *
531
   * @return {number} - The internal unit size.
532
   */
533
  get step() {
534
    return this._step;
248✔
535
  }
536

537
  /**
538
   * Determine if a value is within this range.
539
   * A TypeError is thrown when value is not a number or bigint.
540
   * Subclasses should override and call this method for basic type checking.
541
   *
542
   * @param {number|bigint} value - The number or bigint to check.
543
   * @return {boolean} - True if value satisfies the range; false otherwise.
544
   */
545
  inRange(value) {
546
    if (Array.isArray(value)) {
72!
547
      const valArr = value;
×
548
      return valArr.reduce(
×
549
        (inRange, val) => inRange && this.inRange(val),
×
550
        true
551
      );
552
    } else if (typeof value !== 'number' && typeof value !== 'bigint') {
72!
553
      throw new TypeError('Value must be a number or bigint');
×
554
    }
555

556
    return true;
72✔
557
  }
558

559
  /**
560
   * Abstract method that determines if a ParameterType is compatible.
561
   * Subclasses must implement this method.
562
   *
563
   * @param {ParameterType} parameterType - The parameter type to test.
564
   * @return {boolean} - True if parameterType is compatible; otherwise return false.
565
   */
566
  // eslint-disable-next-line no-unused-vars
567
  isValidType(parameterType) {
568
    return false;
×
569
  }
570
}
571

572
/**
573
 * Defines a range for floating point values.
574
 * @class
575
 */
576
class FloatingPointRange extends Range {
577
  /**
578
   * Create a new instance.
579
   * @constructor
580
   * @param {number} fromValue - The lowest inclusive value in range
581
   * @param {number} toValue - The highest inclusive value in range
582
   * @param {number} step - The internal unit size.
583
   * @param {number} tolerance - The plus/minus tolerance for number equivalence.
584
   */
585
  constructor(
586
    fromValue,
587
    toValue,
588
    step = 1,
×
589
    tolerance = DEFAULT_NUMERIC_RANGE_TOLERANCE
8✔
590
  ) {
591
    super(fromValue, toValue, step);
8✔
592
    this._tolerance = tolerance;
8✔
593
  }
594

595
  get tolerance() {
596
    return this._tolerance;
160✔
597
  }
598

599
  /**
600
   * Determine if a ParameterType is compatible.
601
   *
602
   * @param {ParameterType} parameterType - The parameter type to test.
603
   * @return {boolean} - True if parameterType is compatible; otherwise return false.
604
   */
605
  isValidType(parameterType) {
606
    const result =
607
      parameterType === ParameterType.PARAMETER_DOUBLE ||
×
608
      parameterType === ParameterType.PARAMETER_DOUBLE_ARRAY;
609
    return result;
×
610
  }
611

612
  /**
613
   * Determine if a value is within this range.
614
   * A TypeError is thrown when value is not a number.
615
   *
616
   * @param {number} value - The number to check.
617
   * @return {boolean} - True if value satisfies the range; false otherwise.
618
   */
619
  inRange(value) {
620
    if (!super.inRange(value)) return false;
72!
621

622
    const min = Math.min(this.fromValue, this.toValue);
72✔
623
    const max = Math.max(this.fromValue, this.toValue);
72✔
624

625
    if (
72✔
626
      IsClose.isClose(value, min, this.tolerance) ||
136✔
627
      IsClose.isClose(value, max, this.tolerance)
628
    ) {
629
      return true;
32✔
630
    }
631
    if (value < min || value > max) {
40✔
632
      return false;
16✔
633
    }
634
    if (this.step != 0.0) {
24!
635
      const distanceInSteps = Math.round((value - min) / this.step);
24✔
636
      if (
24!
637
        !IsClose.isClose(
638
          min + distanceInSteps * this.step,
639
          value,
640
          this.tolerance
641
        )
642
      ) {
643
        return false;
×
644
      }
645
    }
646

647
    return true;
24✔
648
  }
649
}
650

651
/**
652
 * Defines a range for integer values.
653
 * @class
654
 */
655
class IntegerRange extends Range {
656
  /**
657
   * Create a new instance.
658
   * @constructor
659
   * @param {bigint} fromValue - The lowest inclusive value in range
660
   * @param {bigint} toValue - The highest inclusive value in range
661
   * @param {bigint} step - The internal unit size.
662
   */
663
  constructor(fromValue, toValue, step = 1n) {
8✔
664
    super(fromValue, toValue, step);
24✔
665
  }
666

667
  /**
668
   * Determine if a ParameterType is compatible.
669
   *
670
   * @param {ParameterType} parameterType - The parameter type to test.
671
   * @return {boolean} - True if parameterType is compatible; otherwise return false.
672
   */
673
  isValidType(parameterType) {
674
    const result =
675
      parameterType === ParameterType.PARAMETER_INTEGER ||
80!
676
      parameterType === ParameterType.PARAMETER_INTEGER_ARRAY;
677
    return result;
80✔
678
  }
679

680
  inRange(value) {
681
    const min = this.fromValue;
120✔
682
    const max = this.toValue;
120✔
683
    if (value < min || value > max) {
120✔
684
      return false;
40✔
685
    }
686

687
    if (this.step != 0n && (value - min) % this.step !== 0n) {
80✔
688
      return false;
8✔
689
    }
690
    return true;
72✔
691
  }
692
}
693

694
/**
695
 * Infer a ParameterType for JS primitive types:
696
 * string, boolean, number and arrays of these types.
697
 * A TypeError is thrown for a value who's type is not one of
698
 * the set listed.
699
 * @param {any} value - The value to infer it's ParameterType
700
 * @returns {ParameterType} - The ParameterType that best scribes the value.
701
 */
702
// eslint-disable-next-line no-unused-vars
703
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)) {
×
709
    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;
×
718
      }
719
    }
720

721
    return ParameterType.PARAMETER_NOT_SET;
×
722
  }
723

724
  // otherwise unrecognized value
725
  throw new TypeError('Unrecognized parameter type.');
×
726
}
727

728
/**
729
 * Determine if a number maps to is a valid ParameterType.
730
 *
731
 * @param {ParameterType} parameterType - The value to test.
732
 * @return {boolean} - True if value is a valid ParameterType; false otherwise.
733
 */
734
function validType(parameterType) {
735
  let result =
736
    typeof parameterType === 'number' &&
51,536✔
737
    ParameterType.PARAMETER_NOT_SET <=
738
      parameterType <=
739
      ParameterType.PARAMETER_STRING_ARRAY;
740

741
  return result;
51,536✔
742
}
743

744
/**
745
 * Test if value can be represented by a ParameterType.
746
 *
747
 * @param {number} value - The value to test.
748
 * @param {ParameterType} type - The ParameterType to test value against.
749
 * @return {boolean} - True if value can be represented by type.
750
 */
751
function validValue(value, type) {
752
  if (value == null) {
26,780✔
753
    return type === ParameterType.PARAMETER_NOT_SET;
32✔
754
  }
755

756
  let result = true;
26,748✔
757
  switch (type) {
26,748!
758
    case ParameterType.PARAMETER_NOT_SET:
759
      result = !value;
×
760
      break;
×
761
    case ParameterType.PARAMETER_BOOL:
762
      result = typeof value === 'boolean';
24,708✔
763
      break;
24,708✔
764
    case ParameterType.PARAMETER_STRING:
765
      result = typeof value === 'string';
584✔
766
      break;
584✔
767
    case ParameterType.PARAMETER_DOUBLE:
768
    case PARAMETER_BYTE:
769
      result = typeof value === 'number';
80✔
770
      break;
80✔
771
    case ParameterType.PARAMETER_INTEGER:
772
      result = typeof value === 'bigint';
1,112✔
773
      break;
1,112✔
774
    case ParameterType.PARAMETER_BOOL_ARRAY:
775
    case ParameterType.PARAMETER_BYTE_ARRAY:
776
    case ParameterType.PARAMETER_INTEGER_ARRAY:
777
    case ParameterType.PARAMETER_DOUBLE_ARRAY:
778
    case ParameterType.PARAMETER_STRING_ARRAY:
779
      result = _validArray(value, type);
264✔
780
      break;
264✔
781
    default:
782
      result = false;
×
783
  }
784

785
  return result;
26,748✔
786
}
787

788
function _validArray(values, type) {
789
  if (!Array.isArray(values)) return false;
264!
790

791
  let arrayElementType;
792
  if (type === ParameterType.PARAMETER_BOOL_ARRAY) {
264✔
793
    arrayElementType = ParameterType.PARAMETER_BOOL;
16✔
794
  } else if (type === ParameterType.PARAMETER_BYTE_ARRAY) {
248✔
795
    arrayElementType = PARAMETER_BYTE;
8✔
796
  }
797
  if (type === ParameterType.PARAMETER_INTEGER_ARRAY) {
264✔
798
    arrayElementType = ParameterType.PARAMETER_INTEGER;
224✔
799
  }
800
  if (type === ParameterType.PARAMETER_DOUBLE_ARRAY) {
264✔
801
    arrayElementType = ParameterType.PARAMETER_DOUBLE;
8✔
802
  }
803
  if (type === ParameterType.PARAMETER_STRING_ARRAY) {
264✔
804
    arrayElementType = ParameterType.PARAMETER_STRING;
8✔
805
  }
806

807
  return values.reduce(
264✔
808
    (compatible, val) =>
809
      compatible &&
816✔
810
      !_isArrayParameterType(arrayElementType) &&
811
      validValue(val, arrayElementType),
812
    true
813
  );
814
}
815

816
function _isArrayParameterType(type) {
817
  return (
816✔
818
    type === ParameterType.PARAMETER_BOOL_ARRAY ||
4,080✔
819
    type === ParameterType.PARAMETER_BYTE_ARRAY ||
820
    type === ParameterType.PARAMETER_INTEGER_ARRAY ||
821
    type === ParameterType.PARAMETER_DOUBLE_ARRAY ||
822
    type === ParameterType.PARAMETER_STRING_ARRAY
823
  );
824
}
825

826
module.exports = {
208✔
827
  ParameterType,
828
  Parameter,
829
  ParameterDescriptor,
830
  PARAMETER_SEPARATOR,
831
  Range,
832
  FloatingPointRange,
833
  IntegerRange,
834
  DEFAULT_NUMERIC_RANGE_TOLERANCE,
835
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc