measurable 2.0.0 → 3.1.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 (43) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +129 -27
  3. package/dist/dimensions/angle.js +12 -5
  4. package/dist/dimensions/area.js +37 -16
  5. package/dist/dimensions/data.js +11 -8
  6. package/dist/dimensions/energy.js +18 -11
  7. package/dist/dimensions/force.js +5 -5
  8. package/dist/dimensions/frequency.js +2 -2
  9. package/dist/dimensions/illuminance.js +7 -4
  10. package/dist/dimensions/length.js +6 -6
  11. package/dist/dimensions/luminance.js +5 -8
  12. package/dist/dimensions/luminousIntensity.js +7 -7
  13. package/dist/dimensions/mass.js +12 -8
  14. package/dist/dimensions/power.js +4 -4
  15. package/dist/dimensions/pressure.js +12 -9
  16. package/dist/dimensions/temperature.js +3 -3
  17. package/dist/dimensions/time.js +18 -6
  18. package/dist/dimensions/volume.js +56 -23
  19. package/dist/errors/AmbiguousUnitError.js +1 -2
  20. package/dist/errors/ArgumentError.d.ts +7 -0
  21. package/dist/errors/ArgumentError.js +11 -0
  22. package/dist/errors/DimensionMismatchError.d.ts +13 -0
  23. package/dist/errors/DimensionMismatchError.js +16 -0
  24. package/dist/errors/DuplicateUnitError.d.ts +5 -0
  25. package/dist/errors/DuplicateUnitError.js +10 -0
  26. package/dist/errors/ParseError.d.ts +4 -0
  27. package/dist/errors/ParseError.js +10 -0
  28. package/dist/errors/UnsupportedDimensionError.d.ts +10 -0
  29. package/dist/errors/UnsupportedDimensionError.js +14 -0
  30. package/dist/index.d.ts +5 -1
  31. package/dist/index.js +5 -1
  32. package/dist/lib/Dimension.d.ts +14 -5
  33. package/dist/lib/Dimension.js +22 -19
  34. package/dist/lib/MeasurementSystem.js +4 -15
  35. package/dist/lib/Quantity.d.ts +81 -3
  36. package/dist/lib/Quantity.js +82 -4
  37. package/dist/lib/Rational.d.ts +1 -1
  38. package/dist/lib/Rational.js +6 -5
  39. package/dist/lib/Unit.d.ts +18 -11
  40. package/dist/lib/Unit.js +7 -3
  41. package/dist/utils/definePrefixed.d.ts +8 -15
  42. package/dist/utils/definePrefixed.js +21 -10
  43. package/package.json +8 -3
@@ -9,16 +9,20 @@ const definePrefixed_1 = require("../utils/definePrefixed");
9
9
  * choice of base is internal and does not affect any conversion.)
10
10
  */
11
11
  exports.mass = new Dimension_1.Dimension("mass");
12
- exports.gram = exports.mass.base("gram", ["g", "grams"]);
12
+ exports.gram = exports.mass.base("gram", { symbol: "g", plural: "grams" });
13
13
  // Customary units, expressed in grams.
14
- exports.pound = exports.mass.unit("pound", 453.59237, ["lb", "lbs", "pounds"]);
15
- exports.ounce = exports.mass.unit("ounce", 28.349523125, ["oz", "ounces"]);
16
- exports.stone = exports.mass.unit("stone", 6350.29318, ["st", "stones"]);
17
- exports.tonne = exports.mass.unit("tonne", 1000000, ["t", "tonnes"]);
14
+ exports.pound = exports.mass.unit("pound", 453.59237, {
15
+ symbol: "lb",
16
+ plural: "pounds",
17
+ aliases: ["lbs"],
18
+ });
19
+ exports.ounce = exports.mass.unit("ounce", 28.349523125, { symbol: "oz", plural: "ounces" });
20
+ exports.stone = exports.mass.unit("stone", 6350.29318, { symbol: "st", plural: "stones" });
21
+ exports.tonne = exports.mass.unit("tonne", 1000000, { symbol: "t", plural: "tonnes" });
18
22
  // Short (US) and long (Imperial) tons share the "ton" alias but differ in size;
19
23
  // the metric tonne above is a separate unit again. Parsing disambiguates these.
20
- exports.shortTon = exports.mass.unit("shortTon", 907184.74, ["ton", "tons"]);
21
- exports.longTon = exports.mass.unit("longTon", 1016046.9088, ["ton", "tons"]);
24
+ exports.shortTon = exports.mass.unit("shortTon", 907184.74, { plural: "tons", aliases: ["ton"] });
25
+ exports.longTon = exports.mass.unit("longTon", 1016046.9088, { plural: "tons", aliases: ["ton"] });
22
26
  /** Every SI-prefixed gram — including the kilogram — keyed by name. */
23
- exports.metricMass = (0, definePrefixed_1.definePrefixed)(exports.mass, { name: "gram", symbol: "g" });
27
+ exports.metricMass = (0, definePrefixed_1.definePrefixed)(exports.mass, exports.gram);
24
28
  exports.kilogram = exports.metricMass.kilogram, exports.megagram = exports.metricMass.megagram, exports.hectogram = exports.metricMass.hectogram, exports.decagram = exports.metricMass.decagram, exports.decigram = exports.metricMass.decigram, exports.centigram = exports.metricMass.centigram, exports.milligram = exports.metricMass.milligram, exports.microgram = exports.metricMass.microgram, exports.nanogram = exports.metricMass.nanogram;
@@ -5,9 +5,9 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Power. Base unit: watt. */
7
7
  exports.power = new Dimension_1.Dimension("power");
8
- exports.watt = exports.power.base("watt", ["W", "watts"]);
9
- exports.horsepower = exports.power.unit("horsepower", 745.699872, ["hp"]);
10
- exports.metricHorsepower = exports.power.unit("metricHorsepower", 735.49875, ["PS"]);
8
+ exports.watt = exports.power.base("watt", { symbol: "W", plural: "watts" });
9
+ exports.horsepower = exports.power.unit("horsepower", 745.699872, { symbol: "hp" });
10
+ exports.metricHorsepower = exports.power.unit("metricHorsepower", 735.49875, { symbol: "PS" });
11
11
  /** Every SI-prefixed watt (kilowatt, megawatt, gigawatt, milliwatt, …), keyed by name. */
12
- exports.metricPower = (0, definePrefixed_1.definePrefixed)(exports.power, { name: "watt", symbol: "W" });
12
+ exports.metricPower = (0, definePrefixed_1.definePrefixed)(exports.power, exports.watt);
13
13
  exports.kilowatt = exports.metricPower.kilowatt, exports.megawatt = exports.metricPower.megawatt, exports.gigawatt = exports.metricPower.gigawatt, exports.terawatt = exports.metricPower.terawatt, exports.milliwatt = exports.metricPower.milliwatt;
@@ -5,14 +5,17 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Pressure. Base unit: pascal. */
7
7
  exports.pressure = new Dimension_1.Dimension("pressure");
8
- exports.pascal = exports.pressure.base("pascal", ["Pa", "pascals"]);
9
- exports.bar = exports.pressure.unit("bar", 1e5, ["bars"]);
10
- exports.millibar = exports.pressure.unit("millibar", 1e2, ["mbar", "millibars"]);
11
- exports.atmosphere = exports.pressure.unit("atmosphere", 101325, ["atm", "atmospheres"]);
12
- exports.torr = exports.pressure.unit("torr", 101325 / 760, ["Torr", "torrs"]);
13
- exports.psi = exports.pressure.unit("psi", 6894.757293168, ["lbf/in²", "lbf/in2"]);
14
- exports.inchOfMercury = exports.pressure.unit("inchOfMercury", 3386.389, ["inHg"]);
15
- exports.inchOfWater = exports.pressure.unit("inchOfWater", 249.0889, ["inAq"]);
8
+ exports.pascal = exports.pressure.base("pascal", { symbol: "Pa", plural: "pascals" });
9
+ exports.bar = exports.pressure.unit("bar", 1e5, { symbol: "bar", plural: "bars" });
10
+ exports.millibar = exports.pressure.unit("millibar", 1e2, { symbol: "mbar", plural: "millibars" });
11
+ exports.atmosphere = exports.pressure.unit("atmosphere", 101325, {
12
+ symbol: "atm",
13
+ plural: "atmospheres",
14
+ });
15
+ exports.torr = exports.pressure.unit("torr", 101325 / 760, { symbol: "Torr", plural: "torrs" });
16
+ exports.psi = exports.pressure.unit("psi", 6894.757293168, { aliases: ["lbf/in²", "lbf/in2"] });
17
+ exports.inchOfMercury = exports.pressure.unit("inchOfMercury", 3386.389, { symbol: "inHg" });
18
+ exports.inchOfWater = exports.pressure.unit("inchOfWater", 249.0889, { symbol: "inAq" });
16
19
  /** Every SI-prefixed pascal (kilopascal, hectopascal, megapascal, …), keyed by name. */
17
- exports.metricPressure = (0, definePrefixed_1.definePrefixed)(exports.pressure, { name: "pascal", symbol: "Pa" });
20
+ exports.metricPressure = (0, definePrefixed_1.definePrefixed)(exports.pressure, exports.pascal);
18
21
  exports.kilopascal = exports.metricPressure.kilopascal, exports.hectopascal = exports.metricPressure.hectopascal, exports.megapascal = exports.metricPressure.megapascal, exports.gigapascal = exports.metricPressure.gigapascal;
@@ -13,6 +13,6 @@ exports.temperature = new Dimension_1.Dimension("temperature");
13
13
  // rational arithmetic so conversions round-trip without drift.
14
14
  const fahrenheitScale = new Rational_1.Rational(5, 9);
15
15
  const fahrenheitOffset = Rational_1.Rational.from(273.15).minus(new Rational_1.Rational(32).times(fahrenheitScale));
16
- exports.kelvin = exports.temperature.base("kelvin", ["K"]);
17
- exports.celsius = exports.temperature.affine("celsius", { scale: 1, offset: 273.15 }, ["C", "°C"]);
18
- exports.fahrenheit = exports.temperature.affine("fahrenheit", { scale: fahrenheitScale, offset: fahrenheitOffset }, ["F", "°F"]);
16
+ exports.kelvin = exports.temperature.base("kelvin", { symbol: "K" });
17
+ exports.celsius = exports.temperature.affine("celsius", { scale: 1, offset: 273.15 }, { symbol: "°C", aliases: ["C"] });
18
+ exports.fahrenheit = exports.temperature.affine("fahrenheit", { scale: fahrenheitScale, offset: fahrenheitOffset }, { symbol: "°F", aliases: ["F"] });
@@ -5,14 +5,26 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Time / duration. Base unit: second. */
7
7
  exports.time = new Dimension_1.Dimension("time");
8
- exports.second = exports.time.base("second", ["s", "sec", "secs", "seconds"]);
9
- exports.minute = exports.time.unit("minute", 60, ["min", "mins", "minutes"]);
10
- exports.hour = exports.time.unit("hour", 3600, ["h", "hr", "hrs", "hours"]);
11
- exports.day = exports.time.unit("day", 86400, ["d", "days"]);
12
- exports.week = exports.time.unit("week", 604800, ["wk", "weeks"]);
8
+ exports.second = exports.time.base("second", {
9
+ symbol: "s",
10
+ plural: "seconds",
11
+ aliases: ["sec", "secs"],
12
+ });
13
+ exports.minute = exports.time.unit("minute", 60, {
14
+ symbol: "min",
15
+ plural: "minutes",
16
+ aliases: ["mins"],
17
+ });
18
+ exports.hour = exports.time.unit("hour", 3600, {
19
+ symbol: "h",
20
+ plural: "hours",
21
+ aliases: ["hr", "hrs"],
22
+ });
23
+ exports.day = exports.time.unit("day", 86400, { symbol: "d", plural: "days" });
24
+ exports.week = exports.time.unit("week", 604800, { symbol: "wk", plural: "weeks" });
13
25
  /**
14
26
  * SI-submultiple seconds (millisecond, microsecond, nanosecond, …). Only
15
27
  * fractions are generated; larger spans use minute/hour/day/week above.
16
28
  */
17
- exports.metricTime = (0, definePrefixed_1.definePrefixed)(exports.time, { name: "second", symbol: "s" }, definePrefixed_1.SI_SUBMULTIPLE_PREFIXES);
29
+ exports.metricTime = (0, definePrefixed_1.definePrefixed)(exports.time, exports.second, definePrefixed_1.SI_SUBMULTIPLE_PREFIXES);
18
30
  exports.millisecond = exports.metricTime.millisecond, exports.microsecond = exports.metricTime.microsecond, exports.nanosecond = exports.metricTime.nanosecond, exports.picosecond = exports.metricTime.picosecond;
@@ -5,33 +5,66 @@ const Dimension_1 = require("../lib/Dimension");
5
5
  const definePrefixed_1 = require("../utils/definePrefixed");
6
6
  /** Volume / capacity. Base unit: liter. */
7
7
  exports.volume = new Dimension_1.Dimension("volume");
8
- exports.liter = exports.volume.base("liter", ["L", "liters"]);
8
+ exports.liter = exports.volume.base("liter", { symbol: "L", plural: "liters" });
9
9
  // US and Imperial liquid measures share names ("gallon", "pint", …) but differ
10
10
  // in size, so each is a distinct unit carrying the shared aliases; parsing
11
11
  // disambiguates via a preferred measurement system. Imperial has 160 fluid
12
12
  // ounces per gallon, US customary has 128.
13
- exports.usGallon = exports.volume.unit("usGallon", 3.785411784, ["gal", "gallon", "gallons"]);
14
- exports.usQuart = exports.volume.unit("usQuart", 0.946352946, ["qt", "quart", "quarts"]);
15
- exports.usPint = exports.volume.unit("usPint", 0.473176473, ["pt", "pint", "pints"]);
16
- exports.usGill = exports.volume.unit("usGill", 0.11829411825, ["gill", "gills"]);
17
- exports.usFluidOunce = exports.volume.unit("usFluidOunce", 0.0295735295625, [
18
- "floz",
19
- "fluidOunce",
20
- "fluidOunces",
21
- ]);
22
- exports.imperialGallon = exports.volume.unit("imperialGallon", 4.54609, ["gal", "gallon", "gallons"]);
23
- exports.imperialQuart = exports.volume.unit("imperialQuart", 1.1365225, ["qt", "quart", "quarts"]);
24
- exports.imperialPint = exports.volume.unit("imperialPint", 0.56826125, ["pt", "pint", "pints"]);
25
- exports.imperialGill = exports.volume.unit("imperialGill", 0.1420653125, ["gill", "gills"]);
26
- exports.imperialFluidOunce = exports.volume.unit("imperialFluidOunce", 0.0284130625, [
27
- "floz",
28
- "fluidOunce",
29
- "fluidOunces",
30
- ]);
13
+ exports.usGallon = exports.volume.unit("usGallon", 3.785411784, {
14
+ symbol: "gal",
15
+ plural: "gallons",
16
+ aliases: ["gallon"],
17
+ });
18
+ exports.usQuart = exports.volume.unit("usQuart", 0.946352946, {
19
+ symbol: "qt",
20
+ plural: "quarts",
21
+ aliases: ["quart"],
22
+ });
23
+ exports.usPint = exports.volume.unit("usPint", 0.473176473, {
24
+ symbol: "pt",
25
+ plural: "pints",
26
+ aliases: ["pint"],
27
+ });
28
+ exports.usGill = exports.volume.unit("usGill", 0.11829411825, { plural: "gills", aliases: ["gill"] });
29
+ exports.usFluidOunce = exports.volume.unit("usFluidOunce", 0.0295735295625, {
30
+ symbol: "floz",
31
+ plural: "fluidOunces",
32
+ aliases: ["fluidOunce"],
33
+ });
34
+ exports.imperialGallon = exports.volume.unit("imperialGallon", 4.54609, {
35
+ symbol: "gal",
36
+ plural: "gallons",
37
+ aliases: ["gallon"],
38
+ });
39
+ exports.imperialQuart = exports.volume.unit("imperialQuart", 1.1365225, {
40
+ symbol: "qt",
41
+ plural: "quarts",
42
+ aliases: ["quart"],
43
+ });
44
+ exports.imperialPint = exports.volume.unit("imperialPint", 0.56826125, {
45
+ symbol: "pt",
46
+ plural: "pints",
47
+ aliases: ["pint"],
48
+ });
49
+ exports.imperialGill = exports.volume.unit("imperialGill", 0.1420653125, {
50
+ plural: "gills",
51
+ aliases: ["gill"],
52
+ });
53
+ exports.imperialFluidOunce = exports.volume.unit("imperialFluidOunce", 0.0284130625, {
54
+ symbol: "floz",
55
+ plural: "fluidOunces",
56
+ aliases: ["fluidOunce"],
57
+ });
31
58
  // US-only cooking measures (no competing imperial unit, so left unprefixed).
32
- exports.cup = exports.volume.unit("cup", 0.2365882365, ["cups"]);
33
- exports.tablespoon = exports.volume.unit("tablespoon", 0.01478676478125, ["tbsp", "tablespoons"]);
34
- exports.teaspoon = exports.volume.unit("teaspoon", 0.00492892159375, ["tsp", "teaspoons"]);
59
+ exports.cup = exports.volume.unit("cup", 0.2365882365, { plural: "cups" });
60
+ exports.tablespoon = exports.volume.unit("tablespoon", 0.01478676478125, {
61
+ symbol: "tbsp",
62
+ plural: "tablespoons",
63
+ });
64
+ exports.teaspoon = exports.volume.unit("teaspoon", 0.00492892159375, {
65
+ symbol: "tsp",
66
+ plural: "teaspoons",
67
+ });
35
68
  /** Every SI-prefixed liter (milliliter, centiliter, kiloliter, …), keyed by name. */
36
- exports.metricVolume = (0, definePrefixed_1.definePrefixed)(exports.volume, { name: "liter", symbol: "L" });
69
+ exports.metricVolume = (0, definePrefixed_1.definePrefixed)(exports.volume, exports.liter);
37
70
  exports.kiloliter = exports.metricVolume.kiloliter, exports.hectoliter = exports.metricVolume.hectoliter, exports.decaliter = exports.metricVolume.decaliter, exports.deciliter = exports.metricVolume.deciliter, exports.centiliter = exports.metricVolume.centiliter, exports.milliliter = exports.metricVolume.milliliter;
@@ -4,8 +4,7 @@ exports.AmbiguousUnitError = void 0;
4
4
  class AmbiguousUnitError extends Error {
5
5
  constructor(token, candidates) {
6
6
  const names = candidates.map((unit) => unit.name).join(", ");
7
- super(`Ambiguous unit "${token}": matches ${names}. ` +
8
- `Pass a preferred measurement system to disambiguate.`);
7
+ super(`Ambiguous unit "${token}": matches ${names}. Pass a preferred measurement system to disambiguate.`);
9
8
  }
10
9
  }
11
10
  exports.AmbiguousUnitError = AmbiguousUnitError;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Thrown when a caller passes an invalid argument — an empty required list, a
3
+ * value outside an allowed range, a wrong numeric type, and so on. Signals a
4
+ * mistake in the calling code rather than a recoverable runtime condition.
5
+ */
6
+ export declare class ArgumentError extends Error {
7
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ArgumentError = void 0;
4
+ /**
5
+ * Thrown when a caller passes an invalid argument — an empty required list, a
6
+ * value outside an allowed range, a wrong numeric type, and so on. Signals a
7
+ * mistake in the calling code rather than a recoverable runtime condition.
8
+ */
9
+ class ArgumentError extends Error {
10
+ }
11
+ exports.ArgumentError = ArgumentError;
@@ -0,0 +1,13 @@
1
+ import type { Unit } from "../lib/Unit";
2
+ /**
3
+ * Thrown when an operation is attempted on two units from different dimensions —
4
+ * converting, comparing, or combining them (e.g. `meter` and `liter`). Such
5
+ * units are dimensionally incompatible, so the operation has no meaning.
6
+ */
7
+ export declare class DimensionMismatchError extends Error {
8
+ constructor(from: Unit, to: Unit);
9
+ }
10
+ /** @deprecated Renamed to {@link DimensionMismatchError}. */
11
+ export declare const InvalidConversionError: typeof DimensionMismatchError;
12
+ /** @deprecated Renamed to {@link DimensionMismatchError}. */
13
+ export type InvalidConversionError = DimensionMismatchError;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InvalidConversionError = exports.DimensionMismatchError = void 0;
4
+ /**
5
+ * Thrown when an operation is attempted on two units from different dimensions —
6
+ * converting, comparing, or combining them (e.g. `meter` and `liter`). Such
7
+ * units are dimensionally incompatible, so the operation has no meaning.
8
+ */
9
+ class DimensionMismatchError extends Error {
10
+ constructor(from, to) {
11
+ super(`Invalid conversion: ${from.name} to ${to.name}`);
12
+ }
13
+ }
14
+ exports.DimensionMismatchError = DimensionMismatchError;
15
+ /** @deprecated Renamed to {@link DimensionMismatchError}. */
16
+ exports.InvalidConversionError = DimensionMismatchError;
@@ -0,0 +1,5 @@
1
+ import type { Dimension } from "../lib/Dimension";
2
+ /** Thrown when defining a unit whose name already exists in its dimension. */
3
+ export declare class DuplicateUnitError extends Error {
4
+ constructor(name: string, dimension: Dimension);
5
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DuplicateUnitError = void 0;
4
+ /** Thrown when defining a unit whose name already exists in its dimension. */
5
+ class DuplicateUnitError extends Error {
6
+ constructor(name, dimension) {
7
+ super(`Duplicate unit name "${name}" in dimension "${dimension.name}"`);
8
+ }
9
+ }
10
+ exports.DuplicateUnitError = DuplicateUnitError;
@@ -0,0 +1,4 @@
1
+ /** Thrown when a string can't be parsed into a quantity (no magnitude/unit found). */
2
+ export declare class ParseError extends Error {
3
+ constructor(input: string);
4
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParseError = void 0;
4
+ /** Thrown when a string can't be parsed into a quantity (no magnitude/unit found). */
5
+ class ParseError extends Error {
6
+ constructor(input) {
7
+ super(`Could not parse a quantity from "${input}"`);
8
+ }
9
+ }
10
+ exports.ParseError = ParseError;
@@ -0,0 +1,10 @@
1
+ import type { Dimension } from "../lib/Dimension";
2
+ import type { MeasurementSystem } from "../lib/MeasurementSystem";
3
+ /**
4
+ * Thrown when a measurement system is asked to express a quantity in a dimension
5
+ * for which it has no units (e.g. an imperial-only system and a metric-only
6
+ * dimension).
7
+ */
8
+ export declare class UnsupportedDimensionError extends Error {
9
+ constructor(system: MeasurementSystem, dimension: Dimension);
10
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnsupportedDimensionError = void 0;
4
+ /**
5
+ * Thrown when a measurement system is asked to express a quantity in a dimension
6
+ * for which it has no units (e.g. an imperial-only system and a metric-only
7
+ * dimension).
8
+ */
9
+ class UnsupportedDimensionError extends Error {
10
+ constructor(system, dimension) {
11
+ super(`Measurement system "${system.name}" has no "${dimension.name}" units to express in`);
12
+ }
13
+ }
14
+ exports.UnsupportedDimensionError = UnsupportedDimensionError;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  export * from "./errors/AmbiguousUnitError";
2
- export * from "./errors/InvalidConversionError";
2
+ export * from "./errors/ArgumentError";
3
+ export * from "./errors/DimensionMismatchError";
4
+ export * from "./errors/DuplicateUnitError";
5
+ export * from "./errors/ParseError";
3
6
  export * from "./errors/UnknownUnitError";
7
+ export * from "./errors/UnsupportedDimensionError";
4
8
  export * from "./lib/Dimension";
5
9
  export * from "./lib/MeasurementSystem";
6
10
  export * from "./lib/Quantity";
package/dist/index.js CHANGED
@@ -15,8 +15,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./errors/AmbiguousUnitError"), exports);
18
- __exportStar(require("./errors/InvalidConversionError"), exports);
18
+ __exportStar(require("./errors/ArgumentError"), exports);
19
+ __exportStar(require("./errors/DimensionMismatchError"), exports);
20
+ __exportStar(require("./errors/DuplicateUnitError"), exports);
21
+ __exportStar(require("./errors/ParseError"), exports);
19
22
  __exportStar(require("./errors/UnknownUnitError"), exports);
23
+ __exportStar(require("./errors/UnsupportedDimensionError"), exports);
20
24
  __exportStar(require("./lib/Dimension"), exports);
21
25
  __exportStar(require("./lib/MeasurementSystem"), exports);
22
26
  __exportStar(require("./lib/Quantity"), exports);
@@ -1,4 +1,4 @@
1
- import { Rational } from "../lib/Rational";
1
+ import { Rational } from "./Rational";
2
2
  import { Unit } from "./Unit";
3
3
  /** A linear unit with an additive offset (e.g. temperature scales). */
4
4
  export interface AffineSpec {
@@ -16,6 +16,15 @@ export interface CustomSpec {
16
16
  toBase: (value: number) => number;
17
17
  fromBase: (value: number) => number;
18
18
  }
19
+ /** Optional descriptors for a unit: its symbol, plural, and extra parse aliases. */
20
+ export interface UnitDef {
21
+ /** Canonical symbol, e.g. `"g"`, `"km"`, `"°C"`. Also registered for parsing. */
22
+ symbol?: string;
23
+ /** Plural name, e.g. `"grams"`. Also registered for parsing. */
24
+ plural?: string;
25
+ /** Additional names this unit parses from (beyond name, symbol, and plural). */
26
+ aliases?: string[];
27
+ }
19
28
  /**
20
29
  * A dimension is a single *kind* of measurable quantity (length, volume, mass,
21
30
  * temperature, …). It owns one canonical **base unit** that every other unit in
@@ -48,15 +57,15 @@ export declare class Dimension {
48
57
  baseUnit?: Unit;
49
58
  constructor(name: string);
50
59
  /** Define the canonical base unit (identity transform). */
51
- base(name: string, aliases?: string[]): Unit;
60
+ base(name: string, def?: UnitDef): Unit;
52
61
  /**
53
62
  * Define a linear unit. `scale` is how many base units make up one of this
54
63
  * unit (e.g. a kilometer is `1000` meters). Pass a {@link Rational} for a
55
64
  * scale a decimal cannot represent exactly.
56
65
  */
57
- unit(name: string, scale: number | Rational, aliases?: string[]): Unit;
66
+ unit(name: string, scale: number | Rational, def?: UnitDef): Unit;
58
67
  /** Define an affine unit (scale plus additive offset, e.g. °C against K). */
59
- affine(name: string, { scale, offset }: AffineSpec, aliases?: string[]): Unit;
68
+ affine(name: string, { scale, offset }: AffineSpec, def?: UnitDef): Unit;
60
69
  /**
61
70
  * Define a non-linear unit from an arbitrary, hand-written inverse transform
62
71
  * pair. Reserve this for units that genuinely cannot be expressed as `value *
@@ -64,7 +73,7 @@ export declare class Dimension {
64
73
  * affine units should use {@link unit} / {@link affine} so conversions stay
65
74
  * exact.
66
75
  */
67
- custom(name: string, { toBase, fromBase }: CustomSpec, aliases?: string[]): Unit;
76
+ custom(name: string, { toBase, fromBase }: CustomSpec, def?: UnitDef): Unit;
68
77
  /** Convert a `number` value between two units of this dimension. */
69
78
  convert(value: number, from: Unit, to: Unit): number;
70
79
  /**
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Dimension = void 0;
4
- const InvalidConversionError_1 = require("../errors/InvalidConversionError");
5
- const Rational_1 = require("../lib/Rational");
4
+ const DimensionMismatchError_1 = require("../errors/DimensionMismatchError");
5
+ const DuplicateUnitError_1 = require("../errors/DuplicateUnitError");
6
+ const Rational_1 = require("./Rational");
6
7
  const Unit_1 = require("./Unit");
7
8
  /** The rational `0`. */
8
9
  const zero = new Rational_1.Rational(0n);
@@ -39,8 +40,8 @@ class Dimension {
39
40
  this.index = new Map();
40
41
  }
41
42
  /** Define the canonical base unit (identity transform). */
42
- base(name, aliases = []) {
43
- const unit = this.defineLinear(name, { scale: one, offset: zero }, aliases);
43
+ base(name, def = {}) {
44
+ const unit = this.defineLinear(name, { scale: one, offset: zero }, def);
44
45
  this.baseUnit = unit;
45
46
  return unit;
46
47
  }
@@ -49,12 +50,12 @@ class Dimension {
49
50
  * unit (e.g. a kilometer is `1000` meters). Pass a {@link Rational} for a
50
51
  * scale a decimal cannot represent exactly.
51
52
  */
52
- unit(name, scale, aliases = []) {
53
- return this.defineLinear(name, { scale: Rational_1.Rational.from(scale), offset: zero }, aliases);
53
+ unit(name, scale, def = {}) {
54
+ return this.defineLinear(name, { scale: Rational_1.Rational.from(scale), offset: zero }, def);
54
55
  }
55
56
  /** Define an affine unit (scale plus additive offset, e.g. °C against K). */
56
- affine(name, { scale, offset }, aliases = []) {
57
- return this.defineLinear(name, { scale: Rational_1.Rational.from(scale), offset: Rational_1.Rational.from(offset) }, aliases);
57
+ affine(name, { scale, offset }, def = {}) {
58
+ return this.defineLinear(name, { scale: Rational_1.Rational.from(scale), offset: Rational_1.Rational.from(offset) }, def);
58
59
  }
59
60
  /**
60
61
  * Define a non-linear unit from an arbitrary, hand-written inverse transform
@@ -63,8 +64,8 @@ class Dimension {
63
64
  * affine units should use {@link unit} / {@link affine} so conversions stay
64
65
  * exact.
65
66
  */
66
- custom(name, { toBase, fromBase }, aliases = []) {
67
- return this.define(name, aliases, { toBase, fromBase });
67
+ custom(name, { toBase, fromBase }, def = {}) {
68
+ return this.define(name, def, { toBase, fromBase });
68
69
  }
69
70
  /** Convert a `number` value between two units of this dimension. */
70
71
  convert(value, from, to) {
@@ -79,7 +80,7 @@ class Dimension {
79
80
  */
80
81
  convertRational(value, from, to) {
81
82
  if (!this.units.has(from) || !this.units.has(to)) {
82
- throw new InvalidConversionError_1.InvalidConversionError(from, to);
83
+ throw new DimensionMismatchError_1.DimensionMismatchError(from, to);
83
84
  }
84
85
  if (from === to) {
85
86
  return value;
@@ -99,20 +100,22 @@ class Dimension {
99
100
  return this.units.has(unit);
100
101
  }
101
102
  /** Define a linear / affine unit from its exact rational transform. */
102
- defineLinear(name, linear, aliases) {
103
- return this.define(name, aliases, { linear });
103
+ defineLinear(name, linear, def) {
104
+ return this.define(name, def, { linear });
104
105
  }
105
- define(name, aliases, transform) {
106
+ define(name, { symbol, plural, aliases = [] }, transform) {
106
107
  for (const existing of this.units) {
107
108
  if (existing.name === name) {
108
- throw new Error(`Duplicate unit name "${name}" in dimension "${this.name}"`);
109
+ throw new DuplicateUnitError_1.DuplicateUnitError(name, this);
109
110
  }
110
111
  }
111
- const unit = new Unit_1.Unit({ name, dimension: this, ...transform });
112
+ const unit = new Unit_1.Unit({ name, dimension: this, symbol, plural, ...transform });
112
113
  this.units.add(unit);
113
- this.register(name, unit);
114
- for (const alias of aliases) {
115
- this.register(alias, unit);
114
+ // Register every label this unit can be parsed from, de-duplicated so a unit
115
+ // never appears twice among a token's candidates (e.g. if symbol === name).
116
+ const tokens = new Set([name, symbol, plural, ...aliases].filter((t) => !!t));
117
+ for (const token of tokens) {
118
+ this.register(token, unit);
116
119
  }
117
120
  return unit;
118
121
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MeasurementSystem = void 0;
4
- const scaleOf_1 = require("../utils/scaleOf");
4
+ const UnsupportedDimensionError_1 = require("../errors/UnsupportedDimensionError");
5
5
  /**
6
6
  * A measurement system (metric, imperial, US customary, …) is a cross-dimension
7
7
  * collection of units that share a real-world standard.
@@ -37,22 +37,11 @@ class MeasurementSystem {
37
37
  * to the smallest unit when even that rounds below 1).
38
38
  */
39
39
  express(quantity) {
40
- const candidates = this.in(quantity.unit.dimension).sort((a, b) => (0, scaleOf_1.scaleOf)(a) - (0, scaleOf_1.scaleOf)(b));
40
+ const candidates = this.in(quantity.unit.dimension);
41
41
  if (candidates.length === 0) {
42
- throw new Error(`Measurement system "${this.name}" has no ` +
43
- `"${quantity.unit.dimension.name}" units to express in`);
42
+ throw new UnsupportedDimensionError_1.UnsupportedDimensionError(this, quantity.unit.dimension);
44
43
  }
45
- const base = quantity.unit.toBase(quantity.magnitude);
46
- let chosen = candidates[0];
47
- for (const unit of candidates) {
48
- if (Math.abs(unit.fromBase(base)) >= 1) {
49
- chosen = unit;
50
- }
51
- else {
52
- break;
53
- }
54
- }
55
- return quantity.to(chosen);
44
+ return quantity.best(...candidates);
56
45
  }
57
46
  }
58
47
  exports.MeasurementSystem = MeasurementSystem;