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

RobotWebTools / rclnodejs / #958

06 Mar 2025 04:45AM UTC coverage: 85.075% (-5.4%) from 90.471%
#958

push

web-flow
Update .npmignore (#1062)

This patch updates `.npmignore` to exclude files not needed in the npm package.

Fix: #1061

706 of 920 branches covered (76.74%)

Branch coverage included in aggregate %.

1728 of 1941 relevant lines covered (89.03%)

343.48 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');
26✔
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;
26✔
32

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

36
/**
37
 * Enum for ParameterType
38
 * @readonly
39
 * @enum {number}
40
 */
41
const ParameterType = {
26✔
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;
17✔
77
    const type = parameterMsg.value.type;
17✔
78
    let value;
79

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

112
    return new Parameter(name, type, value);
17✔
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;
637✔
125
    this._type = type;
637✔
126
    // Convert to bigint if it's type of `PARAMETER_INTEGER`.
127
    this._value =
637✔
128
      this._type == ParameterType.PARAMETER_INTEGER ? BigInt(value) : value;
637✔
129
    this._isDirty = true;
637✔
130

131
    this.validate();
637✔
132
  }
133

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

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

152
  /**
153
   * Get value.
154
   *
155
   * @return {any} - The parameter value.
156
   */
157
  get value() {
158
    return this._value;
3,049✔
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 =
8✔
169
      Array.isArray(newValue) && newValue.length === 0 ? null : newValue;
16!
170

171
    this._dirty = true;
8✔
172
    this.validate();
8✔
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 (
1,849!
183
      !this.name ||
5,547✔
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)) {
1,849!
192
      throw new TypeError('Invalid type');
×
193
    }
194

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

200
    this._dirty = false;
1,848✔
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 = {
597✔
210
      name: this.name,
211
      value: this.toParameterValueMessage(),
212
    };
213
    return msg;
597✔
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 = {};
599✔
223
    msg.type = this.type;
599✔
224
    switch (this.type) {
599!
225
      case ParameterType.PARAMETER_NOT_SET:
226
        break;
1✔
227
      case ParameterType.PARAMETER_BOOL:
228
        msg.bool_value = this.value;
560✔
229
        break;
560✔
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;
9✔
244
        break;
9✔
245
      case ParameterType.PARAMETER_INTEGER_ARRAY:
246
        msg.integer_array_value = this.value;
9✔
247
        break;
9✔
248
      case ParameterType.PARAMETER_STRING:
249
        msg.string_value = this.value;
20✔
250
        break;
20✔
251
      case ParameterType.PARAMETER_STRING_ARRAY:
252
        msg.string_array_value = this.value;
×
253
        break;
×
254
    }
255

256
    return msg;
599✔
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;
559✔
273
    const type = parameter.type;
559✔
274
    return new ParameterDescriptor(name, type, 'Created from parameter.');
559✔
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',
29✔
290
    readOnly = false,
579✔
291
    range = null
607✔
292
  ) {
293
    this._name = name; // string
607✔
294
    this._type = type; // ParameterType
607✔
295
    this._description = description;
607✔
296
    this._readOnly = readOnly;
607✔
297
    this._additionalConstraints = '';
607✔
298
    this._range = range;
607✔
299

300
    this.validate();
607✔
301
  }
302

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

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

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

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

339
  /**
340
   * Get additionalConstraints property.
341
   *
342
   * @return {string} - The additionalConstraints property.
343
   */
344
  get additionalConstraints() {
345
    return this._additionalConstraints;
2✔
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;
2,403✔
364
  }
365

366
  /**
367
   * Get range.
368
   *
369
   * @return {FloatingPointRange|IntegerRange} - The range property.
370
   */
371
  get range() {
372
    return this._range;
16✔
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) {
2!
382
      this._range = null;
×
383
      return;
×
384
    }
385
    if (!(range instanceof Range)) {
2!
386
      throw TypeError('Expected instance of Range.');
×
387
    }
388
    if (!range.isValidType(this.type)) {
2!
389
      throw TypeError('Incompatible Range');
×
390
    }
391

392
    this._range = range;
2✔
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 (
1,800!
404
      !this.name ||
5,400✔
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)) {
1,800!
413
      throw new TypeError('Invalid type');
×
414
    }
415

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

421
    // validate rangeConstraint
422
    if (this.hasRange() && !this.range.isValidType(this.type)) {
1,800!
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) {
603!
436
      throw new TypeError('Parameter is undefined');
×
437
    }
438

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

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

453
    if (this.name !== parameter.name) throw new Error('Name mismatch');
603!
454
    if (this.type !== parameter.type) throw new Error('Type mismatch');
603!
455
    if (this.hasRange() && !this.range.inRange(parameter.value)) {
603✔
456
      throw new RangeError('Parameter value is not in descriptor range');
4✔
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 = {
2✔
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 (
2!
474
      (this._type === ParameterType.PARAMETER_INTEGER ||
4✔
475
        this._type === ParameterType.PARAMETER_INTEGER_ARRAY) &&
476
      this._rangeConstraint instanceof IntegerRange
477
    ) {
478
      msg.integer_range = [this._rangeConstraint];
×
479
    } else if (
2!
480
      (this._type === ParameterType.PARAMETER_DOUBLE ||
4!
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;
2✔
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;
4✔
506
    this._toValue = toValue;
4✔
507
    this._step = step;
4✔
508
  }
509

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

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

528
  /**
529
   * Get step unit.
530
   *
531
   * @return {number} - The internal unit size.
532
   */
533
  get step() {
534
    return this._step;
31✔
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)) {
9!
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') {
9!
553
      throw new TypeError('Value must be a number or bigint');
×
554
    }
555

556
    return true;
9✔
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
1✔
590
  ) {
591
    super(fromValue, toValue, step);
1✔
592
    this._tolerance = tolerance;
1✔
593
  }
594

595
  get tolerance() {
596
    return this._tolerance;
20✔
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;
9!
621

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

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

647
    return true;
3✔
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) {
1✔
664
    super(fromValue, toValue, step);
3✔
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 ||
10!
676
      parameterType === ParameterType.PARAMETER_INTEGER_ARRAY;
677
    return result;
10✔
678
  }
679

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

687
    if (this.step != 0n && (value - min) % this.step !== 0n) {
10✔
688
      return false;
1✔
689
    }
690
    return true;
9✔
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' &&
3,649✔
737
    ParameterType.PARAMETER_NOT_SET <=
738
      parameterType <=
739
      ParameterType.PARAMETER_STRING_ARRAY;
740

741
  return result;
3,649✔
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) {
1,951✔
753
    return type === ParameterType.PARAMETER_NOT_SET;
4✔
754
  }
755

756
  let result = true;
1,947✔
757
  switch (type) {
1,947!
758
    case ParameterType.PARAMETER_NOT_SET:
759
      result = !value;
×
760
      break;
×
761
    case ParameterType.PARAMETER_BOOL:
762
      result = typeof value === 'boolean';
1,692✔
763
      break;
1,692✔
764
    case ParameterType.PARAMETER_STRING:
765
      result = typeof value === 'string';
73✔
766
      break;
73✔
767
    case ParameterType.PARAMETER_DOUBLE:
768
    case PARAMETER_BYTE:
769
      result = typeof value === 'number';
10✔
770
      break;
10✔
771
    case ParameterType.PARAMETER_INTEGER:
772
      result = typeof value === 'bigint';
139✔
773
      break;
139✔
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);
33✔
780
      break;
33✔
781
    default:
782
      result = false;
×
783
  }
784

785
  return result;
1,947✔
786
}
787

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

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

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

816
function _isArrayParameterType(type) {
817
  return (
102✔
818
    type === ParameterType.PARAMETER_BOOL_ARRAY ||
510✔
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 = {
26✔
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