@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.
Files changed (51) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +66 -0
  3. package/dist/bigInteger/index.d.ts +27 -0
  4. package/dist/bigInteger/index.js +76 -0
  5. package/dist/brand/index.d.ts +25 -0
  6. package/dist/brand/index.js +2 -0
  7. package/dist/decimal/arithmetic.d.ts +99 -0
  8. package/dist/decimal/arithmetic.js +388 -0
  9. package/dist/decimal/format.d.ts +55 -0
  10. package/dist/decimal/format.js +195 -0
  11. package/dist/decimal/index.d.ts +296 -0
  12. package/dist/decimal/index.js +407 -0
  13. package/dist/decimal/parse.d.ts +15 -0
  14. package/dist/decimal/parse.js +80 -0
  15. package/dist/decimal/parts.d.ts +83 -0
  16. package/dist/decimal/parts.js +103 -0
  17. package/dist/decimal/round.d.ts +32 -0
  18. package/dist/decimal/round.js +132 -0
  19. package/dist/doublePrecisionFloat/index.d.ts +19 -0
  20. package/dist/doublePrecisionFloat/index.js +12 -0
  21. package/dist/float/index.d.ts +25 -0
  22. package/dist/float/index.js +61 -0
  23. package/dist/halfPrecisionFloat/index.d.ts +20 -0
  24. package/dist/halfPrecisionFloat/index.js +63 -0
  25. package/dist/index.d.ts +22 -0
  26. package/dist/index.js +29 -0
  27. package/dist/integer/index.d.ts +163 -0
  28. package/dist/integer/index.js +402 -0
  29. package/dist/languageService/index.d.ts +21 -0
  30. package/dist/languageService/index.js +23 -0
  31. package/dist/layout/index.d.ts +33 -0
  32. package/dist/layout/index.js +2 -0
  33. package/dist/numericType/index.d.ts +174 -0
  34. package/dist/numericType/index.js +2 -0
  35. package/dist/roundingMode/index.d.ts +30 -0
  36. package/dist/roundingMode/index.js +29 -0
  37. package/dist/signedInteger/index.d.ts +40 -0
  38. package/dist/signedInteger/index.js +34 -0
  39. package/dist/singlePrecisionFloat/index.d.ts +20 -0
  40. package/dist/singlePrecisionFloat/index.js +15 -0
  41. package/dist/transformer/classify/index.d.ts +39 -0
  42. package/dist/transformer/classify/index.js +84 -0
  43. package/dist/transformer/index.d.ts +27 -0
  44. package/dist/transformer/index.js +30 -0
  45. package/dist/transformer/rewriter/index.d.ts +3 -0
  46. package/dist/transformer/rewriter/index.js +374 -0
  47. package/dist/unplugin/index.d.mts +15 -0
  48. package/dist/unplugin/index.mjs +31 -0
  49. package/dist/unsignedInteger/index.d.ts +36 -0
  50. package/dist/unsignedInteger/index.js +34 -0
  51. package/package.json +71 -0
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnsignedInteger = exports.SinglePrecisionFloat = exports.SignedInteger = exports.HalfPrecisionFloat = exports.DoublePrecisionFloat = exports.Decimal = exports.BigInteger = void 0;
4
+ /**
5
+ * Public entry point of the package, matching the `main` and `types` fields of
6
+ * `package.json`.
7
+ *
8
+ * Listed one by one rather than re-exported wholesale, so that adding an export
9
+ * to a module below is never enough on its own to put it in front of consumers.
10
+ * The modules export more than this — `createIntegerType`, the parts and the
11
+ * rounding of `Decimal` — and none of it is a promise.
12
+ *
13
+ * Every type here has a value of the same name beside it. A consumer who wants
14
+ * only the types imports them with `import type` and takes no code at all.
15
+ */
16
+ var bigInteger_1 = require("./bigInteger/index.js");
17
+ Object.defineProperty(exports, "BigInteger", { enumerable: true, get: function () { return bigInteger_1.BigInteger; } });
18
+ var decimal_1 = require("./decimal/index.js");
19
+ Object.defineProperty(exports, "Decimal", { enumerable: true, get: function () { return decimal_1.Decimal; } });
20
+ var doublePrecisionFloat_1 = require("./doublePrecisionFloat/index.js");
21
+ Object.defineProperty(exports, "DoublePrecisionFloat", { enumerable: true, get: function () { return doublePrecisionFloat_1.DoublePrecisionFloat; } });
22
+ var halfPrecisionFloat_1 = require("./halfPrecisionFloat/index.js");
23
+ Object.defineProperty(exports, "HalfPrecisionFloat", { enumerable: true, get: function () { return halfPrecisionFloat_1.HalfPrecisionFloat; } });
24
+ var signedInteger_1 = require("./signedInteger/index.js");
25
+ Object.defineProperty(exports, "SignedInteger", { enumerable: true, get: function () { return signedInteger_1.SignedInteger; } });
26
+ var singlePrecisionFloat_1 = require("./singlePrecisionFloat/index.js");
27
+ Object.defineProperty(exports, "SinglePrecisionFloat", { enumerable: true, get: function () { return singlePrecisionFloat_1.SinglePrecisionFloat; } });
28
+ var unsignedInteger_1 = require("./unsignedInteger/index.js");
29
+ Object.defineProperty(exports, "UnsignedInteger", { enumerable: true, get: function () { return unsignedInteger_1.UnsignedInteger; } });
@@ -0,0 +1,163 @@
1
+ import type { BoundedNumericType } from '../numericType/index.js';
2
+ /**
3
+ * Machinery shared by `SignedInteger` and `UnsignedInteger`.
4
+ *
5
+ * The two differ only in where their range starts, so everything else — the
6
+ * range check, the modular conversion, the checked arithmetic — is written once
7
+ * here. Nothing in this module is exported by the package except the two types
8
+ * a signature needs: `IntegerWidth` and `IntegerType`.
9
+ */
10
+ /** The widths a fixed-width integer comes in, in bits. */
11
+ export type IntegerWidth = 8 | 16 | 32 | 64 | 128;
12
+ /** Size, and alignment, of each width in bytes. */
13
+ interface ByteSizes {
14
+ readonly 8: 1;
15
+ readonly 16: 2;
16
+ readonly 32: 4;
17
+ readonly 64: 8;
18
+ readonly 128: 16;
19
+ }
20
+ /**
21
+ * Size of an integer of a width, in bytes; its alignment is the same number,
22
+ * as it is for every fixed-width integer on the platforms that define one.
23
+ *
24
+ * @template N Width, in bits.
25
+ */
26
+ export type ByteSize<N extends IntegerWidth> = ByteSizes[N];
27
+ /**
28
+ * The primitive an integer of a width is carried by.
29
+ *
30
+ * Up to 32 bits a `number` holds every value exactly and costs nothing. From 64
31
+ * bits it cannot — a `number` is exact only up to 2^53 — so the value is a
32
+ * `bigint`, and a consumer sees that in the type rather than discovering it as
33
+ * a lost digit.
34
+ *
35
+ * @template N Width, in bits.
36
+ */
37
+ export type IntegerRepresentation<N extends IntegerWidth> = N extends 64 | 128 ? bigint : number;
38
+ /**
39
+ * Descriptor of a fixed-width integer type.
40
+ *
41
+ * Every operation is checked: a result outside the range throws a `RangeError`
42
+ * naming the operation, the value and the range, as a C# `checked` context
43
+ * does. `wrap` is the one exception, and the explicit way to ask for the
44
+ * modular behaviour of an `unchecked` conversion.
45
+ *
46
+ * @template T Type of the values this descriptor produces.
47
+ */
48
+ export interface IntegerType<T> extends BoundedNumericType<T, number | bigint> {
49
+ /** Width of the type, in bits. */
50
+ readonly width: IntegerWidth;
51
+ /** Whether the range includes negative values. */
52
+ readonly signed: boolean;
53
+ /**
54
+ * Converts an integer into this type modulo 2^width, as a two's complement
55
+ * truncation does. Never throws for an integer, whatever its size.
56
+ *
57
+ * ```ts
58
+ * SignedInteger(8).wrap(200); // -56
59
+ * UnsignedInteger(8).wrap(-1); // 255
60
+ * ```
61
+ *
62
+ * @param value Integer to convert.
63
+ * @returns The value congruent to it within the range.
64
+ * @throws {RangeError} When the value is a `number` that is not an integer.
65
+ */
66
+ wrap(value: number | bigint): T;
67
+ /**
68
+ * Divides, truncating the quotient towards zero as integer division does in
69
+ * C#, Java and `BigInt`.
70
+ *
71
+ * @param left Dividend.
72
+ * @param right Divisor.
73
+ * @returns The truncated quotient.
74
+ * @throws {RangeError} On a zero divisor, and on the one quotient a signed
75
+ * type cannot hold: its minimum divided by `-1`.
76
+ */
77
+ divide(left: T, right: T): T;
78
+ /**
79
+ * The bits set in both, as `&` computes them.
80
+ *
81
+ * @param left First operand.
82
+ * @param right Second operand.
83
+ * @returns The conjunction of the bits.
84
+ */
85
+ bitwiseAnd(left: T, right: T): T;
86
+ /**
87
+ * The bits set in either, as `|` computes them.
88
+ *
89
+ * @param left First operand.
90
+ * @param right Second operand.
91
+ * @returns The disjunction of the bits.
92
+ */
93
+ bitwiseOr(left: T, right: T): T;
94
+ /**
95
+ * The bits set in exactly one, as `^` computes them.
96
+ *
97
+ * @param left First operand.
98
+ * @param right Second operand.
99
+ * @returns The exclusive disjunction of the bits.
100
+ */
101
+ bitwiseXor(left: T, right: T): T;
102
+ /**
103
+ * Every bit of the width flipped, as `~` does.
104
+ *
105
+ * @param value Operand.
106
+ * @returns The complement, within the width.
107
+ */
108
+ bitwiseNot(value: T): T;
109
+ /**
110
+ * Shifts the bits towards the most significant end, as `<<` does. Bits
111
+ * shifted past the width are discarded: a shift is a bit operation, never
112
+ * an overflow.
113
+ *
114
+ * @param value Operand.
115
+ * @param count Places to shift, from 0 to `width - 1`.
116
+ * @returns The shifted value, within the width.
117
+ * @throws {RangeError} When the count is outside 0 … `width - 1`.
118
+ */
119
+ shiftLeft(value: T, count: T): T;
120
+ /**
121
+ * Shifts the bits towards the least significant end, as `>>` does: copying
122
+ * the sign bit in on a signed type, and zeros on an unsigned one.
123
+ *
124
+ * @param value Operand.
125
+ * @param count Places to shift, from 0 to `width - 1`.
126
+ * @returns The shifted value.
127
+ * @throws {RangeError} When the count is outside 0 … `width - 1`.
128
+ */
129
+ shiftRight(value: T, count: T): T;
130
+ /**
131
+ * Shifts the bits towards the least significant end with zeros coming in,
132
+ * as `>>>` does — on a signed type, over the bits of its own width, and read
133
+ * back as signed, as Java's `>>>` does.
134
+ *
135
+ * ```ts
136
+ * const Int32 = SignedInteger(32);
137
+ *
138
+ * Int32.shiftRightLogical(Int32.from(-1), Int32.from(28)); // 15
139
+ * ```
140
+ *
141
+ * @param value Operand.
142
+ * @param count Places to shift, from 0 to `width - 1`.
143
+ * @returns The shifted value.
144
+ * @throws {RangeError} When the count is outside 0 … `width - 1`.
145
+ */
146
+ shiftRightLogical(value: T, count: T): T;
147
+ }
148
+ /**
149
+ * Builds the descriptor of one signedness and width.
150
+ *
151
+ * Two implementations behind one interface: the widths a `number` holds work in
152
+ * `number` throughout and never touch a `bigint`, and the wider ones work in
153
+ * `bigint` throughout. Mixing the two would make every 32-bit addition pay for
154
+ * a conversion it never needed, which the performance suite counts.
155
+ *
156
+ * @param signed Whether the range includes negative values.
157
+ * @param width Width, in bits.
158
+ * @param name Name of the type, as it reads in an error message.
159
+ * @returns The descriptor.
160
+ * @throws {RangeError} When the width is not one of {@link IntegerWidth}.
161
+ */
162
+ export declare const createIntegerType: <T>(signed: boolean, width: IntegerWidth, name: string) => IntegerType<T>;
163
+ export {};
@@ -0,0 +1,402 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createIntegerType = void 0;
4
+ /** Every width, for validating the argument a descriptor is built from. */
5
+ const INTEGER_WIDTHS = [8, 16, 32, 64, 128];
6
+ /**
7
+ * Formats a range for an error message.
8
+ *
9
+ * @param minimum Smallest value.
10
+ * @param maximum Largest value.
11
+ * @returns The range, as `[minimum, maximum]`.
12
+ */
13
+ const describeRange = (minimum, maximum) => `[${minimum}, ${maximum}]`;
14
+ /**
15
+ * Describes a value for an error message without letting a stray `-0` read as
16
+ * `0` or a `bigint` lose its suffix.
17
+ *
18
+ * @param value Value being reported.
19
+ * @returns The value as the caller wrote it.
20
+ */
21
+ const describeValue = (value) => {
22
+ if (typeof value === 'bigint')
23
+ return `${value}n`;
24
+ if (Object.is(value, -0))
25
+ return '-0';
26
+ return String(value);
27
+ };
28
+ /**
29
+ * Fails a conversion whose input is not a number or a bigint at all.
30
+ *
31
+ * @param name Name of the type converting.
32
+ * @param operation Operation being performed.
33
+ * @param value Value it was handed.
34
+ * @returns Never.
35
+ */
36
+ const rejectKind = (name, operation, value) => {
37
+ throw new TypeError(`${name}.${operation}: expected a number or a bigint, received ${typeof value}.`);
38
+ };
39
+ /**
40
+ * Fails a conversion of a number that has a fractional part, or is not finite.
41
+ *
42
+ * @param name Name of the type converting.
43
+ * @param operation Operation being performed.
44
+ * @param value Value it was handed.
45
+ * @returns Never.
46
+ */
47
+ const rejectFraction = (name, operation, value) => {
48
+ throw new RangeError(`${name}.${operation}: expected an integer, received ${describeValue(value)}.`);
49
+ };
50
+ /**
51
+ * Fails a power whose exponent is negative, which has no integer result.
52
+ *
53
+ * @param name Name of the type.
54
+ * @param exponent Exponent it was handed.
55
+ * @returns Never.
56
+ */
57
+ const rejectNegativeExponent = (name, exponent) => {
58
+ throw new RangeError(`${name}.power: expected an exponent of zero or more, received ${describeValue(exponent)}.`);
59
+ };
60
+ /**
61
+ * Fails a shift whose count is outside the width.
62
+ *
63
+ * @param name Name of the type.
64
+ * @param operation Shift being performed.
65
+ * @param width Width of the type.
66
+ * @param count Count it was handed.
67
+ * @returns Never.
68
+ */
69
+ const rejectCount = (name, operation, width, count) => {
70
+ throw new RangeError(`${name}.${operation}: expected a count from 0 to ${width - 1}, received ${describeValue(count)}.`);
71
+ };
72
+ /**
73
+ * Builds the descriptor of one signedness and width.
74
+ *
75
+ * Two implementations behind one interface: the widths a `number` holds work in
76
+ * `number` throughout and never touch a `bigint`, and the wider ones work in
77
+ * `bigint` throughout. Mixing the two would make every 32-bit addition pay for
78
+ * a conversion it never needed, which the performance suite counts.
79
+ *
80
+ * @param signed Whether the range includes negative values.
81
+ * @param width Width, in bits.
82
+ * @param name Name of the type, as it reads in an error message.
83
+ * @returns The descriptor.
84
+ * @throws {RangeError} When the width is not one of {@link IntegerWidth}.
85
+ */
86
+ const createIntegerType = (signed, width, name) => {
87
+ if (!INTEGER_WIDTHS.includes(width)) {
88
+ throw new RangeError(`${signed ? 'SignedInteger' : 'UnsignedInteger'}: expected a width of ${INTEGER_WIDTHS.join(', ')} bits, received ${describeValue(width)}.`);
89
+ }
90
+ const minimum = signed ? -(1n << BigInt(width - 1)) : 0n;
91
+ const maximum = signed
92
+ ? (1n << BigInt(width - 1)) - 1n
93
+ : (1n << BigInt(width)) - 1n;
94
+ return width >= 64
95
+ ? createBigIntegerType(signed, width, name, minimum, maximum)
96
+ : createNumberIntegerType(signed, width, name, minimum, maximum);
97
+ };
98
+ exports.createIntegerType = createIntegerType;
99
+ /**
100
+ * Builds the descriptor of a width carried by a `number`.
101
+ *
102
+ * @param signed Whether the range includes negative values.
103
+ * @param width Width, in bits.
104
+ * @param name Name of the type.
105
+ * @param minimum Smallest value.
106
+ * @param maximum Largest value.
107
+ * @returns The descriptor.
108
+ */
109
+ const createNumberIntegerType = (signed, width, name, minimum, maximum) => {
110
+ const low = Number(minimum);
111
+ const high = Number(maximum);
112
+ const range = describeRange(minimum, maximum);
113
+ /**
114
+ * Accepts a result that fits, normalising `-0` to `0`.
115
+ *
116
+ * `-0` fits every range, but an integer has one zero: letting the negative
117
+ * one through would make `Object.is` tell two equal integers apart.
118
+ *
119
+ * @param operation Operation that produced the value.
120
+ * @param value Exact result of the operation.
121
+ * @returns The value, as this type.
122
+ */
123
+ const accept = (operation, value) => {
124
+ if (value < low || value > high) {
125
+ throw new RangeError(`${name}.${operation}: ${describeValue(value)} is outside ${range}.`);
126
+ }
127
+ return (value === 0 ? 0 : value);
128
+ };
129
+ // Two's complement truncation, done by the engine's own 32-bit conversions:
130
+ // `| 0` and `>>> 0` reduce any finite integer modulo 2^32 exactly, and the
131
+ // shifts then sign-extend or mask down to the narrower widths.
132
+ const truncate = width === 32
133
+ ? signed
134
+ ? (value) => value | 0
135
+ : (value) => value >>> 0
136
+ : signed
137
+ ? (value) => (value << (32 - width)) >> (32 - width)
138
+ : (value) => value & (2 ** width - 1);
139
+ /**
140
+ * Refuses a zero divisor.
141
+ *
142
+ * @param operation Operation being performed.
143
+ * @param divisor Divisor it was handed.
144
+ */
145
+ const requireDivisor = (operation, divisor) => {
146
+ if (divisor === 0) {
147
+ throw new RangeError(`${name}.${operation}: division by zero.`);
148
+ }
149
+ };
150
+ /** The bits of the width, for reading a narrow signed value as unsigned. */
151
+ const mask = width === 32 ? 0xffff_ffff : 2 ** width - 1;
152
+ /**
153
+ * Accepts a shift count within the width.
154
+ *
155
+ * @param operation Shift being performed.
156
+ * @param count Count it was handed.
157
+ * @returns The count.
158
+ */
159
+ const requireCount = (operation, count) => {
160
+ const places = count;
161
+ if (places < 0 || places >= width) {
162
+ return rejectCount(name, operation, width, places);
163
+ }
164
+ return places;
165
+ };
166
+ return {
167
+ name,
168
+ width,
169
+ signed,
170
+ minimum: low,
171
+ maximum: high,
172
+ from: (value) => {
173
+ if (typeof value === 'bigint') {
174
+ if (value < minimum || value > maximum) {
175
+ throw new RangeError(`${name}.from: ${describeValue(value)} is outside ${range}.`);
176
+ }
177
+ return Number(value);
178
+ }
179
+ if (typeof value !== 'number')
180
+ return rejectKind(name, 'from', value);
181
+ if (!Number.isInteger(value))
182
+ return rejectFraction(name, 'from', value);
183
+ return accept('from', value);
184
+ },
185
+ wrap: (value) => {
186
+ if (typeof value === 'bigint') {
187
+ return Number(signed ? BigInt.asIntN(width, value) : BigInt.asUintN(width, value));
188
+ }
189
+ if (typeof value !== 'number')
190
+ return rejectKind(name, 'wrap', value);
191
+ if (!Number.isInteger(value))
192
+ return rejectFraction(name, 'wrap', value);
193
+ const wrapped = truncate(value);
194
+ return (wrapped === 0 ? 0 : wrapped);
195
+ },
196
+ is: (value) => typeof value === 'number' &&
197
+ Number.isInteger(value) &&
198
+ value >= low &&
199
+ value <= high,
200
+ add: (left, right) => accept('add', left + right),
201
+ subtract: (left, right) => accept('subtract', left - right),
202
+ // Exact below 2^53, and any product at or beyond it is already out of
203
+ // every range carried by a `number`, rounded or not — so checking the
204
+ // rounded product decides the exact one correctly.
205
+ multiply: (left, right) => accept('multiply', left * right),
206
+ // For operands under 2^32 the quotient sits at least 1/|dividend| away
207
+ // from the next integer, which is further than a double can round, so
208
+ // truncating the floating quotient gives the integer one.
209
+ divide: (left, right) => {
210
+ requireDivisor('divide', right);
211
+ return accept('divide', Math.trunc(left / right));
212
+ },
213
+ remainder: (left, right) => {
214
+ requireDivisor('remainder', right);
215
+ return accept('remainder', left % right);
216
+ },
217
+ // By squaring, each product checked as `multiply` checks it. The base is
218
+ // only squared while a higher bit of the exponent remains, and then the
219
+ // result will include that square as a factor — so a square out of range
220
+ // means a result out of range, never a false alarm.
221
+ power: (base, exponent) => {
222
+ let remaining = exponent;
223
+ if (remaining < 0)
224
+ return rejectNegativeExponent(name, remaining);
225
+ let result = 1;
226
+ let factor = base;
227
+ while (remaining > 0) {
228
+ if (remaining % 2 === 1) {
229
+ result = accept('power', result * factor);
230
+ }
231
+ remaining = Math.floor(remaining / 2);
232
+ if (remaining > 0)
233
+ factor = accept('power', factor * factor);
234
+ }
235
+ return accept('power', result);
236
+ },
237
+ negate: (value) => accept('negate', -value),
238
+ increment: (value) => accept('increment', value + 1),
239
+ decrement: (value) => accept('decrement', value - 1),
240
+ equals: (left, right) => left === right,
241
+ lessThan: (left, right) => left < right,
242
+ lessThanOrEqual: (left, right) => left <= right,
243
+ greaterThan: (left, right) => left > right,
244
+ greaterThanOrEqual: (left, right) => left >= right,
245
+ // Two values in range combine, through the engine's 32-bit operators,
246
+ // into a value whose low `width` bits are the answer; truncating keeps
247
+ // exactly those, sign-extended or masked as the type is.
248
+ bitwiseAnd: (left, right) => truncate(left & right),
249
+ bitwiseOr: (left, right) => truncate(left | right),
250
+ bitwiseXor: (left, right) => truncate(left ^ right),
251
+ bitwiseNot: (value) => truncate(~value),
252
+ shiftLeft: (value, count) => truncate(value << requireCount('shiftLeft', count)),
253
+ // Arithmetic on a signed type, whose values are already sign-correct
254
+ // numbers; on an unsigned one the value is non-negative, so the two
255
+ // shifts agree except at 32 bits, where only `>>>` reads it as unsigned.
256
+ shiftRight: (value, count) => {
257
+ const places = requireCount('shiftRight', count);
258
+ return (signed ? value >> places : value >>> places);
259
+ },
260
+ shiftRightLogical: (value, count) => {
261
+ const places = requireCount('shiftRightLogical', count);
262
+ const bits = width === 32 ? value >>> 0 : value & mask;
263
+ return truncate(bits >>> places);
264
+ },
265
+ };
266
+ };
267
+ /**
268
+ * Builds the descriptor of a width carried by a `bigint`.
269
+ *
270
+ * @param signed Whether the range includes negative values.
271
+ * @param width Width, in bits.
272
+ * @param name Name of the type.
273
+ * @param minimum Smallest value.
274
+ * @param maximum Largest value.
275
+ * @returns The descriptor.
276
+ */
277
+ const createBigIntegerType = (signed, width, name, minimum, maximum) => {
278
+ const range = describeRange(minimum, maximum);
279
+ /**
280
+ * Accepts a result that fits.
281
+ *
282
+ * @param operation Operation that produced the value.
283
+ * @param value Exact result of the operation.
284
+ * @param reported The value as the caller wrote it, when it arrived as a
285
+ * `number` and was converted: the message repeats what was passed.
286
+ * @returns The value, as this type.
287
+ */
288
+ const accept = (operation, value, reported = value) => {
289
+ if (value < minimum || value > maximum) {
290
+ throw new RangeError(`${name}.${operation}: ${describeValue(reported)} is outside ${range}.`);
291
+ }
292
+ return value;
293
+ };
294
+ /**
295
+ * Turns an accepted input into a `bigint`.
296
+ *
297
+ * @param operation Operation being performed.
298
+ * @param value Value it was handed.
299
+ * @returns The value, as a `bigint`.
300
+ */
301
+ const toBigInt = (operation, value) => {
302
+ if (typeof value === 'bigint')
303
+ return value;
304
+ if (typeof value !== 'number')
305
+ return rejectKind(name, operation, value);
306
+ if (!Number.isInteger(value))
307
+ return rejectFraction(name, operation, value);
308
+ return BigInt(value);
309
+ };
310
+ /**
311
+ * Refuses a zero divisor, which `bigint` would also refuse, but without
312
+ * saying which type or operation it was.
313
+ *
314
+ * @param operation Operation being performed.
315
+ * @param divisor Divisor it was handed.
316
+ */
317
+ const requireDivisor = (operation, divisor) => {
318
+ if (divisor === 0n) {
319
+ throw new RangeError(`${name}.${operation}: division by zero.`);
320
+ }
321
+ };
322
+ /**
323
+ * Keeps the low `width` bits of a result, sign-extended or not as the type
324
+ * is.
325
+ *
326
+ * @param value Result of a bit operation, of any size.
327
+ * @returns The value, as this type.
328
+ */
329
+ const wrapBits = (value) => (signed ? BigInt.asIntN(width, value) : BigInt.asUintN(width, value));
330
+ /**
331
+ * Accepts a shift count within the width.
332
+ *
333
+ * @param operation Shift being performed.
334
+ * @param count Count it was handed.
335
+ * @returns The count.
336
+ */
337
+ const requireCount = (operation, count) => {
338
+ const places = count;
339
+ if (places < 0n || places >= BigInt(width)) {
340
+ return rejectCount(name, operation, width, places);
341
+ }
342
+ return places;
343
+ };
344
+ return {
345
+ name,
346
+ width,
347
+ signed,
348
+ minimum: minimum,
349
+ maximum: maximum,
350
+ from: (value) => accept('from', toBigInt('from', value), value),
351
+ wrap: (value) => {
352
+ const integer = toBigInt('wrap', value);
353
+ return (signed ? BigInt.asIntN(width, integer) : BigInt.asUintN(width, integer));
354
+ },
355
+ is: (value) => typeof value === 'bigint' && value >= minimum && value <= maximum,
356
+ add: (left, right) => accept('add', left + right),
357
+ subtract: (left, right) => accept('subtract', left - right),
358
+ multiply: (left, right) => accept('multiply', left * right),
359
+ // `bigint` division already truncates towards zero.
360
+ divide: (left, right) => {
361
+ requireDivisor('divide', right);
362
+ return accept('divide', left / right);
363
+ },
364
+ remainder: (left, right) => {
365
+ requireDivisor('remainder', right);
366
+ return accept('remainder', left % right);
367
+ },
368
+ // Any base of magnitude two or more overflows every width once the
369
+ // exponent reaches that width, so a large exponent is refused before the
370
+ // power is built — `2n ** 1_000_000_000n` would otherwise spend its time
371
+ // building a number only to reject it.
372
+ power: (base, exponent) => {
373
+ const power = exponent;
374
+ const value = base;
375
+ if (power < 0n)
376
+ return rejectNegativeExponent(name, power);
377
+ const magnitude = value < 0n ? -value : value;
378
+ if (magnitude >= 2n && power >= BigInt(width)) {
379
+ throw new RangeError(`${name}.power: ${describeValue(value)} ** ${describeValue(power)} is outside ${range}.`);
380
+ }
381
+ return accept('power', value ** power);
382
+ },
383
+ negate: (value) => accept('negate', -value),
384
+ increment: (value) => accept('increment', value + 1n),
385
+ decrement: (value) => accept('decrement', value - 1n),
386
+ equals: (left, right) => left === right,
387
+ lessThan: (left, right) => left < right,
388
+ lessThanOrEqual: (left, right) => left <= right,
389
+ greaterThan: (left, right) => left > right,
390
+ greaterThanOrEqual: (left, right) => left >= right,
391
+ bitwiseAnd: (left, right) => wrapBits(left & right),
392
+ bitwiseOr: (left, right) => wrapBits(left | right),
393
+ bitwiseXor: (left, right) => wrapBits(left ^ right),
394
+ bitwiseNot: (value) => wrapBits(~value),
395
+ shiftLeft: (value, count) => wrapBits(value << requireCount('shiftLeft', count)),
396
+ // `bigint` shifts are arithmetic, which is right for a signed type and,
397
+ // for the non-negative values of an unsigned one, the same as logical.
398
+ shiftRight: (value, count) => (value >> requireCount('shiftRight', count)),
399
+ shiftRightLogical: (value, count) => wrapBits(BigInt.asUintN(width, value) >>
400
+ requireCount('shiftRightLogical', count)),
401
+ };
402
+ };
@@ -0,0 +1,21 @@
1
+ import { type LanguageServicePlugin } from '@fulcro/transform-core';
2
+ /**
3
+ * The editor plugin giving the operators their meaning on this package's
4
+ * numeric types: the types, the hover, the completions and the errors an editor
5
+ * shows are those of the rewritten code, mapped back onto the code as written.
6
+ *
7
+ * ```json
8
+ * {
9
+ * "compilerOptions": {
10
+ * "plugins": [{ "name": "@fulcro/types/language-service" }]
11
+ * }
12
+ * }
13
+ * ```
14
+ *
15
+ * VS Code loads it only from the workspace's own TypeScript: select it with
16
+ * **TypeScript: Select TypeScript Version → Use Workspace Version**. An editor
17
+ * that does not load it shows what `tsc` alone would — the operators on a
18
+ * `Decimal` underlined, the others typed as `number`.
19
+ */
20
+ declare const plugin: LanguageServicePlugin;
21
+ export = plugin;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ const transform_core_1 = require("@fulcro/transform-core");
3
+ const rewriter_1 = require("../transformer/rewriter/index.js");
4
+ /**
5
+ * The editor plugin giving the operators their meaning on this package's
6
+ * numeric types: the types, the hover, the completions and the errors an editor
7
+ * shows are those of the rewritten code, mapped back onto the code as written.
8
+ *
9
+ * ```json
10
+ * {
11
+ * "compilerOptions": {
12
+ * "plugins": [{ "name": "@fulcro/types/language-service" }]
13
+ * }
14
+ * }
15
+ * ```
16
+ *
17
+ * VS Code loads it only from the workspace's own TypeScript: select it with
18
+ * **TypeScript: Select TypeScript Version → Use Workspace Version**. An editor
19
+ * that does not load it shows what `tsc` alone would — the operators on a
20
+ * `Decimal` underlined, the others typed as `number`.
21
+ */
22
+ const plugin = (0, transform_core_1.createLanguageServicePlugin)(rewriter_1.OPERATOR_REWRITER);
23
+ module.exports = plugin;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The memory layout a type declares, carried by the type and never by a value.
3
+ *
4
+ * ```ts
5
+ * type SignedInteger<32> = … & Layout<4, 4>;
6
+ * ```
7
+ *
8
+ * This is a protocol between packages rather than a type either of them owns.
9
+ * `sizeOf<T>()` and `alignOf<T>()` in `@fulcro/reflect` read the property
10
+ * through the type checker, at compile time, and constrain `T` to carry it — so
11
+ * `sizeOf<BigInteger>()`, which has no fixed layout, is a type error rather
12
+ * than a wrong number. Neither package imports the other for this: the shape is
13
+ * the whole agreement, which is also what lets a later value type declare its
14
+ * own layout without depending on this package.
15
+ *
16
+ * The key is a string with a leading `~`, not a symbol, because a symbol would
17
+ * have to be imported to be named, and naming it is exactly what the other side
18
+ * of the protocol needs to do. The tilde sorts the key after every real member
19
+ * in an editor's completion list — the convention Standard Schema settled on
20
+ * for its `~standard` property, for the same reason.
21
+ *
22
+ * The property is declared, never assigned: reading it at runtime yields
23
+ * `undefined`.
24
+ *
25
+ * @template TSize Size of the type, in bytes.
26
+ * @template TAlignment Alignment of the type, in bytes.
27
+ */
28
+ export interface Layout<TSize extends number, TAlignment extends number> {
29
+ readonly '~layout': {
30
+ readonly size: TSize;
31
+ readonly alignment: TAlignment;
32
+ };
33
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });