@williamthorsen/toolbelt.numbers 4.3.7 → 5.0.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/CHANGELOG.md +55 -0
- package/LICENSE +4 -4
- package/README.md +13 -1
- package/dist/esm/2-draft/clamp.js +2 -2
- package/dist/esm/3-candidate/scale.js +4 -4
- package/dist/esm/3-candidate/seeded-rng.d.ts +4 -4
- package/dist/esm/3-candidate/seeded-rng.js +13 -13
- package/dist/esm/internal/IntegerSeed.js +2 -2
- package/dist/esm/internal/evaluateSeed.d.ts +1 -1
- package/dist/esm/internal/evaluateSeed.js +2 -2
- package/dist/esm/internal/getFakeMathRandom.js +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,61 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 5.0.0 — 2026-08-08
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- 🚨 **Breaking:** Spawn seeded number generator of same subclass (#84)
|
|
10
|
+
|
|
11
|
+
A seeded generator spawned from a subclass now matches that subclass, so `IntSeededRng.withSeed` supplies the wrapped function integers where it previously supplied floats. Callers relying on values derived through that path must re-baseline. A detached reference such as `const { withSeed } = SeededRng` now throws.
|
|
12
|
+
|
|
13
|
+
- Use underscore separator at 4 digits or more
|
|
14
|
+
|
|
15
|
+
Changes the `unicorn/numeric-separators-style` rule config so that separators are consistently used in base 10 numbers, instead of exempting numbers of 5 digits or less.
|
|
16
|
+
|
|
17
|
+
### Refactoring
|
|
18
|
+
|
|
19
|
+
- Fixes violations surfaced by newly active lint rules (#84)
|
|
20
|
+
- Fix slug punctuation and require safe integers (#86)
|
|
21
|
+
|
|
22
|
+
- Fixes an issue where the use of certain letters as the slug separator in `slugify` would leave punctuation marks in the result.
|
|
23
|
+
- Time-unit conversions, scaling range bounds, normal-distribution interval counts, and array indices in object paths now reject values too large to represent exactly instead of silently losing precision.
|
|
24
|
+
- Seeded number generators now produce distinct sequences for seeds at or beyond 2^53, where adjacent seeds previously collapsed onto nearly identical output. A seed of that size saved before this release no longer reproduces the same output.
|
|
25
|
+
|
|
26
|
+
- Fix lint
|
|
27
|
+
|
|
28
|
+
### Tooling
|
|
29
|
+
|
|
30
|
+
- Migrate Vitest configs to the nmr projects model (#73)
|
|
31
|
+
|
|
32
|
+
Packages no longer need to declare their own Vitest config. Test suites are now selected by a test file's name suffix rather than by choosing a config file: `*.app.test.ts` and `*.int.test.ts` route to the app and integration suites, and everything else runs as a unit test. Local development is now declared to require Node 24.16 or later.
|
|
33
|
+
|
|
34
|
+
- Adopt the mechanical-syntax deferred unicorn rules (#82)
|
|
35
|
+
|
|
36
|
+
Promotes ten deferred lint rules from warnings to errors and fixes the issues surfaced by those rules. One published behavior changes as well: Converting a Map to a plain object no longer drops an entry keyed `__proto__`.
|
|
37
|
+
|
|
38
|
+
- Adopt four deferred unicorn lint rules (#90)
|
|
39
|
+
|
|
40
|
+
The strict-lint severity cap of four lint rules has been raised to error: `no-array-from-fill`, `no-return-array-push`, `no-unreadable-array-destructuring`, and `prefer-math-constants`.
|
|
41
|
+
|
|
42
|
+
- Use identical compiler settings for all packages (#105)
|
|
43
|
+
|
|
44
|
+
All packages now have identical compiler settings, using the settings from the `@williamthorsen/tsconfig` base config without modification.
|
|
45
|
+
|
|
46
|
+
### Dependencies
|
|
47
|
+
|
|
48
|
+
- Upgrade all deps to latest version
|
|
49
|
+
|
|
50
|
+
## 4.3.8 — 2026-07-27
|
|
51
|
+
|
|
52
|
+
### Tooling
|
|
53
|
+
|
|
54
|
+
- Normalize Vitest, and lint configs
|
|
55
|
+
|
|
56
|
+
### Documentation
|
|
57
|
+
|
|
58
|
+
- Change license to ISC
|
|
59
|
+
|
|
5
60
|
## 4.3.7 — 2026-07-24
|
|
6
61
|
|
|
7
62
|
### Tooling
|
package/LICENSE
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
ISC License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2023-
|
|
3
|
+
Copyright (c) 2023-2026 William Thorsen
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The software is provided "as is" and the author disclaims all warranties with regard to this software including all implied warranties of merchantability and fitness. In no event shall the author be liable for any special, direct, indirect, or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software.
|
package/README.md
CHANGED
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Utility functions for working with numbers.
|
|
4
4
|
|
|
5
|
-
<!-- section:release-notes
|
|
5
|
+
<!-- section:release-notes -->
|
|
6
|
+
## Release notes — v5.0.0 (2026-08-08)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- 🚨 **Breaking:** Spawn seeded number generator of same subclass (#84)
|
|
11
|
+
|
|
12
|
+
A seeded generator spawned from a subclass now matches that subclass, so `IntSeededRng.withSeed` supplies the wrapped function integers where it previously supplied floats. Callers relying on values derived through that path must re-baseline. A detached reference such as `const { withSeed } = SeededRng` now throws.
|
|
13
|
+
|
|
14
|
+
- Use underscore separator at 4 digits or more
|
|
15
|
+
|
|
16
|
+
Changes the `unicorn/numeric-separators-style` rule config so that separators are consistently used in base 10 numbers, instead of exempting numbers of 5 digits or less.
|
|
17
|
+
<!-- /section:release-notes -->
|
|
6
18
|
|
|
7
19
|
## Installation
|
|
8
20
|
|
|
@@ -2,7 +2,7 @@ export function clamp(value, { min, max }) {
|
|
|
2
2
|
if (min !== undefined && max !== undefined && min > max) {
|
|
3
3
|
throw new RangeError('Minimum value cannot be greater than maximum value');
|
|
4
4
|
}
|
|
5
|
-
const minValue = min ??
|
|
6
|
-
const maxValue = max ??
|
|
5
|
+
const minValue = min ?? -Infinity;
|
|
6
|
+
const maxValue = max ?? Infinity;
|
|
7
7
|
return Math.max(minValue, Math.min(maxValue, value));
|
|
8
8
|
}
|
|
@@ -7,12 +7,12 @@ export function scale(value, toRange, fromRange = {}) {
|
|
|
7
7
|
return toMin + (offset * toMagnitude) / fromMagnitude;
|
|
8
8
|
}
|
|
9
9
|
export function scaleInt(value, toRange, fromRange = {}) {
|
|
10
|
-
if (
|
|
11
|
-
throw new RangeError('Invalid range: min and max must be integers.');
|
|
10
|
+
if (isNotSafeInteger(toRange.min) || isNotSafeInteger(toRange.max)) {
|
|
11
|
+
throw new RangeError('Invalid range: min and max must be safe integers.');
|
|
12
12
|
}
|
|
13
13
|
const scaled = scale(value, toRange, fromRange);
|
|
14
14
|
return Math.round(scaled);
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
return num !== undefined && !Number.
|
|
16
|
+
function isNotSafeInteger(num) {
|
|
17
|
+
return num !== undefined && !Number.isSafeInteger(num);
|
|
18
18
|
}
|
|
@@ -3,6 +3,10 @@ export declare class SeededRng implements SeededGenerator {
|
|
|
3
3
|
private _seed;
|
|
4
4
|
private baseSeed;
|
|
5
5
|
private nIncrements;
|
|
6
|
+
constructor(seed?: Seed);
|
|
7
|
+
get maxBase(): number;
|
|
8
|
+
get rng(): () => number;
|
|
9
|
+
get seed(): number;
|
|
6
10
|
static evaluateSeed(seed?: Seed): number | undefined;
|
|
7
11
|
static clone<T extends ThisConstructor<typeof SeededRng>>(this: T, seed: undefined, nIncrements?: number): undefined;
|
|
8
12
|
static clone<T extends ThisConstructor<typeof SeededRng>>(this: T, seed: Seed, nIncrements?: number): This<T>;
|
|
@@ -12,10 +16,6 @@ export declare class SeededRng implements SeededGenerator {
|
|
|
12
16
|
static spawn<T extends ThisConstructor<typeof SeededRng>>(this: T, seed: Seed): This<T>;
|
|
13
17
|
static spawn<T extends ThisConstructor<typeof SeededRng>>(this: T, seed: Seed | undefined): This<T> | undefined;
|
|
14
18
|
static withSeed<TOptions extends object, R>(fn: (options?: OptionsWithSeed<TOptions> | OptionsWithSeed<EmptyObject>) => R, seed: Seed | undefined): (options?: TOptions) => R;
|
|
15
|
-
constructor(seed?: Seed);
|
|
16
|
-
get maxBase(): number;
|
|
17
|
-
get rng(): () => number;
|
|
18
|
-
get seed(): number;
|
|
19
19
|
clone<T extends SeededRng>(this: T, nIncrements?: number): T;
|
|
20
20
|
increment(nIncrements?: number): this;
|
|
21
21
|
next(n?: number): number;
|
|
@@ -8,6 +8,18 @@ export class SeededRng {
|
|
|
8
8
|
_seed = 0;
|
|
9
9
|
baseSeed = 0;
|
|
10
10
|
nIncrements = 0;
|
|
11
|
+
constructor(seed) {
|
|
12
|
+
this.initializeSeeds(SeededRng.evaluateSeed(seed));
|
|
13
|
+
}
|
|
14
|
+
get maxBase() {
|
|
15
|
+
return IntegerSeed.max;
|
|
16
|
+
}
|
|
17
|
+
get rng() {
|
|
18
|
+
return () => this.next();
|
|
19
|
+
}
|
|
20
|
+
get seed() {
|
|
21
|
+
return this._seed;
|
|
22
|
+
}
|
|
11
23
|
static evaluateSeed(seed) {
|
|
12
24
|
return evaluateSeed(seed);
|
|
13
25
|
}
|
|
@@ -24,23 +36,11 @@ export class SeededRng {
|
|
|
24
36
|
return seed === undefined ? undefined : new this(seed);
|
|
25
37
|
}
|
|
26
38
|
static withSeed(fn, seed) {
|
|
27
|
-
const spawnedRng =
|
|
39
|
+
const spawnedRng = this.spawn(seed);
|
|
28
40
|
return function (options) {
|
|
29
41
|
return fn({ ...options, seed: spawnedRng });
|
|
30
42
|
};
|
|
31
43
|
}
|
|
32
|
-
constructor(seed) {
|
|
33
|
-
this.initializeSeeds(SeededRng.evaluateSeed(seed));
|
|
34
|
-
}
|
|
35
|
-
get maxBase() {
|
|
36
|
-
return IntegerSeed.max;
|
|
37
|
-
}
|
|
38
|
-
get rng() {
|
|
39
|
-
return () => this.next();
|
|
40
|
-
}
|
|
41
|
-
get seed() {
|
|
42
|
-
return this._seed;
|
|
43
|
-
}
|
|
44
44
|
clone(nIncrements = 0) {
|
|
45
45
|
return new this.constructor(this._seed).increment(nIncrements);
|
|
46
46
|
}
|
|
@@ -12,11 +12,11 @@ export const IntegerSeed = {
|
|
|
12
12
|
};
|
|
13
13
|
function toIntegerSeed(value) {
|
|
14
14
|
let integer = (() => {
|
|
15
|
-
if (Number.
|
|
15
|
+
if (Number.isSafeInteger(value))
|
|
16
16
|
return value;
|
|
17
17
|
return Math.floor(getFakeMathRandom(value) * MAX_INTEGER);
|
|
18
18
|
})();
|
|
19
|
-
integer
|
|
19
|
+
integer %= MAX_INTEGER;
|
|
20
20
|
if (integer <= 0) {
|
|
21
21
|
integer += MAX_INTEGER;
|
|
22
22
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare function evaluateSeed(seed: Seed | undefined): number | undefined;
|
|
2
2
|
export declare function checkIsRngLike(seed: Seed | undefined): seed is SeededGenerator;
|
|
3
3
|
export interface SeededGenerator {
|
|
4
|
-
next(): number;
|
|
5
4
|
seed: number;
|
|
5
|
+
next(): number;
|
|
6
6
|
}
|
|
7
7
|
export type Seed = number | SeededGenerator | (() => number);
|
|
@@ -2,10 +2,10 @@ export function evaluateSeed(seed) {
|
|
|
2
2
|
if (typeof seed === 'number') {
|
|
3
3
|
return seed;
|
|
4
4
|
}
|
|
5
|
-
|
|
5
|
+
if (checkIsRngLike(seed)) {
|
|
6
6
|
return seed.next();
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
if (typeof seed === 'function') {
|
|
9
9
|
return seed();
|
|
10
10
|
}
|
|
11
11
|
return undefined;
|
|
@@ -4,12 +4,12 @@ export function getFakeMathRandom(seed) {
|
|
|
4
4
|
for (const char of strSeed) {
|
|
5
5
|
const code = char.codePointAt(0) ?? 0;
|
|
6
6
|
hash = (hash << 5) - hash + code;
|
|
7
|
-
hash
|
|
7
|
+
hash |= 0;
|
|
8
8
|
}
|
|
9
9
|
hash += hash << 10;
|
|
10
10
|
hash ^= hash >> 6;
|
|
11
11
|
hash += hash << 3;
|
|
12
12
|
hash ^= hash >> 11;
|
|
13
13
|
hash += hash << 15;
|
|
14
|
-
return (hash &
|
|
14
|
+
return (hash & 0x7fff_ffff) / 0x7fff_ffff;
|
|
15
15
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/toolbelt.numbers",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "API",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/williamthorsen/toolbelt/tree/main/packages/numbers#readme",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"url": "git+https://github.com/williamthorsen/toolbelt.git",
|
|
13
13
|
"directory": "packages/numbers"
|
|
14
14
|
},
|
|
15
|
-
"license": "
|
|
15
|
+
"license": "ISC",
|
|
16
16
|
"author": "William Thorsen <william@thorsen.dev> (https://github.com/williamthorsen)",
|
|
17
17
|
"type": "module",
|
|
18
18
|
"exports": {
|