@fulcro/types 0.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.
- package/LICENSE +15 -0
- package/README.md +66 -0
- package/dist/bigInteger/index.d.ts +27 -0
- package/dist/bigInteger/index.js +76 -0
- package/dist/brand/index.d.ts +25 -0
- package/dist/brand/index.js +2 -0
- package/dist/decimal/arithmetic.d.ts +99 -0
- package/dist/decimal/arithmetic.js +388 -0
- package/dist/decimal/format.d.ts +55 -0
- package/dist/decimal/format.js +195 -0
- package/dist/decimal/index.d.ts +296 -0
- package/dist/decimal/index.js +407 -0
- package/dist/decimal/parse.d.ts +15 -0
- package/dist/decimal/parse.js +80 -0
- package/dist/decimal/parts.d.ts +83 -0
- package/dist/decimal/parts.js +103 -0
- package/dist/decimal/round.d.ts +32 -0
- package/dist/decimal/round.js +132 -0
- package/dist/doublePrecisionFloat/index.d.ts +19 -0
- package/dist/doublePrecisionFloat/index.js +12 -0
- package/dist/float/index.d.ts +25 -0
- package/dist/float/index.js +61 -0
- package/dist/halfPrecisionFloat/index.d.ts +20 -0
- package/dist/halfPrecisionFloat/index.js +63 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +29 -0
- package/dist/integer/index.d.ts +163 -0
- package/dist/integer/index.js +402 -0
- package/dist/languageService/index.d.ts +21 -0
- package/dist/languageService/index.js +23 -0
- package/dist/layout/index.d.ts +33 -0
- package/dist/layout/index.js +2 -0
- package/dist/numericType/index.d.ts +174 -0
- package/dist/numericType/index.js +2 -0
- package/dist/roundingMode/index.d.ts +30 -0
- package/dist/roundingMode/index.js +29 -0
- package/dist/signedInteger/index.d.ts +40 -0
- package/dist/signedInteger/index.js +34 -0
- package/dist/singlePrecisionFloat/index.d.ts +20 -0
- package/dist/singlePrecisionFloat/index.js +15 -0
- package/dist/transformer/classify/index.d.ts +39 -0
- package/dist/transformer/classify/index.js +84 -0
- package/dist/transformer/index.d.ts +27 -0
- package/dist/transformer/index.js +30 -0
- package/dist/transformer/rewriter/index.d.ts +3 -0
- package/dist/transformer/rewriter/index.js +374 -0
- package/dist/unplugin/index.d.mts +15 -0
- package/dist/unplugin/index.mjs +31 -0
- package/dist/unsignedInteger/index.d.ts +36 -0
- package/dist/unsignedInteger/index.js +34 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 diguu <rodrigogeribola@hotmail.com>
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @fulcro/types
|
|
2
|
+
|
|
3
|
+
Numeric types with a declared range and layout: fixed-width integers, half,
|
|
4
|
+
single and double precision floats, an integer of any size, and a `Decimal` with
|
|
5
|
+
the semantics of IEEE 754 decimal128 — with the JavaScript operators working on
|
|
6
|
+
every one of them once the plugin is wired up.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install @fulcro/types
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { Decimal, SignedInteger, UnsignedInteger } from '@fulcro/types';
|
|
14
|
+
|
|
15
|
+
const Int32 = SignedInteger(32);
|
|
16
|
+
|
|
17
|
+
Int32.add(Int32.from(2), Int32.from(3)); // 5
|
|
18
|
+
Int32.add(Int32.maximum, Int32.from(1)); // RangeError: outside [-2147483648, 2147483647]
|
|
19
|
+
UnsignedInteger(8).wrap(-1); // 255, when modular arithmetic is what you mean
|
|
20
|
+
|
|
21
|
+
Decimal.from('0.1').add(Decimal.from('0.2')).toString(); // '0.3'
|
|
22
|
+
Decimal.from('19.99').multiply(Decimal.from(3)).toString(); // '59.97'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
| Type | What it is |
|
|
26
|
+
| -------------------------------------------- | -------------------------------------------------- |
|
|
27
|
+
| `SignedInteger<N>`, `UnsignedInteger<N>` | 8 to 128 bits, checked arithmetic, `wrap` |
|
|
28
|
+
| `HalfPrecisionFloat`, `SinglePrecisionFloat` | binary16 and binary32, each operation rounded once |
|
|
29
|
+
| `DoublePrecisionFloat` | binary64, the `number` named as a choice |
|
|
30
|
+
| `BigInteger` | an integer of any size |
|
|
31
|
+
| `Decimal` | 34 decimal digits, five rounding modes |
|
|
32
|
+
|
|
33
|
+
Each type has a value of the same name that converts, recognises and computes,
|
|
34
|
+
and every type but `BigInteger` reports its `minimum` and `maximum`. If you only
|
|
35
|
+
need the types, `import type` them and no code is loaded.
|
|
36
|
+
|
|
37
|
+
## Operators
|
|
38
|
+
|
|
39
|
+
With the plugin, the operators mean what the type means — checked on an
|
|
40
|
+
integer, rounded on a float, decimal128 on a `Decimal` — and the result keeps
|
|
41
|
+
its type. Both operands have to be the same type, or it is a type error at the
|
|
42
|
+
line:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const total = price * Decimal.from(3); // Decimal
|
|
46
|
+
count++; // SignedInteger<32>.increment, checked
|
|
47
|
+
a + 1; // type error: 'number' is not 'SignedInteger<32>'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Tool | Wire in |
|
|
51
|
+
| ---------- | ---------------------------------------------------------------------------------------- |
|
|
52
|
+
| `tsc` | `{ "transform": "@fulcro/types/transformer", "transformProgram": true }`, via `ts-patch` |
|
|
53
|
+
| A bundler | `vite` (or `rollup`, `webpack`, `esbuild`, …) from `@fulcro/types/unplugin` |
|
|
54
|
+
| The editor | `{ "name": "@fulcro/types/language-service" }` in the tsconfig `plugins` |
|
|
55
|
+
|
|
56
|
+
Without it, the operators are the language's own: unchecked on the
|
|
57
|
+
`number`-backed types, refused on a `Decimal`.
|
|
58
|
+
|
|
59
|
+
Every type but `BigInteger` declares a size and alignment, which
|
|
60
|
+
`sizeOf<T>()` and `alignOf<T>()` from `@fulcro/reflect` read at compile time.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
**Full guide:** [docs/types.md](../../docs/types.md) — ranges, rounding, the
|
|
65
|
+
operators, the special values and the layout table.
|
|
66
|
+
🇧🇷 [Leia em português](../../docs/pt-BR/types.md).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Branded } from '../brand/index.js';
|
|
2
|
+
import type { NumericType } from '../numericType/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* An integer of any size.
|
|
5
|
+
*
|
|
6
|
+
* Carried by a `bigint`, which is exact at every magnitude, and branded like
|
|
7
|
+
* every other numeric type here: a `BigInteger` is a `bigint` that went through
|
|
8
|
+
* {@link BigInteger.from}, not any `bigint` at all. That is what lets the
|
|
9
|
+
* operators be rewritten for this type and left alone on every other `bigint`
|
|
10
|
+
* of a program.
|
|
11
|
+
*
|
|
12
|
+
* It is also the one numeric type here with no fixed layout — its size is the
|
|
13
|
+
* size of its value — which is why `sizeOf<BigInteger>()` is a type error.
|
|
14
|
+
*/
|
|
15
|
+
export type BigInteger = Branded<bigint, 'BigInteger'>;
|
|
16
|
+
/**
|
|
17
|
+
* The descriptor of {@link BigInteger}.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* BigInteger.from('123456789012345678901234567890'); // 123456789012345678901234567890n
|
|
21
|
+
* BigInteger.from(1.5); // RangeError: expected an integer
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Nothing overflows, so the arithmetic throws only on a zero divisor and on a
|
|
25
|
+
* negative exponent.
|
|
26
|
+
*/
|
|
27
|
+
export declare const BigInteger: NumericType<BigInteger, number | bigint | string>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BigInteger = void 0;
|
|
4
|
+
/** Integer literal accepted by {@link BigInteger.from}: decimal digits only. */
|
|
5
|
+
const INTEGER_LITERAL = /^[+-]?\d+$/;
|
|
6
|
+
/**
|
|
7
|
+
* Refuses a zero divisor with the operation named.
|
|
8
|
+
*
|
|
9
|
+
* @param operation Operation being performed.
|
|
10
|
+
* @param divisor Divisor it was handed.
|
|
11
|
+
*/
|
|
12
|
+
const requireDivisor = (operation, divisor) => {
|
|
13
|
+
if (divisor === 0n) {
|
|
14
|
+
throw new RangeError(`BigInteger.${operation}: division by zero.`);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The descriptor of {@link BigInteger}.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* BigInteger.from('123456789012345678901234567890'); // 123456789012345678901234567890n
|
|
22
|
+
* BigInteger.from(1.5); // RangeError: expected an integer
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Nothing overflows, so the arithmetic throws only on a zero divisor and on a
|
|
26
|
+
* negative exponent.
|
|
27
|
+
*/
|
|
28
|
+
exports.BigInteger = {
|
|
29
|
+
name: 'BigInteger',
|
|
30
|
+
from: (value) => {
|
|
31
|
+
if (typeof value === 'bigint')
|
|
32
|
+
return value;
|
|
33
|
+
if (typeof value === 'number') {
|
|
34
|
+
if (!Number.isInteger(value)) {
|
|
35
|
+
throw new RangeError(`BigInteger.from: expected an integer, received ${value}.`);
|
|
36
|
+
}
|
|
37
|
+
return BigInt(value);
|
|
38
|
+
}
|
|
39
|
+
if (typeof value === 'string') {
|
|
40
|
+
// `BigInt` alone would also take `0x1f`, `0b101` and surrounding
|
|
41
|
+
// whitespace. A decimal type accepting a hexadecimal string is a
|
|
42
|
+
// surprise nobody reading `from('0x10')` expects to be 16.
|
|
43
|
+
if (!INTEGER_LITERAL.test(value)) {
|
|
44
|
+
throw new SyntaxError(`BigInteger.from: expected decimal digits with an optional sign, received ${JSON.stringify(value)}.`);
|
|
45
|
+
}
|
|
46
|
+
return BigInt(value);
|
|
47
|
+
}
|
|
48
|
+
throw new TypeError(`BigInteger.from: expected a number, a bigint or a string, received ${typeof value}.`);
|
|
49
|
+
},
|
|
50
|
+
is: (value) => typeof value === 'bigint',
|
|
51
|
+
add: (left, right) => (left + right),
|
|
52
|
+
subtract: (left, right) => (left - right),
|
|
53
|
+
multiply: (left, right) => (left * right),
|
|
54
|
+
divide: (left, right) => {
|
|
55
|
+
requireDivisor('divide', right);
|
|
56
|
+
return (left / right);
|
|
57
|
+
},
|
|
58
|
+
remainder: (left, right) => {
|
|
59
|
+
requireDivisor('remainder', right);
|
|
60
|
+
return (left % right);
|
|
61
|
+
},
|
|
62
|
+
power: (base, exponent) => {
|
|
63
|
+
if (exponent < 0n) {
|
|
64
|
+
throw new RangeError(`BigInteger.power: expected an exponent of zero or more, received ${exponent}n.`);
|
|
65
|
+
}
|
|
66
|
+
return (base ** exponent);
|
|
67
|
+
},
|
|
68
|
+
negate: (value) => -value,
|
|
69
|
+
increment: (value) => (value + 1n),
|
|
70
|
+
decrement: (value) => (value - 1n),
|
|
71
|
+
equals: (left, right) => left === right,
|
|
72
|
+
lessThan: (left, right) => left < right,
|
|
73
|
+
lessThanOrEqual: (left, right) => left <= right,
|
|
74
|
+
greaterThan: (left, right) => left > right,
|
|
75
|
+
greaterThanOrEqual: (left, right) => left >= right,
|
|
76
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Key of the brand, declared and never defined.
|
|
3
|
+
*
|
|
4
|
+
* A `unique symbol` rather than a string so that no consumer can spell the
|
|
5
|
+
* brand by hand: the only way to hold a `SignedInteger<32>` is to be given one
|
|
6
|
+
* by the function that checked it.
|
|
7
|
+
*/
|
|
8
|
+
declare const brand: unique symbol;
|
|
9
|
+
/**
|
|
10
|
+
* A primitive tagged, at the type level only, with the name of the numeric type
|
|
11
|
+
* it was checked against.
|
|
12
|
+
*
|
|
13
|
+
* Nothing of this exists at runtime — a branded `number` is a `number` — which is
|
|
14
|
+
* what lets a consumer take only the types of this package and pay for no code.
|
|
15
|
+
* The price is that the brand does not survive arithmetic written with the
|
|
16
|
+
* operators: `a + b` is a plain `number` again, and has to go back through the
|
|
17
|
+
* type's own `add` to be one of ours.
|
|
18
|
+
*
|
|
19
|
+
* @template TBase Primitive that carries the value.
|
|
20
|
+
* @template TName Name of the numeric type the value was checked against.
|
|
21
|
+
*/
|
|
22
|
+
export type Branded<TBase, TName extends string> = TBase & {
|
|
23
|
+
readonly [brand]: TName;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { RoundingMode } from '../roundingMode/index.js';
|
|
2
|
+
import { type DecimalParts } from './parts';
|
|
3
|
+
/**
|
|
4
|
+
* The operations of `Decimal`, on parts.
|
|
5
|
+
*
|
|
6
|
+
* Each one computes an exact result on integers and hands it to `finish`, which
|
|
7
|
+
* rounds it into the format once. The care is in keeping "exact" affordable: a
|
|
8
|
+
* decimal128 exponent spans more than twelve thousand powers of ten, and an
|
|
9
|
+
* operation that aligned two operands by multiplying one of them out would
|
|
10
|
+
* build a twelve-thousand-digit integer to add 1 to 10^6000. None of the
|
|
11
|
+
* operations below lets an intermediate grow past about a hundred digits,
|
|
12
|
+
* whatever the exponents, and the performance suite holds them to it.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Adds two values.
|
|
16
|
+
*
|
|
17
|
+
* @param left First operand.
|
|
18
|
+
* @param right Second operand.
|
|
19
|
+
* @param mode Rounding mode.
|
|
20
|
+
* @returns The rounded sum.
|
|
21
|
+
*/
|
|
22
|
+
export declare const addParts: (left: DecimalParts, right: DecimalParts, mode: RoundingMode) => DecimalParts;
|
|
23
|
+
/**
|
|
24
|
+
* Multiplies two values.
|
|
25
|
+
*
|
|
26
|
+
* The product of two coefficients has at most sixty-eight digits, and the
|
|
27
|
+
* exponents simply add, so nothing here needs guarding.
|
|
28
|
+
*
|
|
29
|
+
* @param left First operand.
|
|
30
|
+
* @param right Second operand.
|
|
31
|
+
* @param mode Rounding mode.
|
|
32
|
+
* @returns The rounded product.
|
|
33
|
+
*/
|
|
34
|
+
export declare const multiplyParts: (left: DecimalParts, right: DecimalParts, mode: RoundingMode) => DecimalParts;
|
|
35
|
+
/**
|
|
36
|
+
* Divides one value by another.
|
|
37
|
+
*
|
|
38
|
+
* The dividend is scaled so that the quotient has thirty-five digits — one
|
|
39
|
+
* more than the format keeps — and a non-zero remainder is recorded as a
|
|
40
|
+
* thirty-sixth digit of 1. Those two digits are all the rounding reads, so the
|
|
41
|
+
* quotient is rounded exactly as the infinitely precise one would be.
|
|
42
|
+
*
|
|
43
|
+
* Division by zero follows IEEE 754 rather than throwing: a non-zero value over
|
|
44
|
+
* zero is an infinity, and zero over zero is not a number.
|
|
45
|
+
*
|
|
46
|
+
* @param left Dividend.
|
|
47
|
+
* @param right Divisor.
|
|
48
|
+
* @param mode Rounding mode.
|
|
49
|
+
* @returns The rounded quotient.
|
|
50
|
+
*/
|
|
51
|
+
export declare const divideParts: (left: DecimalParts, right: DecimalParts, mode: RoundingMode) => DecimalParts;
|
|
52
|
+
/**
|
|
53
|
+
* The remainder of a division truncated towards zero, carrying the sign of the
|
|
54
|
+
* dividend — what `%` computes on numbers.
|
|
55
|
+
*
|
|
56
|
+
* Always exact: the remainder is smaller than the divisor and no finer than the
|
|
57
|
+
* finer of the two operands, so it always fits the format.
|
|
58
|
+
*
|
|
59
|
+
* @param left Dividend.
|
|
60
|
+
* @param right Divisor.
|
|
61
|
+
* @returns The remainder.
|
|
62
|
+
*/
|
|
63
|
+
export declare const remainderParts: (left: DecimalParts, right: DecimalParts) => DecimalParts;
|
|
64
|
+
/**
|
|
65
|
+
* Compares two values in magnitude and sign.
|
|
66
|
+
*
|
|
67
|
+
* @param left First operand.
|
|
68
|
+
* @param right Second operand.
|
|
69
|
+
* @returns -1, 0 or 1, or `undefined` when either is not a number, which is
|
|
70
|
+
* ordered against nothing.
|
|
71
|
+
*/
|
|
72
|
+
export declare const compareParts: (left: DecimalParts, right: DecimalParts) => -1 | 0 | 1 | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Raises a value to an integer power, with the special cases of IEEE 754's
|
|
75
|
+
* `pown`: anything to the power zero is one, `NaN` included; a zero or an
|
|
76
|
+
* infinity to a negative power swaps with the other; and the sign is negative
|
|
77
|
+
* only for a negative base and an odd power.
|
|
78
|
+
*
|
|
79
|
+
* The finite case is computed exactly and rounded once, so the result is the
|
|
80
|
+
* correctly rounded one — up to {@link EXACT_DIGIT_LIMIT} digits of exact
|
|
81
|
+
* power, past which it carries {@link GUARD_DIGITS} guard digits instead. A
|
|
82
|
+
* result that plainly overflows or underflows is settled from its logarithm,
|
|
83
|
+
* without building a number only to discard it.
|
|
84
|
+
*
|
|
85
|
+
* @param base Value raised.
|
|
86
|
+
* @param power Integer exponent.
|
|
87
|
+
* @param mode Rounding mode.
|
|
88
|
+
* @returns The rounded power.
|
|
89
|
+
*/
|
|
90
|
+
export declare const powerParts: (base: DecimalParts, power: bigint, mode: RoundingMode) => DecimalParts;
|
|
91
|
+
/**
|
|
92
|
+
* Rounds a value to a number of places after the point.
|
|
93
|
+
*
|
|
94
|
+
* @param parts Value to round.
|
|
95
|
+
* @param places Places after the point; negative to round to tens, hundreds…
|
|
96
|
+
* @param mode Rounding mode.
|
|
97
|
+
* @returns The rounded value.
|
|
98
|
+
*/
|
|
99
|
+
export declare const roundParts: (parts: DecimalParts, places: number, mode: RoundingMode) => DecimalParts;
|