@fulcro/types 0.1.0 → 0.2.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 -15
- package/README.md +86 -66
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/struct/arithmetic.d.ts +131 -0
- package/dist/struct/arithmetic.js +20 -0
- package/dist/struct/codec.d.ts +58 -0
- package/dist/struct/codec.js +325 -0
- package/dist/struct/index.d.ts +174 -0
- package/dist/struct/index.js +198 -0
- package/package.json +71 -71
package/LICENSE
CHANGED
|
@@ -1,15 +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.
|
|
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
CHANGED
|
@@ -1,66 +1,86 @@
|
|
|
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
|
-
|
|
65
|
-
|
|
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
|
+
## Structs
|
|
63
|
+
|
|
64
|
+
Value types with a fixed layout, built from the types above and from each
|
|
65
|
+
other — frozen, compared by field, and stored in a `DataView` without an object
|
|
66
|
+
per value:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { SinglePrecisionFloat, struct, type Struct } from '@fulcro/types';
|
|
70
|
+
|
|
71
|
+
const Vector3 = struct('Vector3', {
|
|
72
|
+
x: SinglePrecisionFloat,
|
|
73
|
+
y: SinglePrecisionFloat,
|
|
74
|
+
z: SinglePrecisionFloat,
|
|
75
|
+
});
|
|
76
|
+
type Vector3 = Struct<typeof Vector3>;
|
|
77
|
+
|
|
78
|
+
Vector3.layout.size; // 12, and sizeOf<Vector3>() at compile time
|
|
79
|
+
Vector3.write(view, 0, Vector3.from({ x: 0, y: 1, z: 0 }));
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
**Full guide:** [docs/types.md](../../docs/types.md) — ranges, rounding, the
|
|
85
|
+
operators, the special values, the layout table and structs.
|
|
86
|
+
🇧🇷 [Leia em português](../../docs/pt-BR/types.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -19,4 +19,6 @@ export type { BoundedNumericType, NumericType } from './numericType/index.js';
|
|
|
19
19
|
export type { RoundingMode } from './roundingMode/index.js';
|
|
20
20
|
export { SignedInteger } from './signedInteger/index.js';
|
|
21
21
|
export { SinglePrecisionFloat } from './singlePrecisionFloat/index.js';
|
|
22
|
+
export type { Struct, StructType } from './struct/index.js';
|
|
23
|
+
export { struct } from './struct/index.js';
|
|
22
24
|
export { UnsignedInteger } from './unsignedInteger/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.UnsignedInteger = exports.SinglePrecisionFloat = exports.SignedInteger = exports.HalfPrecisionFloat = exports.DoublePrecisionFloat = exports.Decimal = exports.BigInteger = void 0;
|
|
3
|
+
exports.UnsignedInteger = exports.struct = exports.SinglePrecisionFloat = exports.SignedInteger = exports.HalfPrecisionFloat = exports.DoublePrecisionFloat = exports.Decimal = exports.BigInteger = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Public entry point of the package, matching the `main` and `types` fields of
|
|
6
6
|
* `package.json`.
|
|
@@ -25,5 +25,7 @@ var signedInteger_1 = require("./signedInteger/index.js");
|
|
|
25
25
|
Object.defineProperty(exports, "SignedInteger", { enumerable: true, get: function () { return signedInteger_1.SignedInteger; } });
|
|
26
26
|
var singlePrecisionFloat_1 = require("./singlePrecisionFloat/index.js");
|
|
27
27
|
Object.defineProperty(exports, "SinglePrecisionFloat", { enumerable: true, get: function () { return singlePrecisionFloat_1.SinglePrecisionFloat; } });
|
|
28
|
+
var struct_1 = require("./struct/index.js");
|
|
29
|
+
Object.defineProperty(exports, "struct", { enumerable: true, get: function () { return struct_1.struct; } });
|
|
28
30
|
var unsignedInteger_1 = require("./unsignedInteger/index.js");
|
|
29
31
|
Object.defineProperty(exports, "UnsignedInteger", { enumerable: true, get: function () { return unsignedInteger_1.UnsignedInteger; } });
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arithmetic on number literals, at the type level, for the layout a struct
|
|
3
|
+
* type declares.
|
|
4
|
+
*
|
|
5
|
+
* `sizeOf<T>()` answers from the literal in `T['~layout']`, so a struct type
|
|
6
|
+
* has to carry its size as a literal, and that literal is a sum of its fields
|
|
7
|
+
* rounded up to its alignment. TypeScript has no arithmetic on literals; this
|
|
8
|
+
* module is the smallest amount of it the layout needs.
|
|
9
|
+
*
|
|
10
|
+
* Addition works digit by digit on the decimal text of a number, so it is not
|
|
11
|
+
* bounded by the recursion limit the way counting a tuple up to the sum would
|
|
12
|
+
* be. Rounding needs a remainder, and the remainder by an alignment — a power
|
|
13
|
+
* of two up to sixteen — depends only on the last four digits, since sixteen
|
|
14
|
+
* divides ten thousand; so no tuple built here is ever longer than 9,999.
|
|
15
|
+
*
|
|
16
|
+
* Wherever an input is `number` rather than a literal, the answer is `number`:
|
|
17
|
+
* `sizeOf` then refuses the type rather than report a wrong size.
|
|
18
|
+
*/
|
|
19
|
+
/** A decimal digit, as text. */
|
|
20
|
+
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
|
|
21
|
+
/** The value of each digit. */
|
|
22
|
+
interface DigitValues {
|
|
23
|
+
readonly '0': 0;
|
|
24
|
+
readonly '1': 1;
|
|
25
|
+
readonly '2': 2;
|
|
26
|
+
readonly '3': 3;
|
|
27
|
+
readonly '4': 4;
|
|
28
|
+
readonly '5': 5;
|
|
29
|
+
readonly '6': 6;
|
|
30
|
+
readonly '7': 7;
|
|
31
|
+
readonly '8': 8;
|
|
32
|
+
readonly '9': 9;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A tuple of `N` elements, for the small `N` a digit or an alignment needs.
|
|
36
|
+
*
|
|
37
|
+
* @template N Length, from 0 to 19.
|
|
38
|
+
*/
|
|
39
|
+
type Units<N extends number, TAccumulated extends unknown[] = []> = TAccumulated['length'] extends N ? TAccumulated : Units<N, [...TAccumulated, unknown]>;
|
|
40
|
+
/** Ten elements, the carry out of a digit. */
|
|
41
|
+
type Ten = Units<10>;
|
|
42
|
+
/**
|
|
43
|
+
* Adds two digits and a carry.
|
|
44
|
+
*
|
|
45
|
+
* @template TLeft First digit.
|
|
46
|
+
* @template TRight Second digit.
|
|
47
|
+
* @template TCarry Carry into this digit.
|
|
48
|
+
* @returns `[digit, carry]` of the sum.
|
|
49
|
+
*/
|
|
50
|
+
type AddDigits<TLeft extends Digit, TRight extends Digit, TCarry extends 0 | 1> = [
|
|
51
|
+
...Units<DigitValues[TLeft]>,
|
|
52
|
+
...Units<DigitValues[TRight]>,
|
|
53
|
+
...Units<TCarry>
|
|
54
|
+
] extends infer TAll extends unknown[] ? TAll extends [...Ten, ...infer TRest] ? [`${TRest['length']}`, 1] : [`${TAll['length']}`, 0] : never;
|
|
55
|
+
/** First character of a text, `'0'` once it is empty. */
|
|
56
|
+
type Head<TText extends string> = TText extends `${infer THead extends Digit}${string}` ? THead : '0';
|
|
57
|
+
/** A text without its first character. */
|
|
58
|
+
type Tail<TText extends string> = TText extends `${Digit}${infer TTail}` ? TTail : '';
|
|
59
|
+
/**
|
|
60
|
+
* Adds two numbers written least significant digit first.
|
|
61
|
+
*
|
|
62
|
+
* @returns The sum, least significant digit first.
|
|
63
|
+
*/
|
|
64
|
+
type AddReversed<TLeft extends string, TRight extends string, TCarry extends 0 | 1 = 0, TSum extends string = ''> = TLeft extends '' ? TRight extends '' ? TCarry extends 1 ? `${TSum}1` : TSum : AddStep<TLeft, TRight, TCarry, TSum> : AddStep<TLeft, TRight, TCarry, TSum>;
|
|
65
|
+
/** One digit of {@link AddReversed}. */
|
|
66
|
+
type AddStep<TLeft extends string, TRight extends string, TCarry extends 0 | 1, TSum extends string> = AddDigits<Head<TLeft>, Head<TRight>, TCarry> extends [
|
|
67
|
+
infer TDigit extends string,
|
|
68
|
+
infer TNext extends 0 | 1
|
|
69
|
+
] ? AddReversed<Tail<TLeft>, Tail<TRight>, TNext, `${TSum}${TDigit}`> : never;
|
|
70
|
+
/** A text, backwards. */
|
|
71
|
+
type Reverse<TText extends string, TReversed extends string = ''> = TText extends `${infer THead}${infer TTail}` ? Reverse<TTail, `${THead}${TReversed}`> : TReversed;
|
|
72
|
+
/** The number a text of digits spells. */
|
|
73
|
+
type ToNumber<TText extends string> = TText extends `${infer N extends number}` ? N : never;
|
|
74
|
+
/**
|
|
75
|
+
* The sum of two number literals; `number` when either is not a literal.
|
|
76
|
+
*
|
|
77
|
+
* @template TLeft First operand, a non-negative integer.
|
|
78
|
+
* @template TRight Second operand, a non-negative integer.
|
|
79
|
+
*/
|
|
80
|
+
export type Add<TLeft extends number, TRight extends number> = number extends TLeft | TRight ? number : ToNumber<Reverse<AddReversed<Reverse<`${TLeft}`>, Reverse<`${TRight}`>>>>;
|
|
81
|
+
/**
|
|
82
|
+
* A tuple as long as a number of up to four digits, built one digit at a time
|
|
83
|
+
* — ten copies of what came before, and the digit — so its depth is the number
|
|
84
|
+
* of digits rather than the number itself.
|
|
85
|
+
*/
|
|
86
|
+
type TupleOf<TDigits extends string, TAccumulated extends unknown[] = []> = TDigits extends `${infer THead extends Digit}${infer TTail}` ? TupleOf<TTail, [
|
|
87
|
+
...TAccumulated,
|
|
88
|
+
...TAccumulated,
|
|
89
|
+
...TAccumulated,
|
|
90
|
+
...TAccumulated,
|
|
91
|
+
...TAccumulated,
|
|
92
|
+
...TAccumulated,
|
|
93
|
+
...TAccumulated,
|
|
94
|
+
...TAccumulated,
|
|
95
|
+
...TAccumulated,
|
|
96
|
+
...TAccumulated,
|
|
97
|
+
...Units<DigitValues[THead]>
|
|
98
|
+
]> : TAccumulated;
|
|
99
|
+
/** The last `N` characters of a text written least significant first. */
|
|
100
|
+
type Take<TText extends string, N extends number, TTaken extends string = '', TCount extends unknown[] = []> = TCount['length'] extends N ? TTaken : TText extends `${infer THead}${infer TTail}` ? Take<TTail, N, `${THead}${TTaken}`, [...TCount, unknown]> : TTaken;
|
|
101
|
+
/** Digits of a number that decide its remainder by each alignment. */
|
|
102
|
+
interface RemainderDigits {
|
|
103
|
+
readonly 2: 1;
|
|
104
|
+
readonly 4: 2;
|
|
105
|
+
readonly 8: 3;
|
|
106
|
+
readonly 16: 4;
|
|
107
|
+
}
|
|
108
|
+
/** How many elements are left of a tuple once whole chunks are taken off it. */
|
|
109
|
+
type Leftover<TTuple extends unknown[], TChunk extends unknown[]> = TTuple extends [...TChunk, ...infer TRest] ? Leftover<TRest, TChunk> : TTuple['length'];
|
|
110
|
+
/**
|
|
111
|
+
* The remainder of a number by an alignment.
|
|
112
|
+
*
|
|
113
|
+
* @template N A non-negative integer literal.
|
|
114
|
+
* @template TAlignment 2, 4, 8 or 16.
|
|
115
|
+
*/
|
|
116
|
+
type Remainder<N extends number, TAlignment extends keyof RemainderDigits> = Leftover<TupleOf<Take<Reverse<`${N}`>, RemainderDigits[TAlignment]>>, Units<TAlignment>>;
|
|
117
|
+
/**
|
|
118
|
+
* A number rounded up to a multiple of an alignment: the size of a struct once
|
|
119
|
+
* its tail padding is added.
|
|
120
|
+
*
|
|
121
|
+
* @template N Size before padding.
|
|
122
|
+
* @template TAlignment Alignment of the struct, a power of two up to 16.
|
|
123
|
+
*/
|
|
124
|
+
export type RoundUp<N extends number, TAlignment extends number> = number extends N | TAlignment ? number : TAlignment extends keyof RemainderDigits ? Remainder<N, TAlignment> extends infer TRemainder extends number ? TRemainder extends 0 ? N : Units<TAlignment> extends [...Units<TRemainder>, ...infer TPadding] ? Add<N, TPadding['length']> : never : never : N;
|
|
125
|
+
/**
|
|
126
|
+
* The largest of a union of alignments.
|
|
127
|
+
*
|
|
128
|
+
* @template TAlignments Alignments of the fields, each a power of two up to 16.
|
|
129
|
+
*/
|
|
130
|
+
export type LargestAlignment<TAlignments extends number> = number extends TAlignments ? number : 16 extends TAlignments ? 16 : 8 extends TAlignments ? 8 : 4 extends TAlignments ? 4 : 2 extends TAlignments ? 2 : 1;
|
|
131
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Arithmetic on number literals, at the type level, for the layout a struct
|
|
4
|
+
* type declares.
|
|
5
|
+
*
|
|
6
|
+
* `sizeOf<T>()` answers from the literal in `T['~layout']`, so a struct type
|
|
7
|
+
* has to carry its size as a literal, and that literal is a sum of its fields
|
|
8
|
+
* rounded up to its alignment. TypeScript has no arithmetic on literals; this
|
|
9
|
+
* module is the smallest amount of it the layout needs.
|
|
10
|
+
*
|
|
11
|
+
* Addition works digit by digit on the decimal text of a number, so it is not
|
|
12
|
+
* bounded by the recursion limit the way counting a tuple up to the sum would
|
|
13
|
+
* be. Rounding needs a remainder, and the remainder by an alignment — a power
|
|
14
|
+
* of two up to sixteen — depends only on the last four digits, since sixteen
|
|
15
|
+
* divides ten thousand; so no tuple built here is ever longer than 9,999.
|
|
16
|
+
*
|
|
17
|
+
* Wherever an input is `number` rather than a literal, the answer is `number`:
|
|
18
|
+
* `sizeOf` then refuses the type rather than report a wrong size.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a field of a struct is laid out in bytes, and how it is compared.
|
|
3
|
+
*
|
|
4
|
+
* Internal. A struct is built from descriptors, and a descriptor says how a
|
|
5
|
+
* value is checked but not how it is stored — `SinglePrecisionFloat` has no
|
|
6
|
+
* byte order. The codec is what a struct looks up for each field, by the
|
|
7
|
+
* identity of its descriptor, so the numeric types did not have to grow a
|
|
8
|
+
* public byte encoding for this.
|
|
9
|
+
*
|
|
10
|
+
* Every multi-byte value is little-endian, whatever the platform: bytes a
|
|
11
|
+
* struct writes on one machine read back the same on any other, and it is the
|
|
12
|
+
* order of every platform a JavaScript engine runs on in practice.
|
|
13
|
+
*/
|
|
14
|
+
export interface FieldCodec {
|
|
15
|
+
/** Size, in bytes. */
|
|
16
|
+
readonly size: number;
|
|
17
|
+
/** Alignment, in bytes. */
|
|
18
|
+
readonly alignment: number;
|
|
19
|
+
/**
|
|
20
|
+
* Reads a value.
|
|
21
|
+
*
|
|
22
|
+
* @param view Bytes to read from.
|
|
23
|
+
* @param offset Where the value starts.
|
|
24
|
+
* @returns The value.
|
|
25
|
+
*/
|
|
26
|
+
read(view: DataView, offset: number): unknown;
|
|
27
|
+
/**
|
|
28
|
+
* Writes a value.
|
|
29
|
+
*
|
|
30
|
+
* @param view Bytes to write into.
|
|
31
|
+
* @param offset Where the value starts.
|
|
32
|
+
* @param value The value, already of the field's type.
|
|
33
|
+
*/
|
|
34
|
+
write(view: DataView, offset: number, value: unknown): void;
|
|
35
|
+
/**
|
|
36
|
+
* Compares two values as the field's own type does.
|
|
37
|
+
*
|
|
38
|
+
* @param left First value.
|
|
39
|
+
* @param right Second value.
|
|
40
|
+
* @returns `true` when they are equal.
|
|
41
|
+
*/
|
|
42
|
+
equals(left: unknown, right: unknown): boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Registers the codec of a struct, so that another struct can hold it as a
|
|
46
|
+
* field.
|
|
47
|
+
*
|
|
48
|
+
* @param descriptor Descriptor of the struct.
|
|
49
|
+
* @param codec How it is stored.
|
|
50
|
+
*/
|
|
51
|
+
export declare const registerStructCodec: (descriptor: object, codec: FieldCodec) => void;
|
|
52
|
+
/**
|
|
53
|
+
* The codec of a field's descriptor.
|
|
54
|
+
*
|
|
55
|
+
* @param descriptor What the field was declared with.
|
|
56
|
+
* @returns Its codec, or `undefined` for a descriptor with no fixed layout.
|
|
57
|
+
*/
|
|
58
|
+
export declare const codecOf: (descriptor: unknown) => FieldCodec | undefined;
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.codecOf = exports.registerStructCodec = void 0;
|
|
4
|
+
const decimal_1 = require("../decimal/index.js");
|
|
5
|
+
const format_1 = require("../decimal/format.js");
|
|
6
|
+
const parse_1 = require("../decimal/parse.js");
|
|
7
|
+
const parts_1 = require("../decimal/parts.js");
|
|
8
|
+
const doublePrecisionFloat_1 = require("../doublePrecisionFloat/index.js");
|
|
9
|
+
const halfPrecisionFloat_1 = require("../halfPrecisionFloat/index.js");
|
|
10
|
+
const signedInteger_1 = require("../signedInteger/index.js");
|
|
11
|
+
const singlePrecisionFloat_1 = require("../singlePrecisionFloat/index.js");
|
|
12
|
+
const unsignedInteger_1 = require("../unsignedInteger/index.js");
|
|
13
|
+
/** Bits of a 64-bit half of a 128-bit value. */
|
|
14
|
+
const HALF_WIDTH = 64n;
|
|
15
|
+
/**
|
|
16
|
+
* The codec of a 128-bit integer, as two 64-bit halves, low half first.
|
|
17
|
+
*
|
|
18
|
+
* @param descriptor Descriptor of the width.
|
|
19
|
+
* @returns The codec.
|
|
20
|
+
*/
|
|
21
|
+
const integer128Codec = (descriptor) => ({
|
|
22
|
+
size: 16,
|
|
23
|
+
alignment: 16,
|
|
24
|
+
read: (view, offset) => {
|
|
25
|
+
const low = view.getBigUint64(offset, true);
|
|
26
|
+
const high = descriptor.signed
|
|
27
|
+
? view.getBigInt64(offset + 8, true)
|
|
28
|
+
: view.getBigUint64(offset + 8, true);
|
|
29
|
+
return (high << HALF_WIDTH) | low;
|
|
30
|
+
},
|
|
31
|
+
write: (view, offset, value) => {
|
|
32
|
+
view.setBigUint64(offset, BigInt.asUintN(64, value), true);
|
|
33
|
+
view.setBigUint64(offset + 8, BigInt.asUintN(64, value >> HALF_WIDTH), true);
|
|
34
|
+
},
|
|
35
|
+
equals: (left, right) => descriptor.equals(left, right),
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* The codec of a fixed-width integer, through the `DataView` accessor of its
|
|
39
|
+
* width, or two of them at 128 bits.
|
|
40
|
+
*
|
|
41
|
+
* @param descriptor Descriptor of the width.
|
|
42
|
+
* @returns The codec.
|
|
43
|
+
*/
|
|
44
|
+
const integerCodec = (descriptor) => {
|
|
45
|
+
const { signed, width } = descriptor;
|
|
46
|
+
if (width === 128)
|
|
47
|
+
return integer128Codec(descriptor);
|
|
48
|
+
const size = width / 8;
|
|
49
|
+
const equals = (left, right) => descriptor.equals(left, right);
|
|
50
|
+
switch (width) {
|
|
51
|
+
case 8:
|
|
52
|
+
return {
|
|
53
|
+
size,
|
|
54
|
+
alignment: size,
|
|
55
|
+
read: (view, offset) => signed ? view.getInt8(offset) : view.getUint8(offset),
|
|
56
|
+
write: (view, offset, value) => signed
|
|
57
|
+
? view.setInt8(offset, value)
|
|
58
|
+
: view.setUint8(offset, value),
|
|
59
|
+
equals,
|
|
60
|
+
};
|
|
61
|
+
case 16:
|
|
62
|
+
return {
|
|
63
|
+
size,
|
|
64
|
+
alignment: size,
|
|
65
|
+
read: (view, offset) => signed ? view.getInt16(offset, true) : view.getUint16(offset, true),
|
|
66
|
+
write: (view, offset, value) => signed
|
|
67
|
+
? view.setInt16(offset, value, true)
|
|
68
|
+
: view.setUint16(offset, value, true),
|
|
69
|
+
equals,
|
|
70
|
+
};
|
|
71
|
+
case 32:
|
|
72
|
+
return {
|
|
73
|
+
size,
|
|
74
|
+
alignment: size,
|
|
75
|
+
read: (view, offset) => signed ? view.getInt32(offset, true) : view.getUint32(offset, true),
|
|
76
|
+
write: (view, offset, value) => signed
|
|
77
|
+
? view.setInt32(offset, value, true)
|
|
78
|
+
: view.setUint32(offset, value, true),
|
|
79
|
+
equals,
|
|
80
|
+
};
|
|
81
|
+
default:
|
|
82
|
+
return {
|
|
83
|
+
size,
|
|
84
|
+
alignment: size,
|
|
85
|
+
read: (view, offset) => signed
|
|
86
|
+
? view.getBigInt64(offset, true)
|
|
87
|
+
: view.getBigUint64(offset, true),
|
|
88
|
+
write: (view, offset, value) => signed
|
|
89
|
+
? view.setBigInt64(offset, value, true)
|
|
90
|
+
: view.setBigUint64(offset, value, true),
|
|
91
|
+
equals,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
/** Exponent of the smallest normal half precision value. */
|
|
96
|
+
const HALF_MINIMUM_NORMAL_EXPONENT = -14;
|
|
97
|
+
/** Spacing of the half precision subnormals, 2^-24. */
|
|
98
|
+
const HALF_SUBNORMAL_SPACING = 2 ** -24;
|
|
99
|
+
/**
|
|
100
|
+
* The binary16 bit pattern of a half precision value.
|
|
101
|
+
*
|
|
102
|
+
* Written here rather than delegated to `DataView.prototype.setFloat16`, which
|
|
103
|
+
* Node 22 — the oldest line this package supports — does not have. The value
|
|
104
|
+
* is already exactly representable, so every division below is exact and
|
|
105
|
+
* nothing is rounded: this only rearranges bits.
|
|
106
|
+
*
|
|
107
|
+
* @param value A half precision value.
|
|
108
|
+
* @returns Its sixteen bits.
|
|
109
|
+
*/
|
|
110
|
+
const encodeHalf = (value) => {
|
|
111
|
+
if (Number.isNaN(value))
|
|
112
|
+
return 0x7e00;
|
|
113
|
+
const sign = value < 0 || Object.is(value, -0) ? 0x8000 : 0;
|
|
114
|
+
const magnitude = Math.abs(value);
|
|
115
|
+
if (magnitude === Infinity)
|
|
116
|
+
return sign | 0x7c00;
|
|
117
|
+
if (magnitude < 2 ** HALF_MINIMUM_NORMAL_EXPONENT) {
|
|
118
|
+
return sign | (magnitude / HALF_SUBNORMAL_SPACING);
|
|
119
|
+
}
|
|
120
|
+
// `Math.log2` is not exact next to a power of two, so the estimate is
|
|
121
|
+
// corrected against the power itself.
|
|
122
|
+
let exponent = Math.floor(Math.log2(magnitude));
|
|
123
|
+
if (2 ** exponent > magnitude)
|
|
124
|
+
exponent--;
|
|
125
|
+
else if (2 ** (exponent + 1) <= magnitude)
|
|
126
|
+
exponent++;
|
|
127
|
+
return (sign | ((exponent + 15) << 10) | ((magnitude / 2 ** exponent - 1) * 1024));
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* The half precision value of a binary16 bit pattern.
|
|
131
|
+
*
|
|
132
|
+
* @param bits Sixteen bits.
|
|
133
|
+
* @returns The value they encode.
|
|
134
|
+
*/
|
|
135
|
+
const decodeHalf = (bits) => {
|
|
136
|
+
const sign = bits & 0x8000 ? -1 : 1;
|
|
137
|
+
const exponent = (bits >> 10) & 0x1f;
|
|
138
|
+
const fraction = bits & 0x3ff;
|
|
139
|
+
if (exponent === 0)
|
|
140
|
+
return sign * fraction * HALF_SUBNORMAL_SPACING;
|
|
141
|
+
if (exponent === 0x1f)
|
|
142
|
+
return fraction === 0 ? sign * Infinity : NaN;
|
|
143
|
+
return sign * (1 + fraction / 1024) * 2 ** (exponent - 15);
|
|
144
|
+
};
|
|
145
|
+
/** The codecs of the three binary float formats, by descriptor. */
|
|
146
|
+
const floatCodecs = new Map([
|
|
147
|
+
[
|
|
148
|
+
halfPrecisionFloat_1.HalfPrecisionFloat,
|
|
149
|
+
{
|
|
150
|
+
size: 2,
|
|
151
|
+
alignment: 2,
|
|
152
|
+
read: (view, offset) => decodeHalf(view.getUint16(offset, true)),
|
|
153
|
+
write: (view, offset, value) => view.setUint16(offset, encodeHalf(value), true),
|
|
154
|
+
equals: (left, right) => halfPrecisionFloat_1.HalfPrecisionFloat.equals(left, right),
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
[
|
|
158
|
+
singlePrecisionFloat_1.SinglePrecisionFloat,
|
|
159
|
+
{
|
|
160
|
+
size: 4,
|
|
161
|
+
alignment: 4,
|
|
162
|
+
read: (view, offset) => view.getFloat32(offset, true),
|
|
163
|
+
write: (view, offset, value) => view.setFloat32(offset, value, true),
|
|
164
|
+
equals: (left, right) => singlePrecisionFloat_1.SinglePrecisionFloat.equals(left, right),
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
[
|
|
168
|
+
doublePrecisionFloat_1.DoublePrecisionFloat,
|
|
169
|
+
{
|
|
170
|
+
size: 8,
|
|
171
|
+
alignment: 8,
|
|
172
|
+
read: (view, offset) => view.getFloat64(offset, true),
|
|
173
|
+
write: (view, offset, value) => view.setFloat64(offset, value, true),
|
|
174
|
+
equals: (left, right) => doublePrecisionFloat_1.DoublePrecisionFloat.equals(left, right),
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
]);
|
|
178
|
+
/** Bias of the decimal128 exponent: the stored field is `exponent + 6176`. */
|
|
179
|
+
const DECIMAL_EXPONENT_BIAS = 6176;
|
|
180
|
+
/**
|
|
181
|
+
* Largest exponent a decimal128 coefficient can be scaled by: the largest
|
|
182
|
+
* value, thirty-four nines, has its last digit at 10^6111.
|
|
183
|
+
*/
|
|
184
|
+
const DECIMAL_MAXIMUM_EXPONENT = 6111;
|
|
185
|
+
/** Bits of the decimal128 coefficient field. */
|
|
186
|
+
const DECIMAL_COEFFICIENT_BITS = 113n;
|
|
187
|
+
/** The coefficients a decimal128 holds are below this; above it is non-canonical. */
|
|
188
|
+
const DECIMAL_COEFFICIENT_LIMIT = (0, parts_1.powerOfTen)(34);
|
|
189
|
+
/**
|
|
190
|
+
* The decimal128 bits of a decimal, in the binary integer decimal encoding
|
|
191
|
+
* IEEE 754-2008 defines: a sign bit, fourteen bits of biased exponent and a
|
|
192
|
+
* 113-bit binary coefficient.
|
|
193
|
+
*
|
|
194
|
+
* The parts are normalised, which can leave a large value with a short
|
|
195
|
+
* coefficient and an exponent past the one the format stores — 1 × 10^6144.
|
|
196
|
+
* Those are scaled back into range by moving digits into the coefficient,
|
|
197
|
+
* which always has room for them because the value is within range.
|
|
198
|
+
*
|
|
199
|
+
* @param parts The value.
|
|
200
|
+
* @returns Its 128 bits.
|
|
201
|
+
*/
|
|
202
|
+
const encodeDecimal = (parts) => {
|
|
203
|
+
const sign = parts.negative ? 1n << 127n : 0n;
|
|
204
|
+
if (parts.kind === 'nan')
|
|
205
|
+
return 0x7c00000000000000n << HALF_WIDTH;
|
|
206
|
+
if (parts.kind === 'infinity') {
|
|
207
|
+
return sign | (0x7800000000000000n << HALF_WIDTH);
|
|
208
|
+
}
|
|
209
|
+
let { coefficient, exponent } = parts;
|
|
210
|
+
if (exponent > DECIMAL_MAXIMUM_EXPONENT) {
|
|
211
|
+
coefficient *= (0, parts_1.powerOfTen)(exponent - DECIMAL_MAXIMUM_EXPONENT);
|
|
212
|
+
exponent = DECIMAL_MAXIMUM_EXPONENT;
|
|
213
|
+
}
|
|
214
|
+
return (sign |
|
|
215
|
+
(BigInt(exponent + DECIMAL_EXPONENT_BIAS) << DECIMAL_COEFFICIENT_BITS) |
|
|
216
|
+
coefficient);
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* The decimal of decimal128 bits.
|
|
220
|
+
*
|
|
221
|
+
* A coefficient past thirty-four digits — including every one written in the
|
|
222
|
+
* encoding's second form, whose leading bits are `11` — is non-canonical, and
|
|
223
|
+
* IEEE 754 reads it as zero. Trailing zeros are moved into the exponent, which
|
|
224
|
+
* is the normalised form every `Decimal` holds.
|
|
225
|
+
*
|
|
226
|
+
* @param bits 128 bits.
|
|
227
|
+
* @returns The parts they encode.
|
|
228
|
+
*/
|
|
229
|
+
const decodeDecimal = (bits) => {
|
|
230
|
+
const negative = bits >> 127n === 1n;
|
|
231
|
+
const combination = (bits >> 122n) & 0x1fn;
|
|
232
|
+
if (combination === 0x1fn) {
|
|
233
|
+
return { kind: 'nan', negative: false, coefficient: 0n, exponent: 0 };
|
|
234
|
+
}
|
|
235
|
+
if (combination === 0x1en) {
|
|
236
|
+
return { kind: 'infinity', negative, coefficient: 0n, exponent: 0 };
|
|
237
|
+
}
|
|
238
|
+
let coefficient = ((bits >> 125n) & 0x3n) === 0x3n
|
|
239
|
+
? 0n
|
|
240
|
+
: bits & ((1n << DECIMAL_COEFFICIENT_BITS) - 1n);
|
|
241
|
+
if (coefficient >= DECIMAL_COEFFICIENT_LIMIT)
|
|
242
|
+
coefficient = 0n;
|
|
243
|
+
if (coefficient === 0n) {
|
|
244
|
+
return { kind: 'finite', negative, coefficient: 0n, exponent: 0 };
|
|
245
|
+
}
|
|
246
|
+
let exponent = Number((bits >> DECIMAL_COEFFICIENT_BITS) & 0x3fffn) -
|
|
247
|
+
DECIMAL_EXPONENT_BIAS;
|
|
248
|
+
while (coefficient % 10n === 0n) {
|
|
249
|
+
coefficient /= 10n;
|
|
250
|
+
exponent++;
|
|
251
|
+
}
|
|
252
|
+
return { kind: 'finite', negative, coefficient, exponent };
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* The codec of `Decimal`, as decimal128.
|
|
256
|
+
*
|
|
257
|
+
* Its parts are private to the class, so they travel through its text, which
|
|
258
|
+
* reads back exactly: the shortest text of a normalised value names exactly
|
|
259
|
+
* that value.
|
|
260
|
+
*/
|
|
261
|
+
const decimalCodec = {
|
|
262
|
+
size: 16,
|
|
263
|
+
alignment: 16,
|
|
264
|
+
read: (view, offset) => {
|
|
265
|
+
const bits = (view.getBigUint64(offset + 8, true) << HALF_WIDTH) |
|
|
266
|
+
view.getBigUint64(offset, true);
|
|
267
|
+
return decimal_1.Decimal.from((0, format_1.formatDecimal)(decodeDecimal(bits)));
|
|
268
|
+
},
|
|
269
|
+
write: (view, offset, value) => {
|
|
270
|
+
const bits = encodeDecimal((0, parse_1.parseDecimal)(String(value)));
|
|
271
|
+
view.setBigUint64(offset, BigInt.asUintN(64, bits), true);
|
|
272
|
+
view.setBigUint64(offset + 8, bits >> HALF_WIDTH, true);
|
|
273
|
+
},
|
|
274
|
+
equals: (left, right) => left.equals(right),
|
|
275
|
+
};
|
|
276
|
+
/** Codecs registered by the structs themselves, so one can nest in another. */
|
|
277
|
+
const structCodecs = new WeakMap();
|
|
278
|
+
/** Widths a fixed-width integer comes in. */
|
|
279
|
+
const INTEGER_WIDTHS = [8, 16, 32, 64, 128];
|
|
280
|
+
/**
|
|
281
|
+
* Tells whether a value is the descriptor `SignedInteger` or `UnsignedInteger`
|
|
282
|
+
* returns for its width — the same object, not one shaped like it.
|
|
283
|
+
*
|
|
284
|
+
* @param descriptor Value to inspect.
|
|
285
|
+
* @returns `true` for an integer descriptor of this package.
|
|
286
|
+
*/
|
|
287
|
+
const isIntegerDescriptor = (descriptor) => {
|
|
288
|
+
const { signed, width } = descriptor;
|
|
289
|
+
if (typeof signed !== 'boolean' || !INTEGER_WIDTHS.includes(width)) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
const factory = signed ? signedInteger_1.SignedInteger : unsignedInteger_1.UnsignedInteger;
|
|
293
|
+
return factory(width) === descriptor;
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* Registers the codec of a struct, so that another struct can hold it as a
|
|
297
|
+
* field.
|
|
298
|
+
*
|
|
299
|
+
* @param descriptor Descriptor of the struct.
|
|
300
|
+
* @param codec How it is stored.
|
|
301
|
+
*/
|
|
302
|
+
const registerStructCodec = (descriptor, codec) => {
|
|
303
|
+
structCodecs.set(descriptor, codec);
|
|
304
|
+
};
|
|
305
|
+
exports.registerStructCodec = registerStructCodec;
|
|
306
|
+
/**
|
|
307
|
+
* The codec of a field's descriptor.
|
|
308
|
+
*
|
|
309
|
+
* @param descriptor What the field was declared with.
|
|
310
|
+
* @returns Its codec, or `undefined` for a descriptor with no fixed layout.
|
|
311
|
+
*/
|
|
312
|
+
const codecOf = (descriptor) => {
|
|
313
|
+
if (typeof descriptor !== 'object' && typeof descriptor !== 'function') {
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
if (descriptor === null)
|
|
317
|
+
return undefined;
|
|
318
|
+
if (descriptor === decimal_1.Decimal)
|
|
319
|
+
return decimalCodec;
|
|
320
|
+
const known = floatCodecs.get(descriptor) ?? structCodecs.get(descriptor);
|
|
321
|
+
if (known !== undefined)
|
|
322
|
+
return known;
|
|
323
|
+
return isIntegerDescriptor(descriptor) ? integerCodec(descriptor) : undefined;
|
|
324
|
+
};
|
|
325
|
+
exports.codecOf = codecOf;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { Layout } from '../layout/index.js';
|
|
2
|
+
import type { Add, LargestAlignment, RoundUp } from './arithmetic.js';
|
|
3
|
+
/**
|
|
4
|
+
* What a field of a struct may be declared with: the descriptor of a type that
|
|
5
|
+
* declares a fixed layout — a numeric type of this package other than
|
|
6
|
+
* `BigInteger`, or another struct.
|
|
7
|
+
*/
|
|
8
|
+
interface FieldDescriptor {
|
|
9
|
+
is(value: unknown): value is Layout<number, number>;
|
|
10
|
+
}
|
|
11
|
+
/** The fields of a struct, by name, in declaration order. */
|
|
12
|
+
type StructFields = {
|
|
13
|
+
readonly [field: string]: FieldDescriptor;
|
|
14
|
+
};
|
|
15
|
+
/** The type of the values a descriptor recognises. */
|
|
16
|
+
type ValueOf<TDescriptor> = TDescriptor extends {
|
|
17
|
+
is(value: unknown): value is infer T;
|
|
18
|
+
} ? T : never;
|
|
19
|
+
/** What a descriptor's `from` accepts. */
|
|
20
|
+
type SourceOf<TDescriptor> = TDescriptor extends {
|
|
21
|
+
from(value: infer TSource): unknown;
|
|
22
|
+
} ? TSource : never;
|
|
23
|
+
/** The layout a field's type declares. */
|
|
24
|
+
type FieldLayout<TDescriptor> = ValueOf<TDescriptor> extends Layout<infer TSize, infer TAlignment> ? {
|
|
25
|
+
readonly size: TSize;
|
|
26
|
+
readonly alignment: TAlignment;
|
|
27
|
+
} : never;
|
|
28
|
+
/** A union, as a tuple in no particular order — enough to sum over it. */
|
|
29
|
+
type UnionToTuple<TUnion, TTuple extends unknown[] = []> = [TUnion] extends [
|
|
30
|
+
never
|
|
31
|
+
] ? TTuple : UnionToTuple<Exclude<TUnion, LastOf<TUnion>>, [LastOf<TUnion>, ...TTuple]>;
|
|
32
|
+
/** One member of a union, picked by how TypeScript orders overloads. */
|
|
33
|
+
type LastOf<TUnion> = (TUnion extends unknown ? (member: () => TUnion) => void : never) extends (member: infer TIntersection) => void ? TIntersection extends () => infer TMember ? TMember : never : never;
|
|
34
|
+
/** The sizes of some fields, added up. */
|
|
35
|
+
type SumOfSizes<TFields extends StructFields, TKeys extends unknown[], TSum extends number = 0> = TKeys extends [infer TKey extends keyof TFields, ...infer TRest] ? SumOfSizes<TFields, TRest, Add<TSum, FieldLayout<TFields[TKey]>['size']>> : TSum;
|
|
36
|
+
/** Alignment of a struct: the largest alignment of its fields. */
|
|
37
|
+
type StructAlignment<TFields extends StructFields> = LargestAlignment<{
|
|
38
|
+
[TKey in keyof TFields]: FieldLayout<TFields[TKey]>['alignment'];
|
|
39
|
+
}[keyof TFields]>;
|
|
40
|
+
/**
|
|
41
|
+
* Size of a struct: its fields, which pack without padding once ordered by
|
|
42
|
+
* alignment, rounded up to its alignment. Independent of the order the fields
|
|
43
|
+
* were declared in, which is why it can be computed here at all — a type does
|
|
44
|
+
* not promise an order for its keys.
|
|
45
|
+
*/
|
|
46
|
+
type StructSize<TFields extends StructFields> = RoundUp<SumOfSizes<TFields, UnionToTuple<keyof TFields>>, StructAlignment<TFields>>;
|
|
47
|
+
/** A value of a struct. */
|
|
48
|
+
type StructValue<TFields extends StructFields> = {
|
|
49
|
+
readonly [TKey in keyof TFields]: ValueOf<TFields[TKey]>;
|
|
50
|
+
} & Layout<StructSize<TFields>, StructAlignment<TFields>>;
|
|
51
|
+
/** What a struct's `from` accepts: each field in what its own `from` accepts. */
|
|
52
|
+
type StructSource<TFields extends StructFields> = {
|
|
53
|
+
readonly [TKey in keyof TFields]: SourceOf<TFields[TKey]>;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* The descriptor of a struct: its layout, and how its values are made,
|
|
57
|
+
* recognised, compared and stored.
|
|
58
|
+
*
|
|
59
|
+
* @template TFields Descriptors of the fields, by name.
|
|
60
|
+
*/
|
|
61
|
+
export interface StructType<TFields extends StructFields> {
|
|
62
|
+
/** Name of the struct, as it reads in an error message. */
|
|
63
|
+
readonly name: string;
|
|
64
|
+
/**
|
|
65
|
+
* Where each field sits and how much room the struct takes.
|
|
66
|
+
*
|
|
67
|
+
* Fields are placed by alignment, largest first, and in declaration order
|
|
68
|
+
* among equals — so no field needs padding before it, and only the end of
|
|
69
|
+
* the struct is padded, up to its alignment, so that the next one in an
|
|
70
|
+
* array starts aligned. `size` and `alignment` are the numbers
|
|
71
|
+
* `sizeOf<T>()` and `alignOf<T>()` report.
|
|
72
|
+
*/
|
|
73
|
+
readonly layout: {
|
|
74
|
+
readonly size: StructSize<TFields>;
|
|
75
|
+
readonly alignment: StructAlignment<TFields>;
|
|
76
|
+
readonly fields: {
|
|
77
|
+
readonly [TKey in keyof TFields]: {
|
|
78
|
+
readonly offset: number;
|
|
79
|
+
readonly size: FieldLayout<TFields[TKey]>['size'];
|
|
80
|
+
readonly alignment: FieldLayout<TFields[TKey]>['alignment'];
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Makes a value, converting each field with its own type's `from`.
|
|
86
|
+
*
|
|
87
|
+
* @param source One entry per field, and nothing else.
|
|
88
|
+
* @returns The value, frozen.
|
|
89
|
+
* @throws {TypeError} When a field is missing or not a field of the struct.
|
|
90
|
+
* @throws {RangeError} When a field's own conversion refuses its value; the
|
|
91
|
+
* message names the field, and `cause` is the original error.
|
|
92
|
+
*/
|
|
93
|
+
from(source: StructSource<TFields>): StructValue<TFields>;
|
|
94
|
+
/**
|
|
95
|
+
* Tells whether a value is one of this struct's: frozen, with exactly its
|
|
96
|
+
* fields, each of its type.
|
|
97
|
+
*
|
|
98
|
+
* @param value Value to inspect.
|
|
99
|
+
* @returns `true` when it is.
|
|
100
|
+
*/
|
|
101
|
+
is(value: unknown): value is StructValue<TFields>;
|
|
102
|
+
/**
|
|
103
|
+
* Compares two values field by field, each as its own type compares — so a
|
|
104
|
+
* `NaN` field makes a value unequal to itself, as it does in C#.
|
|
105
|
+
*
|
|
106
|
+
* @param left First value.
|
|
107
|
+
* @param right Second value.
|
|
108
|
+
* @returns `true` when every field is equal.
|
|
109
|
+
*/
|
|
110
|
+
equals(left: StructValue<TFields>, right: StructValue<TFields>): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Reads a value from bytes, in the layout of {@link StructType.layout},
|
|
113
|
+
* little-endian.
|
|
114
|
+
*
|
|
115
|
+
* @param view Bytes to read from.
|
|
116
|
+
* @param offset Where the value starts.
|
|
117
|
+
* @returns The value, frozen.
|
|
118
|
+
* @throws {RangeError} When the struct does not fit in the view at that
|
|
119
|
+
* offset.
|
|
120
|
+
*/
|
|
121
|
+
read(view: DataView, offset: number): StructValue<TFields>;
|
|
122
|
+
/**
|
|
123
|
+
* Writes a value into bytes, in the layout of {@link StructType.layout},
|
|
124
|
+
* little-endian. Padding bytes are left as they were.
|
|
125
|
+
*
|
|
126
|
+
* @param view Bytes to write into.
|
|
127
|
+
* @param offset Where the value starts.
|
|
128
|
+
* @param value Value to write.
|
|
129
|
+
* @throws {RangeError} When the struct does not fit in the view at that
|
|
130
|
+
* offset.
|
|
131
|
+
*/
|
|
132
|
+
write(view: DataView, offset: number, value: StructValue<TFields>): void;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The type of the values of a struct, named from its descriptor.
|
|
136
|
+
*
|
|
137
|
+
* ```ts
|
|
138
|
+
* export const Vector3 = struct('Vector3', { x: SinglePrecisionFloat, … });
|
|
139
|
+
* export type Vector3 = Struct<typeof Vector3>;
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @template TDescriptor Type of the descriptor `struct` returned.
|
|
143
|
+
*/
|
|
144
|
+
export type Struct<TDescriptor extends StructType<StructFields>> = TDescriptor extends StructType<infer TFields> ? StructValue<TFields> : never;
|
|
145
|
+
/**
|
|
146
|
+
* Declares a struct: a value type with a fixed layout.
|
|
147
|
+
*
|
|
148
|
+
* ```ts
|
|
149
|
+
* export const Vector3 = struct('Vector3', {
|
|
150
|
+
* x: SinglePrecisionFloat,
|
|
151
|
+
* y: SinglePrecisionFloat,
|
|
152
|
+
* z: SinglePrecisionFloat,
|
|
153
|
+
* });
|
|
154
|
+
* export type Vector3 = Struct<typeof Vector3>;
|
|
155
|
+
*
|
|
156
|
+
* const up: Vector3 = Vector3.from({ x: 0, y: 1, z: 0 });
|
|
157
|
+
*
|
|
158
|
+
* Vector3.layout.size; // 12
|
|
159
|
+
* Vector3.write(new DataView(buffer), 0, up);
|
|
160
|
+
* ```
|
|
161
|
+
*
|
|
162
|
+
* A value has no identity: it is frozen, two values with the same fields are
|
|
163
|
+
* equal by {@link StructType.equals}, and it can be written into bytes and
|
|
164
|
+
* read back as the same value. It is still a JavaScript object while it is
|
|
165
|
+
* held as one; the layout is what it occupies when it is stored.
|
|
166
|
+
*
|
|
167
|
+
* @param name Name of the struct, for error messages.
|
|
168
|
+
* @param fields Descriptor of each field, by name.
|
|
169
|
+
* @returns The descriptor of the struct.
|
|
170
|
+
* @throws {TypeError} When there is no field, a field's type has no fixed
|
|
171
|
+
* layout, or a field is named like an array index or `~layout`.
|
|
172
|
+
*/
|
|
173
|
+
export declare const struct: <TFields extends StructFields>(name: string, fields: TFields) => StructType<TFields>;
|
|
174
|
+
export {};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.struct = void 0;
|
|
4
|
+
const codec_1 = require("./codec.js");
|
|
5
|
+
/**
|
|
6
|
+
* Keys an object lists before every other, in numeric order, whatever order
|
|
7
|
+
* they were written in. A field named like one would silently move, so it is
|
|
8
|
+
* refused.
|
|
9
|
+
*/
|
|
10
|
+
const ARRAY_INDEX = /^(?:0|[1-9]\d*)$/;
|
|
11
|
+
/**
|
|
12
|
+
* Describes a value for an error message.
|
|
13
|
+
*
|
|
14
|
+
* @param value Value being reported.
|
|
15
|
+
* @returns Its kind.
|
|
16
|
+
*/
|
|
17
|
+
const describeKind = (value) => value === null ? 'null' : typeof value;
|
|
18
|
+
/**
|
|
19
|
+
* Re-throws the error a field's own conversion raised, naming the field.
|
|
20
|
+
*
|
|
21
|
+
* @param name Name of the struct.
|
|
22
|
+
* @param key Field being converted.
|
|
23
|
+
* @param error What the conversion threw.
|
|
24
|
+
* @returns Never.
|
|
25
|
+
*/
|
|
26
|
+
const rethrowForField = (name, key, error) => {
|
|
27
|
+
const message = (original) => `${name}.from: field '${key}': ${original.message}`;
|
|
28
|
+
if (error instanceof RangeError) {
|
|
29
|
+
throw new RangeError(message(error), { cause: error });
|
|
30
|
+
}
|
|
31
|
+
if (error instanceof TypeError) {
|
|
32
|
+
throw new TypeError(message(error), { cause: error });
|
|
33
|
+
}
|
|
34
|
+
if (error instanceof SyntaxError) {
|
|
35
|
+
throw new SyntaxError(message(error), { cause: error });
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Declares a struct: a value type with a fixed layout.
|
|
41
|
+
*
|
|
42
|
+
* ```ts
|
|
43
|
+
* export const Vector3 = struct('Vector3', {
|
|
44
|
+
* x: SinglePrecisionFloat,
|
|
45
|
+
* y: SinglePrecisionFloat,
|
|
46
|
+
* z: SinglePrecisionFloat,
|
|
47
|
+
* });
|
|
48
|
+
* export type Vector3 = Struct<typeof Vector3>;
|
|
49
|
+
*
|
|
50
|
+
* const up: Vector3 = Vector3.from({ x: 0, y: 1, z: 0 });
|
|
51
|
+
*
|
|
52
|
+
* Vector3.layout.size; // 12
|
|
53
|
+
* Vector3.write(new DataView(buffer), 0, up);
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* A value has no identity: it is frozen, two values with the same fields are
|
|
57
|
+
* equal by {@link StructType.equals}, and it can be written into bytes and
|
|
58
|
+
* read back as the same value. It is still a JavaScript object while it is
|
|
59
|
+
* held as one; the layout is what it occupies when it is stored.
|
|
60
|
+
*
|
|
61
|
+
* @param name Name of the struct, for error messages.
|
|
62
|
+
* @param fields Descriptor of each field, by name.
|
|
63
|
+
* @returns The descriptor of the struct.
|
|
64
|
+
* @throws {TypeError} When there is no field, a field's type has no fixed
|
|
65
|
+
* layout, or a field is named like an array index or `~layout`.
|
|
66
|
+
*/
|
|
67
|
+
const struct = (name, fields) => {
|
|
68
|
+
if (typeof name !== 'string' || name === '') {
|
|
69
|
+
throw new TypeError(`struct: expected a name, received ${describeKind(name)}.`);
|
|
70
|
+
}
|
|
71
|
+
if (typeof fields !== 'object' || fields === null) {
|
|
72
|
+
throw new TypeError(`struct ${name}: expected an object of fields, received ${describeKind(fields)}.`);
|
|
73
|
+
}
|
|
74
|
+
const keys = Object.keys(fields);
|
|
75
|
+
if (keys.length === 0) {
|
|
76
|
+
throw new TypeError(`struct ${name}: expected at least one field.`);
|
|
77
|
+
}
|
|
78
|
+
const declared = keys.map((key) => {
|
|
79
|
+
if (ARRAY_INDEX.test(key) || key === '~layout') {
|
|
80
|
+
throw new TypeError(`struct ${name}: '${key}' cannot name a field; an array index would be reordered, and '~layout' is the layout itself.`);
|
|
81
|
+
}
|
|
82
|
+
const codec = (0, codec_1.codecOf)(fields[key]);
|
|
83
|
+
if (codec === undefined) {
|
|
84
|
+
throw new TypeError(`struct ${name}: field '${key}' has no fixed layout. Declare it with a numeric type of @fulcro/types other than BigInteger, or with another struct.`);
|
|
85
|
+
}
|
|
86
|
+
return { key, descriptor: fields[key], codec };
|
|
87
|
+
});
|
|
88
|
+
// Largest alignment first, so that every field lands aligned without padding
|
|
89
|
+
// before it: each size is a multiple of its own alignment. `sort` is stable,
|
|
90
|
+
// which keeps declaration order among equals.
|
|
91
|
+
const offsets = new Map();
|
|
92
|
+
let end = 0;
|
|
93
|
+
for (const field of [...declared].sort((left, right) => right.codec.alignment - left.codec.alignment)) {
|
|
94
|
+
offsets.set(field.key, end);
|
|
95
|
+
end += field.codec.size;
|
|
96
|
+
}
|
|
97
|
+
const alignment = Math.max(...declared.map((field) => field.codec.alignment));
|
|
98
|
+
const size = Math.ceil(end / alignment) * alignment;
|
|
99
|
+
const plan = declared.map((field) => ({
|
|
100
|
+
...field,
|
|
101
|
+
descriptor: field.descriptor,
|
|
102
|
+
offset: offsets.get(field.key),
|
|
103
|
+
}));
|
|
104
|
+
const layout = Object.freeze({
|
|
105
|
+
size,
|
|
106
|
+
alignment,
|
|
107
|
+
fields: Object.freeze(Object.fromEntries(plan.map((field) => [
|
|
108
|
+
field.key,
|
|
109
|
+
Object.freeze({
|
|
110
|
+
offset: field.offset,
|
|
111
|
+
size: field.codec.size,
|
|
112
|
+
alignment: field.codec.alignment,
|
|
113
|
+
}),
|
|
114
|
+
]))),
|
|
115
|
+
});
|
|
116
|
+
/**
|
|
117
|
+
* Refuses a view and an offset the struct does not fit in, before a byte is
|
|
118
|
+
* touched — so a failed write leaves the view as it was.
|
|
119
|
+
*
|
|
120
|
+
* @param operation Operation being performed.
|
|
121
|
+
* @param view View handed in.
|
|
122
|
+
* @param offset Offset handed in.
|
|
123
|
+
*/
|
|
124
|
+
const requireRoom = (operation, view, offset) => {
|
|
125
|
+
if (!(view instanceof DataView)) {
|
|
126
|
+
throw new TypeError(`${name}.${operation}: expected a DataView, received ${describeKind(view)}.`);
|
|
127
|
+
}
|
|
128
|
+
if (!Number.isInteger(offset) ||
|
|
129
|
+
offset < 0 ||
|
|
130
|
+
offset + size > view.byteLength) {
|
|
131
|
+
throw new RangeError(`${name}.${operation}: ${size} bytes at offset ${offset} do not fit in a view of ${view.byteLength} bytes.`);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const readFields = (view, offset) => {
|
|
135
|
+
const value = {};
|
|
136
|
+
for (const field of plan) {
|
|
137
|
+
value[field.key] = field.codec.read(view, offset + field.offset);
|
|
138
|
+
}
|
|
139
|
+
return Object.freeze(value);
|
|
140
|
+
};
|
|
141
|
+
const writeFields = (view, offset, value) => {
|
|
142
|
+
for (const field of plan) {
|
|
143
|
+
field.codec.write(view, offset + field.offset, value[field.key]);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const equals = (left, right) => plan.every((field) => field.codec.equals(left[field.key], right[field.key]));
|
|
147
|
+
const descriptor = {
|
|
148
|
+
name,
|
|
149
|
+
layout: layout,
|
|
150
|
+
from: (source) => {
|
|
151
|
+
if (typeof source !== 'object' || source === null) {
|
|
152
|
+
throw new TypeError(`${name}.from: expected an object, received ${describeKind(source)}.`);
|
|
153
|
+
}
|
|
154
|
+
for (const key of Object.keys(source)) {
|
|
155
|
+
if (!Object.hasOwn(fields, key)) {
|
|
156
|
+
throw new TypeError(`${name}.from: '${key}' is not a field; the fields are ${keys.join(', ')}.`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const value = {};
|
|
160
|
+
for (const field of plan) {
|
|
161
|
+
if (!Object.hasOwn(source, field.key)) {
|
|
162
|
+
throw new TypeError(`${name}.from: missing field '${field.key}'.`);
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
value[field.key] = field.descriptor.from(source[field.key]);
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
rethrowForField(name, field.key, error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return Object.freeze(value);
|
|
172
|
+
},
|
|
173
|
+
is: (value) => typeof value === 'object' &&
|
|
174
|
+
value !== null &&
|
|
175
|
+
Object.isFrozen(value) &&
|
|
176
|
+
Object.keys(value).length === plan.length &&
|
|
177
|
+
plan.every((field) => Object.hasOwn(value, field.key) &&
|
|
178
|
+
field.descriptor.is(value[field.key])),
|
|
179
|
+
equals,
|
|
180
|
+
read: (view, offset) => {
|
|
181
|
+
requireRoom('read', view, offset);
|
|
182
|
+
return readFields(view, offset);
|
|
183
|
+
},
|
|
184
|
+
write: (view, offset, value) => {
|
|
185
|
+
requireRoom('write', view, offset);
|
|
186
|
+
writeFields(view, offset, value);
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
(0, codec_1.registerStructCodec)(descriptor, {
|
|
190
|
+
size,
|
|
191
|
+
alignment,
|
|
192
|
+
read: readFields,
|
|
193
|
+
write: writeFields,
|
|
194
|
+
equals,
|
|
195
|
+
});
|
|
196
|
+
return descriptor;
|
|
197
|
+
};
|
|
198
|
+
exports.struct = struct;
|
package/package.json
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@fulcro/types",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Numeric types with a defined range and layout: fixed-width integers, half, single and double precision floats, and a decimal128 Decimal.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"integer",
|
|
7
|
-
"float",
|
|
8
|
-
"decimal",
|
|
9
|
-
"decimal128",
|
|
10
|
-
"numeric",
|
|
11
|
-
"typescript"
|
|
12
|
-
],
|
|
13
|
-
"license": "ISC",
|
|
14
|
-
"author": "diguu <rodrigogeribola@hotmail.com>",
|
|
15
|
-
"main": "./dist/index.js",
|
|
16
|
-
"types": "./dist/index.d.ts",
|
|
17
|
-
"exports": {
|
|
18
|
-
".": {
|
|
19
|
-
"types": "./dist/index.d.ts",
|
|
20
|
-
"default": "./dist/index.js"
|
|
21
|
-
},
|
|
22
|
-
"./transformer": {
|
|
23
|
-
"types": "./dist/transformer/index.d.ts",
|
|
24
|
-
"default": "./dist/transformer/index.js"
|
|
25
|
-
},
|
|
26
|
-
"./unplugin": {
|
|
27
|
-
"types": "./dist/unplugin/index.d.mts",
|
|
28
|
-
"default": "./dist/unplugin/index.mjs"
|
|
29
|
-
},
|
|
30
|
-
"./language-service": {
|
|
31
|
-
"types": "./dist/languageService/index.d.ts",
|
|
32
|
-
"default": "./dist/languageService/index.js"
|
|
33
|
-
},
|
|
34
|
-
"./package.json": "./package.json"
|
|
35
|
-
},
|
|
36
|
-
"files": [
|
|
37
|
-
"dist"
|
|
38
|
-
],
|
|
39
|
-
"sideEffects": false,
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"@fulcro/transform-core": "^0.10.0"
|
|
42
|
-
},
|
|
43
|
-
"peerDependencies": {
|
|
44
|
-
"typescript": ">=5.3.3 <7"
|
|
45
|
-
},
|
|
46
|
-
"peerDependenciesMeta": {
|
|
47
|
-
"typescript": {
|
|
48
|
-
"optional": true
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
"engines": {
|
|
52
|
-
"node": ">=22"
|
|
53
|
-
},
|
|
54
|
-
"publishConfig": {
|
|
55
|
-
"access": "public"
|
|
56
|
-
},
|
|
57
|
-
"repository": {
|
|
58
|
-
"type": "git",
|
|
59
|
-
"url": "git+https://github.com/DigUu-RL/fulcro.git",
|
|
60
|
-
"directory": "packages/types"
|
|
61
|
-
},
|
|
62
|
-
"homepage": "https://github.com/DigUu-RL/fulcro/tree/main/packages/types#readme",
|
|
63
|
-
"bugs": {
|
|
64
|
-
"url": "https://github.com/DigUu-RL/fulcro/issues"
|
|
65
|
-
},
|
|
66
|
-
"scripts": {
|
|
67
|
-
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
68
|
-
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
69
|
-
"prepublishOnly": "npm run build"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@fulcro/types",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Numeric types with a defined range and layout: fixed-width integers, half, single and double precision floats, and a decimal128 Decimal.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"integer",
|
|
7
|
+
"float",
|
|
8
|
+
"decimal",
|
|
9
|
+
"decimal128",
|
|
10
|
+
"numeric",
|
|
11
|
+
"typescript"
|
|
12
|
+
],
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"author": "diguu <rodrigogeribola@hotmail.com>",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./transformer": {
|
|
23
|
+
"types": "./dist/transformer/index.d.ts",
|
|
24
|
+
"default": "./dist/transformer/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./unplugin": {
|
|
27
|
+
"types": "./dist/unplugin/index.d.mts",
|
|
28
|
+
"default": "./dist/unplugin/index.mjs"
|
|
29
|
+
},
|
|
30
|
+
"./language-service": {
|
|
31
|
+
"types": "./dist/languageService/index.d.ts",
|
|
32
|
+
"default": "./dist/languageService/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"sideEffects": false,
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@fulcro/transform-core": "^0.10.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"typescript": ">=5.3.3 <7"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"typescript": {
|
|
48
|
+
"optional": true
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=22"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/DigUu-RL/fulcro.git",
|
|
60
|
+
"directory": "packages/types"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/DigUu-RL/fulcro/tree/main/packages/types#readme",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/DigUu-RL/fulcro/issues"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
68
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
69
|
+
"prepublishOnly": "npm run build"
|
|
70
|
+
}
|
|
71
|
+
}
|