@openmbee/opensysml 0.9.2 → 0.10.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 (67) hide show
  1. package/README.md +355 -33
  2. package/dist/browser/index.d.ts +9 -0
  3. package/dist/browser/index.js +16 -0
  4. package/dist/browser/wasm-worker.d.ts +1 -0
  5. package/dist/browser/wasm-worker.js +2 -0
  6. package/dist/browser/wasm.d.ts +17 -0
  7. package/dist/browser/wasm.js +94 -0
  8. package/dist/core/arguments.d.ts +4 -0
  9. package/dist/core/arguments.js +19 -0
  10. package/dist/core/capabilities.d.ts +90 -9
  11. package/dist/core/capabilities.js +111 -10
  12. package/dist/core/connection.d.ts +203 -2
  13. package/dist/core/connection.js +810 -17
  14. package/dist/core/conversion.d.ts +153 -0
  15. package/dist/core/conversion.js +191 -0
  16. package/dist/core/document.d.ts +188 -0
  17. package/dist/core/document.js +457 -0
  18. package/dist/core/edit.d.ts +446 -0
  19. package/dist/core/edit.js +1994 -0
  20. package/dist/core/engines.d.ts +64 -0
  21. package/dist/core/engines.js +74 -0
  22. package/dist/core/errors.d.ts +164 -2
  23. package/dist/core/errors.js +209 -3
  24. package/dist/core/exploration.d.ts +71 -0
  25. package/dist/core/exploration.js +116 -0
  26. package/dist/core/graphs.d.ts +15 -0
  27. package/dist/core/graphs.js +3 -0
  28. package/dist/core/index.d.ts +30 -9
  29. package/dist/core/index.js +14 -5
  30. package/dist/core/model.d.ts +168 -8
  31. package/dist/core/model.js +258 -31
  32. package/dist/core/package.d.ts +2 -0
  33. package/dist/core/package.js +2 -0
  34. package/dist/core/query.d.ts +50 -0
  35. package/dist/core/query.js +197 -0
  36. package/dist/core/render-view.d.ts +97 -0
  37. package/dist/core/render-view.js +120 -0
  38. package/dist/core/sources.d.ts +34 -0
  39. package/dist/core/sources.js +134 -0
  40. package/dist/core/status.d.ts +9 -2
  41. package/dist/core/status.js +24 -8
  42. package/dist/core/typed.d.ts +71 -0
  43. package/dist/core/typed.js +245 -0
  44. package/dist/core/values.d.ts +127 -8
  45. package/dist/core/values.js +536 -30
  46. package/dist/core/verdict.d.ts +271 -0
  47. package/dist/core/verdict.js +567 -0
  48. package/dist/core/wasm.d.ts +114 -0
  49. package/dist/core/wasm.js +514 -0
  50. package/dist/generated/sysml_pb.d.ts +1062 -15
  51. package/dist/generated/sysml_pb.js +200 -100
  52. package/dist/node/generate-cli.d.ts +2 -0
  53. package/dist/node/generate-cli.js +10 -0
  54. package/dist/node/generate.d.ts +99 -0
  55. package/dist/node/generate.js +757 -0
  56. package/dist/node/index.d.ts +29 -2
  57. package/dist/node/index.js +211 -10
  58. package/dist/node/service.d.ts +5 -3
  59. package/dist/node/service.js +32 -26
  60. package/dist/node/wasm-source.d.ts +18 -0
  61. package/dist/node/wasm-source.js +103 -0
  62. package/dist/node/wasm-worker.d.ts +1 -0
  63. package/dist/node/wasm-worker.js +10 -0
  64. package/dist/node/wasm.d.ts +17 -0
  65. package/dist/node/wasm.js +63 -0
  66. package/package.json +19 -7
  67. package/release-digests.json +105 -0
@@ -1,8 +1,9 @@
1
1
  // Values, quantities and verdicts as discriminated unions: a caller switches on
2
2
  // `kind` and the compiler checks the switch is exhaustive.
3
3
  import { create } from "@bufbuild/protobuf";
4
- import { ArraySchema, ComplexSchema, EnumLiteralSchema, FailureReason, FunctionSchema, MeasurementRefSchema, MetaobjectSchema, QuantitySchema, TensorQuantitySchema, UnitFactorSchema, UnitTermSchema, ValueSchema, ValueSequenceSchema, ValueSetSchema, VectorQuantitySchema, VectorSchema, } from "../generated/sysml_pb.js";
5
- import { MalformedValueError } from "./errors.js";
4
+ import { ArraySchema, ComplexSchema, EnumLiteralSchema, FailureReason, FunctionSchema, MeasurementRefSchema, MetaobjectSchema, QuantitySchema, RationalSchema, TensorQuantitySchema, UnitFactorSchema, UnitTermSchema, ValueSchema, ValueSequenceSchema, ValueSetSchema, VectorQuantitySchema, VectorSchema, } from "../generated/sysml_pb.js";
5
+ import { CAPABILITY_BIG_INT_VALUES, CAPABILITY_COMPLEX_VALUES, CAPABILITY_FUNCTION_VALUES, CAPABILITY_INFINITY_VALUE, CAPABILITY_MEASUREMENT_REFS, CAPABILITY_METAOBJECT_VALUES, CAPABILITY_RATIONAL_VALUES, CAPABILITY_SET_VALUES, CAPABILITY_STRUCTURED_VALUES, CAPABILITY_TENSOR_VALUES, requireCapability, upgradeRemedy, } from "./capabilities.js";
6
+ import { MalformedValueError, UnsupportedValueError } from "./errors.js";
6
7
  /**
7
8
  * Decodes a `sysml.Value` into the union.
8
9
  *
@@ -22,6 +23,10 @@ export function decodeValue(value) {
22
23
  switch (kind.case) {
23
24
  case "intValue":
24
25
  return { kind: "int", value: kind.value };
26
+ case "bigIntValue":
27
+ return { kind: "int", value: decodeBigInteger(kind.value) };
28
+ case "rationalValue":
29
+ return { kind: "rational", ...decodeRational(kind.value) };
25
30
  case "realValue":
26
31
  return { kind: "real", value: kind.value };
27
32
  case "complex":
@@ -85,7 +90,8 @@ export function decodeValue(value) {
85
90
  export function encodeValue(value) {
86
91
  switch (value.kind) {
87
92
  case "int":
88
- return create(ValueSchema, { kind: { case: "intValue", value: value.value } });
93
+ case "rational":
94
+ return encodeMagnitude(value);
89
95
  case "real":
90
96
  return create(ValueSchema, { kind: { case: "realValue", value: value.value } });
91
97
  case "complex":
@@ -180,15 +186,23 @@ export function encodeValue(value) {
180
186
  }
181
187
  }
182
188
  /** Decodes a `sysml.Verdict` into the union. */
183
- export function decodeVerdict(verdict) {
189
+ export function decodeVerdict(verdict, verifications = []) {
184
190
  const subject = {
185
191
  kind: verdict.kind,
186
192
  elementId: verdict.elementId,
187
193
  element: verdict.element,
188
194
  ...(verdict.instanceId === 0n ? {} : { instanceId: verdict.instanceId }),
189
195
  ...(verdict.instanceTypeId === "" ? {} : { instanceTypeId: verdict.instanceTypeId }),
196
+ ...(verdict.requirementId === "" ? {} : { requirementId: verdict.requirementId }),
197
+ ...(verdict.instancePath === "" ? {} : { instancePath: verdict.instancePath }),
190
198
  };
191
199
  const standing = decodeStanding(verdict);
200
+ const witness = verdict.witness.map((assignment) => ({
201
+ feature: assignment.feature,
202
+ value: decodeValue(assignment.value),
203
+ unit: assignment.unit,
204
+ exact: assignment.exact,
205
+ }));
192
206
  if (verdict.error !== "") {
193
207
  return {
194
208
  kind: "undecided",
@@ -196,18 +210,42 @@ export function decodeVerdict(verdict) {
196
210
  error: verdict.error,
197
211
  cause: failureCause(verdict.failureReason),
198
212
  standing,
213
+ question: verdict.question,
214
+ status: verdict.status,
215
+ witness,
216
+ verifications: [...verifications],
199
217
  };
200
218
  }
201
219
  return verdict.holds
202
- ? { kind: "holds", subject, standing }
203
- : { kind: "fails", subject, condition: verdict.condition, standing };
220
+ ? {
221
+ kind: "holds",
222
+ subject,
223
+ standing,
224
+ question: verdict.question,
225
+ status: verdict.status,
226
+ witness,
227
+ verifications: [...verifications],
228
+ }
229
+ : {
230
+ kind: "fails",
231
+ subject,
232
+ condition: verdict.condition,
233
+ standing,
234
+ question: verdict.question,
235
+ status: verdict.status,
236
+ witness,
237
+ verifications: [...verifications],
238
+ };
204
239
  }
205
240
  /** Reads the standing fields the `engines` capability adds to a verdict. */
206
241
  export function decodeStanding(verdict) {
242
+ const bounds = verdict.bounds.map((b) => ({ name: b.name, limit: b.limit, reached: b.reached }));
207
243
  return {
208
244
  engine: verdict.engine,
209
245
  strength: verdict.strength,
210
- bounds: verdict.bounds.map((b) => ({ name: b.name, limit: b.limit, reached: b.reached })),
246
+ bounds,
247
+ reported: verdict.strength !== "",
248
+ reached: bounds.filter((bound) => bound.reached),
211
249
  };
212
250
  }
213
251
  /** Names the enum the service reports for a failure it could not answer through. */
@@ -230,6 +268,8 @@ export function formatValue(value) {
230
268
  switch (value.kind) {
231
269
  case "int":
232
270
  return value.value.toString();
271
+ case "rational":
272
+ return formatRational(value);
233
273
  case "real":
234
274
  return formatReal(value.value);
235
275
  case "complex":
@@ -280,7 +320,37 @@ function formatReal(value) {
280
320
  return Number.isInteger(value) ? value.toFixed(1) : value.toString();
281
321
  }
282
322
  function formatMagnitude(magnitude) {
283
- return magnitude.kind === "int" ? magnitude.value.toString() : formatReal(magnitude.value);
323
+ switch (magnitude.kind) {
324
+ case "int":
325
+ return magnitude.value.toString();
326
+ case "rational":
327
+ return formatRational(magnitude);
328
+ case "real":
329
+ return formatReal(magnitude.value);
330
+ }
331
+ }
332
+ /** A terminating Rational as its decimal, `0.1`; any other as `numerator/denominator`, `1/3`. */
333
+ export function formatRational(value) {
334
+ let twos = 0;
335
+ let fives = 0;
336
+ let rest = value.denominator;
337
+ while (rest % 2n === 0n) {
338
+ rest /= 2n;
339
+ twos++;
340
+ }
341
+ while (rest % 5n === 0n) {
342
+ rest /= 5n;
343
+ fives++;
344
+ }
345
+ if (rest !== 1n) {
346
+ return `${value.numerator.toString()}/${value.denominator.toString()}`;
347
+ }
348
+ const places = Math.max(twos, fives, 1);
349
+ const scaled = (value.numerator * 10n ** BigInt(places)) / value.denominator;
350
+ const negative = scaled < 0n;
351
+ const digits = (negative ? -scaled : scaled).toString().padStart(places + 1, "0");
352
+ const point = digits.length - places;
353
+ return `${negative ? "-" : ""}${digits.slice(0, point)}.${digits.slice(point)}`;
284
354
  }
285
355
  /** `Tensor(2, 2, 2)[1.0, …, 8.0][Pa]` when every component shares a unit; else each with its own. */
286
356
  function formatTensorQuantity(tensor) {
@@ -304,9 +374,15 @@ function decodeQuantity(quantity) {
304
374
  case "intMagnitude":
305
375
  magnitude = { kind: "int", value: quantity.magnitude.value };
306
376
  break;
377
+ case "bigIntMagnitude":
378
+ magnitude = { kind: "int", value: decodeBigInteger(quantity.magnitude.value) };
379
+ break;
307
380
  case "realMagnitude":
308
381
  magnitude = { kind: "real", value: quantity.magnitude.value };
309
382
  break;
383
+ case "rationalMagnitude":
384
+ magnitude = { kind: "rational", ...decodeRational(quantity.magnitude.value) };
385
+ break;
310
386
  default:
311
387
  throw new MalformedValueError(`a quantity in [${quantity.unit}] has no magnitude`);
312
388
  }
@@ -319,9 +395,7 @@ function decodeQuantity(quantity) {
319
395
  }
320
396
  function encodeQuantity(quantity) {
321
397
  return create(QuantitySchema, {
322
- magnitude: quantity.magnitude.kind === "int"
323
- ? { case: "intMagnitude", value: quantity.magnitude.value }
324
- : { case: "realMagnitude", value: quantity.magnitude.value },
398
+ magnitude: encodeQuantityMagnitude(quantity.magnitude),
325
399
  unit: quantity.unit,
326
400
  ...(quantity.unitTerm === undefined ? {} : { unitTerm: encodeUnitTerm(quantity.unitTerm) }),
327
401
  });
@@ -388,10 +462,191 @@ function formatUnitTerm(term) {
388
462
  }
389
463
  return parts.length === 0 ? "1" : parts.join("·");
390
464
  }
465
+ /** A quantity magnitude as the wire's `Quantity.magnitude` oneof writes it. */
466
+ export function encodeQuantityMagnitude(magnitude) {
467
+ switch (magnitude.kind) {
468
+ case "real":
469
+ return { case: "realMagnitude", value: magnitude.value };
470
+ case "rational": {
471
+ return { case: "rationalMagnitude", value: encodeRational(magnitude) };
472
+ }
473
+ case "int":
474
+ return fitsInt64(magnitude.value)
475
+ ? { case: "intMagnitude", value: magnitude.value }
476
+ : { case: "bigIntMagnitude", value: magnitude.value.toString() };
477
+ }
478
+ }
391
479
  function encodeMagnitude(magnitude) {
392
- return magnitude.kind === "int"
480
+ if (magnitude.kind === "real") {
481
+ return create(ValueSchema, { kind: { case: "realValue", value: magnitude.value } });
482
+ }
483
+ if (magnitude.kind === "rational") {
484
+ return create(ValueSchema, { kind: { case: "rationalValue", value: encodeRational(magnitude) } });
485
+ }
486
+ return fitsInt64(magnitude.value)
393
487
  ? create(ValueSchema, { kind: { case: "intValue", value: magnitude.value } })
394
- : create(ValueSchema, { kind: { case: "realValue", value: magnitude.value } });
488
+ : create(ValueSchema, { kind: { case: "bigIntValue", value: magnitude.value.toString() } });
489
+ }
490
+ const INT64_MIN = -(2n ** 63n);
491
+ const INT64_MAX = 2n ** 63n - 1n;
492
+ /** Whether an Integer travels as `int_value`; one beyond int64 travels as `big_int_value`. */
493
+ export function fitsInt64(value) {
494
+ return value >= INT64_MIN && value <= INT64_MAX;
495
+ }
496
+ /** Reads the decimal of a `big_int_value` or `big_int_magnitude`. */
497
+ export function decodeBigInteger(text) {
498
+ if (!/^-?\d+$/.test(text)) {
499
+ throw new MalformedValueError(`a big Integer ${JSON.stringify(text)} is not decimal`);
500
+ }
501
+ return BigInt(text);
502
+ }
503
+ /**
504
+ * Rewrites in place each Rational arm of a wire value a double holds exactly as
505
+ * that double: the form a service without `rational_values` reads. A Rational
506
+ * no double holds is left as it is; a collection's elements are values of their own.
507
+ */
508
+ export function rationalsAsReals(value) {
509
+ switch (value.kind.case) {
510
+ case "rationalValue": {
511
+ const double = wireRationalAsDouble(value.kind.value);
512
+ if (double !== undefined) {
513
+ value.kind = { case: "realValue", value: double };
514
+ }
515
+ return;
516
+ }
517
+ case "quantity":
518
+ quantityRationalAsReal(value.kind.value);
519
+ return;
520
+ case "vector":
521
+ value.kind.value.components.forEach(rationalsAsReals);
522
+ return;
523
+ case "vectorQuantity":
524
+ value.kind.value.components.forEach(quantityRationalAsReal);
525
+ return;
526
+ case "tensorQuantity":
527
+ value.kind.value.components.forEach(quantityRationalAsReal);
528
+ return;
529
+ default:
530
+ return;
531
+ }
532
+ }
533
+ /** Rewrites a quantity's Rational magnitude a double holds exactly as `real_magnitude`. */
534
+ export function quantityRationalAsReal(quantity) {
535
+ if (quantity.magnitude.case === "rationalMagnitude") {
536
+ const double = wireRationalAsDouble(quantity.magnitude.value);
537
+ if (double !== undefined) {
538
+ quantity.magnitude = { case: "realMagnitude", value: double };
539
+ }
540
+ }
541
+ }
542
+ function wireRationalAsDouble(wire) {
543
+ return rationalAsDouble({ numerator: BigInt(wire.numerator), denominator: BigInt(wire.denominator) });
544
+ }
545
+ /** The `Rational` message of an exact Rational, numerator and denominator in full. */
546
+ export function encodeRational(value) {
547
+ return create(RationalSchema, {
548
+ numerator: value.numerator.toString(),
549
+ denominator: value.denominator.toString(),
550
+ });
551
+ }
552
+ /**
553
+ * Reads a `rational_value` or `rational_magnitude`.
554
+ *
555
+ * @throws {MalformedValueError} for one not in lowest terms over a positive
556
+ * denominator, or one a double holds, which travels as `real_value`.
557
+ */
558
+ export function decodeRational(rational) {
559
+ const numerator = decodeBigInteger(rational.numerator);
560
+ const denominator = decodeBigInteger(rational.denominator);
561
+ const text = `${rational.numerator}/${rational.denominator}`;
562
+ if (denominator <= 0n || gcd(numerator, denominator) !== 1n) {
563
+ throw new MalformedValueError(`a Rational ${text} is not in lowest terms over a positive denominator`);
564
+ }
565
+ if (rationalAsDouble({ numerator, denominator }) !== undefined) {
566
+ throw new MalformedValueError(`a Rational ${text} is a double, which travels as real_value`);
567
+ }
568
+ return { numerator, denominator };
569
+ }
570
+ /** The exact Rational `numerator/denominator`, reduced; throws a RangeError over a zero denominator. */
571
+ export function rational(numerator, denominator = 1n) {
572
+ if (denominator === 0n) {
573
+ throw new RangeError("a Rational's denominator is not zero");
574
+ }
575
+ const sign = denominator < 0n ? -1n : 1n;
576
+ const divisor = gcd(numerator, denominator);
577
+ return { kind: "rational", numerator: (sign * numerator) / divisor, denominator: (sign * denominator) / divisor };
578
+ }
579
+ function gcd(a, b) {
580
+ a = a < 0n ? -a : a;
581
+ b = b < 0n ? -b : b;
582
+ while (b !== 0n) {
583
+ [a, b] = [b, a % b];
584
+ }
585
+ return a;
586
+ }
587
+ function bitLength(n) {
588
+ return n === 0n ? 0 : (n < 0n ? -n : n).toString(2).length;
589
+ }
590
+ /** The exact Rational a finite double holds. */
591
+ export function rationalOfDouble(x) {
592
+ if (!Number.isFinite(x)) {
593
+ throw new RangeError(`${x} is no Rational`);
594
+ }
595
+ let whole = x;
596
+ let denominator = 1n;
597
+ while (!Number.isInteger(whole)) {
598
+ whole *= 2;
599
+ denominator *= 2n;
600
+ }
601
+ return rational(BigInt(whole), denominator);
602
+ }
603
+ /** The double that holds a reduced Rational exactly, or undefined where none does. */
604
+ export function rationalAsDouble(value) {
605
+ const { numerator, denominator } = value;
606
+ if ((denominator & (denominator - 1n)) !== 0n) {
607
+ return undefined;
608
+ }
609
+ const shift = bitLength(denominator) - 1;
610
+ let odd = numerator;
611
+ let zeros = 0;
612
+ while (odd !== 0n && odd % 2n === 0n) {
613
+ odd /= 2n;
614
+ zeros++;
615
+ }
616
+ if (bitLength(odd) > 53 || zeros - shift < -1074 || bitLength(numerator) - shift > 1024) {
617
+ return undefined;
618
+ }
619
+ return scaleByPowerOfTwo(Number(odd), zeros - shift);
620
+ }
621
+ /** The double nearest the Rational: how a Real reads one. */
622
+ export function rationalToNumber(value) {
623
+ const exact = rationalAsDouble(value);
624
+ if (exact !== undefined) {
625
+ return exact;
626
+ }
627
+ const negative = value.numerator < 0n;
628
+ const magnitude = negative ? -value.numerator : value.numerator;
629
+ // A quotient of at least 55 bits, its last bit sticky, rounds once to 53.
630
+ const shift = 55 - (bitLength(magnitude) - bitLength(value.denominator));
631
+ const num = shift >= 0 ? magnitude << BigInt(shift) : magnitude;
632
+ const den = shift >= 0 ? value.denominator : value.denominator << BigInt(-shift);
633
+ let quotient = num / den;
634
+ if (num % den !== 0n) {
635
+ quotient |= 1n;
636
+ }
637
+ const result = scaleByPowerOfTwo(Number(quotient), -shift);
638
+ return negative ? -result : result;
639
+ }
640
+ function scaleByPowerOfTwo(x, exponent) {
641
+ while (exponent > 1000) {
642
+ x *= 2 ** 1000;
643
+ exponent -= 1000;
644
+ }
645
+ while (exponent < -1000) {
646
+ x *= 2 ** -1000;
647
+ exponent += 1000;
648
+ }
649
+ return x * 2 ** exponent;
395
650
  }
396
651
  /** The flattened size the dimensions demand, refusing a dimension that is not positive. */
397
652
  function checkShape(what, dimensions, elementCount) {
@@ -441,6 +696,7 @@ export function valuesEqual(a, b) {
441
696
  }
442
697
  switch (a.kind) {
443
698
  case "int":
699
+ case "rational":
444
700
  case "real":
445
701
  case "complex":
446
702
  return numbersEqual(a, b);
@@ -531,18 +787,40 @@ function numbersEqual(a, b) {
531
787
  }
532
788
  b = { kind: "real", value: b.value.real };
533
789
  }
534
- if (a.kind === "int") {
535
- if (b.kind === "int")
536
- return a.value === b.value;
537
- return b.kind === "real" && realIsInt(b.value, a.value);
790
+ if (a.kind === "real" && b.kind === "real") {
791
+ return a.value === b.value;
538
792
  }
539
- if (b.kind === "int")
540
- return realIsInt(a.value, b.value);
541
- return b.kind === "real" && a.value === b.value;
793
+ // A Rational meets a Real at Real precision, as the service compares them.
794
+ if (a.kind === "rational" && b.kind === "real") {
795
+ return rationalToNumber(a) === b.value;
796
+ }
797
+ if (a.kind === "real" && b.kind === "rational") {
798
+ return a.value === rationalToNumber(b);
799
+ }
800
+ const x = exactOf(a);
801
+ const y = exactOf(b);
802
+ return x !== undefined && y !== undefined && x.numerator * y.denominator === y.numerator * x.denominator;
542
803
  }
543
- // Whether r is exactly the integer n, never rounding n.
544
- function realIsInt(r, n) {
545
- return Number.isInteger(r) && r >= -(2 ** 63) && r < 2 ** 63 && BigInt(r) === n;
804
+ // The exact number a value holds, a finite Real's double included, never rounding.
805
+ function exactOf(value) {
806
+ switch (value.kind) {
807
+ case "int":
808
+ return { numerator: value.value, denominator: 1n };
809
+ case "rational":
810
+ return value;
811
+ case "real":
812
+ return Number.isFinite(value.value) ? doubleAsRational(value.value) : undefined;
813
+ default:
814
+ return undefined;
815
+ }
816
+ }
817
+ function doubleAsRational(x) {
818
+ let denominator = 1n;
819
+ while (!Number.isInteger(x)) {
820
+ x *= 2;
821
+ denominator *= 2n;
822
+ }
823
+ return { numerator: BigInt(x), denominator };
546
824
  }
547
825
  function magnitudesEqual(a, b) {
548
826
  return a.length === b.length && a.every((m, i) => numbersEqual(m, b[i]));
@@ -564,18 +842,18 @@ function quantitiesEqual(a, b) {
564
842
  zeroScale(b.unitTerm)) {
565
843
  return false;
566
844
  }
567
- if (a.magnitude.kind === "int" &&
568
- b.magnitude.kind === "int" &&
569
- wholeScale(a.unitTerm) &&
570
- wholeScale(b.unitTerm)) {
845
+ const x = a.magnitude.kind === "real" ? undefined : exactOf(a.magnitude);
846
+ const y = b.magnitude.kind === "real" ? undefined : exactOf(b.magnitude);
847
+ if (x !== undefined && y !== undefined && wholeScale(a.unitTerm) && wholeScale(b.unitTerm)) {
571
848
  // m₁·n₁/d₁ = m₂·n₂/d₂ exactly, cross-multiplied in bigint.
572
- return (a.magnitude.value * BigInt(a.unitTerm.scaleNum) * BigInt(b.unitTerm.scaleDen) ===
573
- b.magnitude.value * BigInt(b.unitTerm.scaleNum) * BigInt(a.unitTerm.scaleDen));
849
+ return (x.numerator * y.denominator * BigInt(a.unitTerm.scaleNum) * BigInt(b.unitTerm.scaleDen) ===
850
+ y.numerator * x.denominator * BigInt(b.unitTerm.scaleNum) * BigInt(a.unitTerm.scaleDen));
574
851
  }
575
852
  return baseMagnitude(a.magnitude, a.unitTerm) === baseMagnitude(b.magnitude, b.unitTerm);
576
853
  }
577
854
  function baseMagnitude(magnitude, term) {
578
- return (Number(magnitude.value) * term.scaleNum) / term.scaleDen;
855
+ const value = magnitude.kind === "rational" ? rationalToNumber(magnitude) : Number(magnitude.value);
856
+ return (value * term.scaleNum) / term.scaleDen;
579
857
  }
580
858
  // The reduction as base unit → exponent, repeated base units summed and
581
859
  // cancelled ones dropped, so two reductions compare however they are listed.
@@ -639,8 +917,12 @@ function decodeVector(vector) {
639
917
  switch (component.kind.case) {
640
918
  case "intValue":
641
919
  return { kind: "int", value: component.kind.value };
920
+ case "bigIntValue":
921
+ return { kind: "int", value: decodeBigInteger(component.kind.value) };
642
922
  case "realValue":
643
923
  return { kind: "real", value: component.kind.value };
924
+ case "rationalValue":
925
+ return { kind: "rational", ...decodeRational(component.kind.value) };
644
926
  default:
645
927
  throw new MalformedValueError(`a vector component is ${component.kind.case ?? "empty"}, not a number`);
646
928
  }
@@ -685,3 +967,227 @@ function encodeEnumLiteral(literal) {
685
967
  value: literal.value === undefined ? undefined : encodeValue(literal.value),
686
968
  });
687
969
  }
970
+ /** Refuses an instance or `self` id outside int64, the width of the wire's id fields. */
971
+ function checkInt64(value) {
972
+ if (!fitsInt64(value)) {
973
+ throw new RangeError(`value out of range: ${value.toString()}`);
974
+ }
975
+ }
976
+ /**
977
+ * Encodes a caller-facing value for the wire, requiring of `info` each
978
+ * capability a value kind needs before the call is sent — exactly the checks
979
+ * Python's `Connection._python_to_value` makes. A `SysMLValue` encodes as
980
+ * itself; a boolean, string, bigint and number encode as the scalar arms; a
981
+ * JavaScript array and `Set` encode as a sequence and a set.
982
+ */
983
+ export function toValue(input, info) {
984
+ const probe = input;
985
+ if (typeof probe === "object" && probe !== null && "$typeName" in probe) {
986
+ return probe;
987
+ }
988
+ return encodeInput(normalizeInput(input), info);
989
+ }
990
+ /** The `SysMLValue` one input names, collection elements converted the same way. */
991
+ function normalizeInput(input) {
992
+ switch (typeof input) {
993
+ case "boolean":
994
+ return { kind: "boolean", value: input };
995
+ case "bigint":
996
+ return { kind: "int", value: input };
997
+ case "number":
998
+ return { kind: "real", value: input };
999
+ case "string":
1000
+ return { kind: "string", value: input };
1001
+ case "undefined":
1002
+ throw new RangeError("unsupported input type: undefined");
1003
+ case "function":
1004
+ case "symbol":
1005
+ case "object":
1006
+ break;
1007
+ }
1008
+ if (input === null) {
1009
+ return { kind: "null", reason: "" };
1010
+ }
1011
+ if (Array.isArray(input)) {
1012
+ return { kind: "sequence", elements: input.map(normalizeInput) };
1013
+ }
1014
+ if (input instanceof Set) {
1015
+ return { kind: "set", elements: [...input].map(normalizeInput) };
1016
+ }
1017
+ const value = input;
1018
+ switch (value.kind) {
1019
+ case "sequence":
1020
+ return { kind: "sequence", elements: value.elements.map(normalizeInput) };
1021
+ case "set":
1022
+ return { kind: "set", elements: value.elements.map(normalizeInput) };
1023
+ case "array":
1024
+ return {
1025
+ kind: "array",
1026
+ dimensions: value.dimensions,
1027
+ elements: value.elements.map(normalizeInput),
1028
+ };
1029
+ case "enum":
1030
+ return value.value.value === undefined
1031
+ ? value
1032
+ : { kind: "enum", value: { ...value.value, value: normalizeInput(value.value.value) } };
1033
+ case "unset":
1034
+ throw new RangeError("an unset value cannot be sent as an input; it is a value the service answers, not one it takes");
1035
+ default:
1036
+ if (typeof value.kind === "string") {
1037
+ return value;
1038
+ }
1039
+ throw new RangeError(`unsupported input type: ${typeof input}`);
1040
+ }
1041
+ }
1042
+ function encodeInput(value, info) {
1043
+ requireInput(value, info);
1044
+ switch (value.kind) {
1045
+ case "sequence":
1046
+ return create(ValueSchema, {
1047
+ kind: {
1048
+ case: "sequence",
1049
+ value: create(ValueSequenceSchema, {
1050
+ elements: value.elements.map((element) => encodeInput(element, info)),
1051
+ }),
1052
+ },
1053
+ });
1054
+ case "array":
1055
+ checkShape("an array", value.dimensions, value.elements.length);
1056
+ return create(ValueSchema, {
1057
+ kind: {
1058
+ case: "array",
1059
+ value: create(ArraySchema, {
1060
+ dimensions: value.dimensions,
1061
+ elements: value.elements.map((element) => encodeInput(element, info)),
1062
+ }),
1063
+ },
1064
+ });
1065
+ case "set":
1066
+ return create(ValueSchema, {
1067
+ kind: {
1068
+ case: "set",
1069
+ value: create(ValueSetSchema, {
1070
+ elements: uniqueMembers(value.elements).map((element) => encodeInput(element, info)),
1071
+ }),
1072
+ },
1073
+ });
1074
+ default: {
1075
+ const wire = encodeValue(value);
1076
+ if (!info.has(CAPABILITY_RATIONAL_VALUES)) {
1077
+ rationalsAsReals(wire);
1078
+ }
1079
+ return wire;
1080
+ }
1081
+ }
1082
+ }
1083
+ /** The capabilities one input value needs of the service that will decode it. */
1084
+ export function requireInput(value, info) {
1085
+ const require = (capability) => {
1086
+ requireCapability(info, capability, upgradeRemedy(capability));
1087
+ };
1088
+ const requireMagnitudes = (magnitudes) => {
1089
+ if (magnitudes.some((magnitude) => magnitude.kind === "int" && !fitsInt64(magnitude.value))) {
1090
+ require(CAPABILITY_BIG_INT_VALUES);
1091
+ }
1092
+ if (magnitudes.some((magnitude) => magnitude.kind === "rational" && rationalAsDouble(magnitude) === undefined)) {
1093
+ require(CAPABILITY_RATIONAL_VALUES);
1094
+ }
1095
+ };
1096
+ switch (value.kind) {
1097
+ case "int":
1098
+ case "rational":
1099
+ requireMagnitudes([value]);
1100
+ return;
1101
+ case "complex":
1102
+ require(CAPABILITY_COMPLEX_VALUES);
1103
+ return;
1104
+ case "quantity":
1105
+ requireMagnitudes([value.magnitude]);
1106
+ checkReduction(`quantity in [${value.unit}]`, value.unit, value.unitTerm);
1107
+ return;
1108
+ case "measurementRef": {
1109
+ require(CAPABILITY_MEASUREMENT_REFS);
1110
+ const unitTerm = value.unitTerm;
1111
+ if (value.unit === "" && (value.unitId ?? "") === "" && unitTerm === undefined) {
1112
+ throw new UnsupportedValueError("measurement reference naming no unit");
1113
+ }
1114
+ // The wire needs the reduction on every reference, id-only ones too;
1115
+ // encodeMeasurementRef reads unitTerm unconditionally.
1116
+ if (unitTerm === undefined) {
1117
+ throw new UnsupportedValueError(`measurement reference ${value.unit || value.unitId} carries no reduction to base units, so the service cannot tell what it ` +
1118
+ "measures: build it from a unit the service sent, or from one the model declares");
1119
+ }
1120
+ return;
1121
+ }
1122
+ case "instance":
1123
+ checkInt64(value.id);
1124
+ return;
1125
+ case "unset":
1126
+ throw new RangeError("an unset value cannot be sent as an input; it is a value the service answers, not one it takes");
1127
+ case "function":
1128
+ require(CAPABILITY_FUNCTION_VALUES);
1129
+ if (value.selfId !== undefined) {
1130
+ checkInt64(value.selfId);
1131
+ }
1132
+ return;
1133
+ case "metaobject":
1134
+ require(CAPABILITY_METAOBJECT_VALUES);
1135
+ return;
1136
+ case "array":
1137
+ require(CAPABILITY_STRUCTURED_VALUES);
1138
+ value.elements.forEach((element) => {
1139
+ requireInput(element, info);
1140
+ });
1141
+ return;
1142
+ case "vector":
1143
+ require(CAPABILITY_STRUCTURED_VALUES);
1144
+ requireMagnitudes(value.components);
1145
+ return;
1146
+ case "vectorQuantity":
1147
+ require(CAPABILITY_STRUCTURED_VALUES);
1148
+ requireMagnitudes(value.components.map((component) => component.magnitude));
1149
+ value.components.forEach((component) => {
1150
+ checkReduction(`quantity in [${component.unit}]`, component.unit, component.unitTerm);
1151
+ });
1152
+ return;
1153
+ case "set":
1154
+ require(CAPABILITY_SET_VALUES);
1155
+ value.elements.forEach((element) => {
1156
+ requireInput(element, info);
1157
+ });
1158
+ return;
1159
+ case "tensorQuantity":
1160
+ require(CAPABILITY_TENSOR_VALUES);
1161
+ requireMagnitudes(value.components.map((component) => component.magnitude));
1162
+ value.components.forEach((component) => {
1163
+ checkReduction(`quantity in [${component.unit}]`, component.unit, component.unitTerm);
1164
+ });
1165
+ return;
1166
+ case "infinity":
1167
+ require(CAPABILITY_INFINITY_VALUE);
1168
+ return;
1169
+ case "enum":
1170
+ if (value.value.value !== undefined) {
1171
+ requireInput(value.value.value, info);
1172
+ }
1173
+ return;
1174
+ case "sequence":
1175
+ value.elements.forEach((element) => {
1176
+ requireInput(element, info);
1177
+ });
1178
+ return;
1179
+ default:
1180
+ return;
1181
+ }
1182
+ }
1183
+ /**
1184
+ * Refuses a unit named without its reduction to base units — the service
1185
+ * decides commensurability over the reduction and rejects a unit sent without
1186
+ * one; an unnamed unit means dimension one and carries none.
1187
+ */
1188
+ function checkReduction(what, unit, unitTerm) {
1189
+ if (unit !== "" && unitTerm === undefined) {
1190
+ throw new UnsupportedValueError(`${what} carries no reduction to base units, so the service cannot tell what it ` +
1191
+ "measures: build it from a unit the service sent, or from one the model declares");
1192
+ }
1193
+ }