@williamthorsen/toolbelt.numbers 7.0.3 → 7.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/.readyup/kits/default.js +48 -18
- package/.readyup/manifest.json +32 -17
- package/CHANGELOG.md +127 -69
- package/README.md +40 -9
- package/dist/esm/1-proposed/makeRng.d.ts +6 -1
- package/dist/esm/3-candidate/clamp.d.ts +8 -0
- package/dist/esm/3-candidate/generateRandom.d.ts +8 -1
- package/dist/esm/3-candidate/integer-string.d.ts +19 -0
- package/dist/esm/3-candidate/numeric-string.d.ts +21 -0
- package/dist/esm/3-candidate/pickInteger.d.ts +10 -0
- package/dist/esm/3-candidate/pickInteger.js +0 -3
- package/dist/esm/3-candidate/round.d.ts +9 -0
- package/dist/esm/3-candidate/scale.d.ts +18 -0
- package/dist/esm/3-candidate/seeded-rng.d.ts +23 -1
- package/dist/esm/internal/IntegerSeed.d.ts +5 -0
- package/dist/esm/internal/computeFakeMathRandom.d.ts +5 -0
- package/dist/esm/internal/computeFakeMathRandom.js +1 -1
- package/dist/esm/internal/evaluateSeed.d.ts +13 -0
- package/dist/esm/internal/wrapSum.d.ts +5 -0
- package/package.json +4 -4
package/.readyup/kits/default.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @noformat -- @generated. Do not edit. Compiled by rdy. */
|
|
2
2
|
/* eslint-disable */
|
|
3
|
-
export const __readyupVersion = "0.
|
|
3
|
+
export const __readyupVersion = "0.36.0";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
// ../adoption/src/conventions/path-predicates.ts
|
|
@@ -50,12 +50,17 @@ import {
|
|
|
50
50
|
countPackageUsage,
|
|
51
51
|
readTrackedSources
|
|
52
52
|
} from "readyup/check-utils";
|
|
53
|
-
var NOT_A_REPO = "the project is not a git working tree, and these checks read the files git tracks";
|
|
53
|
+
var NOT_A_REPO = "the project is not a git working tree, and these checks read the files that git tracks";
|
|
54
54
|
var NOTHING_TO_REPORT = { findings: [] };
|
|
55
55
|
function defineAdoptionKit(spec) {
|
|
56
56
|
assertCheckIdsAreUnique();
|
|
57
57
|
const cache = {};
|
|
58
58
|
const adoptedPackage = { exportNames: spec.exportNames, packageName: spec.packageName };
|
|
59
|
+
const kitScope = { noSourcesReason: spec.noSourcesReason, pathFilter: spec.pathFilter };
|
|
60
|
+
const pathFiltersByKind = mapPathFiltersByKind();
|
|
61
|
+
const sweptPathFilters = [
|
|
62
|
+
.../* @__PURE__ */ new Set([spec.pathFilter, ...spec.checks.map((check) => resolveScope(check).pathFilter)])
|
|
63
|
+
];
|
|
59
64
|
return defineRdyKit({
|
|
60
65
|
description: spec.description,
|
|
61
66
|
defaultSeverity: "warn",
|
|
@@ -66,7 +71,7 @@ function defineAdoptionKit(spec) {
|
|
|
66
71
|
name: check.name,
|
|
67
72
|
id: check.id,
|
|
68
73
|
...check.severity !== void 0 && { severity: check.severity },
|
|
69
|
-
skip: skipUnlessProjectHoldsSources,
|
|
74
|
+
skip: () => skipUnlessProjectHoldsSources(resolveScope(check)),
|
|
70
75
|
check: () => reportKinds(check.kinds),
|
|
71
76
|
fix: check.fix
|
|
72
77
|
}))
|
|
@@ -85,16 +90,38 @@ function defineAdoptionKit(spec) {
|
|
|
85
90
|
throw new Error(`${spec.packageName}'s kit gives one id to more than one check: ${ids}`);
|
|
86
91
|
}
|
|
87
92
|
}
|
|
93
|
+
function isSweptPath(path) {
|
|
94
|
+
return sweptPathFilters.some((pathFilter) => pathFilter(path));
|
|
95
|
+
}
|
|
88
96
|
function loadSummary() {
|
|
89
97
|
cache.summary ??= readProject();
|
|
90
98
|
return cache.summary;
|
|
91
99
|
}
|
|
100
|
+
function mapPathFiltersByKind() {
|
|
101
|
+
const pathFilters = /* @__PURE__ */ new Map();
|
|
102
|
+
const conflicted = /* @__PURE__ */ new Set();
|
|
103
|
+
for (const check of spec.checks) {
|
|
104
|
+
const { pathFilter } = resolveScope(check);
|
|
105
|
+
for (const kind of check.kinds) {
|
|
106
|
+
const assigned = pathFilters.get(kind);
|
|
107
|
+
if (assigned !== void 0 && assigned !== pathFilter) conflicted.add(kind);
|
|
108
|
+
pathFilters.set(kind, pathFilter);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (conflicted.size > 0) {
|
|
112
|
+
const kinds = [...conflicted].toSorted().join(", ");
|
|
113
|
+
throw new Error(`${spec.packageName}'s kit reads one kind through more than one path filter: ${kinds}`);
|
|
114
|
+
}
|
|
115
|
+
return pathFilters;
|
|
116
|
+
}
|
|
92
117
|
async function readProject() {
|
|
93
|
-
const sources = await readTrackedSources(
|
|
118
|
+
const sources = await readTrackedSources(isSweptPath);
|
|
94
119
|
if (sources === void 0) return void 0;
|
|
95
120
|
return {
|
|
96
121
|
adoptedCount: countPackageUsage(sources, adoptedPackage),
|
|
97
|
-
findings: sources.flatMap(
|
|
122
|
+
findings: sources.flatMap(
|
|
123
|
+
(source) => spec.detect(source.text).filter((site) => (pathFiltersByKind.get(site.kind) ?? spec.pathFilter)(source.path)).map((site) => ({ ...site, path: source.path }))
|
|
124
|
+
),
|
|
98
125
|
sources
|
|
99
126
|
};
|
|
100
127
|
}
|
|
@@ -108,10 +135,13 @@ function defineAdoptionKit(spec) {
|
|
|
108
135
|
shouldReport: (finding) => kinds.includes(finding.kind)
|
|
109
136
|
});
|
|
110
137
|
}
|
|
111
|
-
|
|
138
|
+
function resolveScope(check) {
|
|
139
|
+
return check.pathFilter === void 0 ? kitScope : { noSourcesReason: check.noSourcesReason, pathFilter: check.pathFilter };
|
|
140
|
+
}
|
|
141
|
+
async function skipUnlessProjectHoldsSources(scope) {
|
|
112
142
|
const summary = await loadSummary();
|
|
113
143
|
if (summary === void 0) return NOT_A_REPO;
|
|
114
|
-
return summary.sources.
|
|
144
|
+
return summary.sources.some((source) => scope.pathFilter(source.path)) ? false : scope.noSourcesReason;
|
|
115
145
|
}
|
|
116
146
|
}
|
|
117
147
|
|
|
@@ -120,6 +150,14 @@ function condenseWhitespace(text) {
|
|
|
120
150
|
return text.replaceAll(/\s+/g, " ");
|
|
121
151
|
}
|
|
122
152
|
|
|
153
|
+
// ../adoption/src/portable/readAnchoredWindow.ts
|
|
154
|
+
function readAnchoredWindow(source, offset, lengths) {
|
|
155
|
+
return {
|
|
156
|
+
after: condenseWhitespace(source.slice(offset, offset + lengths.lookahead)),
|
|
157
|
+
before: condenseWhitespace(source.slice(Math.max(0, offset - lengths.lookbehind), offset))
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
123
161
|
// ../adoption/src/portable/readBalancedGroup.ts
|
|
124
162
|
var PARENTHESES = { close: ")", open: "(" };
|
|
125
163
|
function readBalancedGroup(source, from, delimiters) {
|
|
@@ -136,14 +174,6 @@ function readBalancedGroup(source, from, delimiters) {
|
|
|
136
174
|
return void 0;
|
|
137
175
|
}
|
|
138
176
|
|
|
139
|
-
// ../adoption/src/portable/readAnchoredWindow.ts
|
|
140
|
-
function readAnchoredWindow(source, offset, lengths) {
|
|
141
|
-
return {
|
|
142
|
-
after: condenseWhitespace(source.slice(offset, offset + lengths.lookahead)),
|
|
143
|
-
before: condenseWhitespace(source.slice(Math.max(0, offset - lengths.lookbehind), offset))
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
177
|
// ../adoption/src/mod.ts
|
|
148
178
|
import { blankNonCode, getLineAtOffset } from "readyup/check-utils";
|
|
149
179
|
|
|
@@ -273,8 +303,8 @@ var default_default = defineAdoptionKit({
|
|
|
273
303
|
exportNames: ADOPTED_EXPORTS,
|
|
274
304
|
noSourcesReason: "the project holds no JavaScript or TypeScript sources outside the exempt paths",
|
|
275
305
|
packageName: PACKAGE_NAME,
|
|
276
|
-
// A test computes these values deliberately, and a bootstrap wrapper's hand-rolled arithmetic
|
|
277
|
-
//
|
|
306
|
+
// A test computes these values deliberately, and a bootstrap wrapper's hand-rolled arithmetic keeps its
|
|
307
|
+
// build-first message alive through an incomplete install.
|
|
278
308
|
pathFilter: isAdoptableSource,
|
|
279
309
|
checks: [
|
|
280
310
|
{
|
|
@@ -289,7 +319,7 @@ var default_default = defineAdoptionKit({
|
|
|
289
319
|
id: "no-hand-rolled-round",
|
|
290
320
|
kinds: ["round-scale"],
|
|
291
321
|
severity: "recommend",
|
|
292
|
-
fix: `Replace each expression named above with round from ${PACKAGE_NAME}/candidate, called as round(value, places). The substitution is exact: round scales by the same power of ten these sites write out. Reference: ${README_URL}`
|
|
322
|
+
fix: `Replace each expression named above with round from ${PACKAGE_NAME}/candidate, called as round(value, places). The substitution is exact: round scales by the same power of ten that these sites write out. Reference: ${README_URL}`
|
|
293
323
|
},
|
|
294
324
|
{
|
|
295
325
|
name: "No source derives a random integer by hand",
|
package/.readyup/manifest.json
CHANGED
|
@@ -9,82 +9,97 @@
|
|
|
9
9
|
"esbuildVersion": "0.28.2",
|
|
10
10
|
"inputs": [
|
|
11
11
|
{
|
|
12
|
-
"hash": "
|
|
12
|
+
"hash": "1dc83619",
|
|
13
13
|
"kind": "module",
|
|
14
14
|
"path": "../../adoption/src/conventions/path-predicates.ts"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
|
-
"hash": "
|
|
17
|
+
"hash": "0237a871",
|
|
18
18
|
"kind": "module",
|
|
19
19
|
"path": "../../adoption/src/conventions/site-handoffs.ts"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
|
-
"hash": "
|
|
22
|
+
"hash": "8fb83c2d",
|
|
23
23
|
"kind": "module",
|
|
24
24
|
"path": "../../adoption/src/kits/defineAdoptionKit.ts"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
|
-
"hash": "
|
|
27
|
+
"hash": "10596562",
|
|
28
28
|
"kind": "module",
|
|
29
29
|
"path": "../../adoption/src/mod.ts"
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
|
-
"hash": "
|
|
32
|
+
"hash": "6fdaacac",
|
|
33
33
|
"kind": "module",
|
|
34
34
|
"path": "../../adoption/src/portable/condenseWhitespace.ts"
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
|
-
"hash": "
|
|
37
|
+
"hash": "0661f0dd",
|
|
38
|
+
"kind": "module",
|
|
39
|
+
"path": "../../adoption/src/portable/listDirectoryAscents.ts"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"hash": "07c8358f",
|
|
38
43
|
"kind": "module",
|
|
39
44
|
"path": "../../adoption/src/portable/listFunctionBodies.ts"
|
|
40
45
|
},
|
|
41
46
|
{
|
|
42
|
-
"hash": "
|
|
47
|
+
"hash": "2d9f1edb",
|
|
48
|
+
"kind": "module",
|
|
49
|
+
"path": "../../adoption/src/portable/listTemplateLiterals.ts"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"hash": "b0751395",
|
|
43
53
|
"kind": "module",
|
|
44
54
|
"path": "../../adoption/src/portable/readAnchoredWindow.ts"
|
|
45
55
|
},
|
|
46
56
|
{
|
|
47
|
-
"hash": "
|
|
57
|
+
"hash": "0eb1daf1",
|
|
48
58
|
"kind": "module",
|
|
49
59
|
"path": "../../adoption/src/portable/readBalancedGroup.ts"
|
|
50
60
|
},
|
|
51
61
|
{
|
|
52
|
-
"hash": "
|
|
62
|
+
"hash": "e9427d50",
|
|
63
|
+
"kind": "module",
|
|
64
|
+
"path": "../../adoption/src/portable/readLiteral.ts"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"hash": "24f6e6db",
|
|
53
68
|
"kind": "module",
|
|
54
69
|
"path": "kits/default.ts"
|
|
55
70
|
},
|
|
56
71
|
{
|
|
57
|
-
"hash": "
|
|
72
|
+
"hash": "05bd292b",
|
|
58
73
|
"kind": "module",
|
|
59
74
|
"path": "../src/readiness/adoptedExports.ts"
|
|
60
75
|
},
|
|
61
76
|
{
|
|
62
|
-
"hash": "
|
|
77
|
+
"hash": "f14085f3",
|
|
63
78
|
"kind": "module",
|
|
64
79
|
"path": "../src/readiness/listClampNestLines.ts"
|
|
65
80
|
},
|
|
66
81
|
{
|
|
67
|
-
"hash": "
|
|
82
|
+
"hash": "eabb769f",
|
|
68
83
|
"kind": "module",
|
|
69
84
|
"path": "../src/readiness/listMathIdioms.ts"
|
|
70
85
|
},
|
|
71
86
|
{
|
|
72
|
-
"hash": "
|
|
87
|
+
"hash": "827c07b2",
|
|
73
88
|
"kind": "module",
|
|
74
89
|
"path": "../src/readiness/listRandomIntegerLines.ts"
|
|
75
90
|
},
|
|
76
91
|
{
|
|
77
|
-
"hash": "
|
|
92
|
+
"hash": "bdcac255",
|
|
78
93
|
"kind": "module",
|
|
79
94
|
"path": "../src/readiness/listRoundScaleLines.ts"
|
|
80
95
|
}
|
|
81
96
|
],
|
|
82
97
|
"name": "default",
|
|
83
98
|
"path": "kits/default.js",
|
|
84
|
-
"readyupVersion": "0.
|
|
99
|
+
"readyupVersion": "0.36.0",
|
|
85
100
|
"source": "kits/default.ts",
|
|
86
|
-
"sourceHash": "
|
|
87
|
-
"targetHash": "
|
|
101
|
+
"sourceHash": "24f6e6db",
|
|
102
|
+
"targetHash": "b1603ae6"
|
|
88
103
|
}
|
|
89
104
|
]
|
|
90
105
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,9 +2,115 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 7.2.0 — 2026-09-15
|
|
6
|
+
|
|
7
|
+
### 🎉 Features
|
|
8
|
+
|
|
9
|
+
- Add a ReadyUp adoption kit reporting hand-rolled guards (#311)
|
|
10
|
+
|
|
11
|
+
- Adds a ReadyUp adoption kit to `@williamthorsen/toolbelt.guards` that reports every function whose entire body re-implements a guard published by the package.
|
|
12
|
+
|
|
13
|
+
- Add a ReadyUp adoption kit to toolbelt.filesystem (#316)
|
|
14
|
+
|
|
15
|
+
- Adds a ReadyUp adoption kit to `@williamthorsen/toolbelt.filesystem` that recommends `writeAtomic` where a project writes a file to a temporary path and renames it into place, and `listDirectoryChain`, `findDirectoryChainMatch`, or `listDirectoryChainMatches` where a loop ascends to the filesystem root by `path.dirname`.
|
|
16
|
+
- Promotes `writeAtomic` to the candidate tier.
|
|
17
|
+
|
|
18
|
+
Migration: Change any import of `writeAtomic` from `@williamthorsen/toolbelt.filesystem/proposed` to `@williamthorsen/toolbelt.filesystem/candidate`.
|
|
19
|
+
|
|
20
|
+
- Read a directory walk's probed name through a binding or constant (#324)
|
|
21
|
+
|
|
22
|
+
- Extends the ReadyUp adoption kit in `@williamthorsen/toolbelt.packaging` to recommend `findPackageRoot` or `resolveSelfVersion` for a hand-rolled `package.json` search that checks for the file through a variable declared once inside its loop and never reassigned there, or through a string constant declared once in the same file.
|
|
23
|
+
- Stops the kit in `@williamthorsen/toolbelt.filesystem` from reporting such a search, which it previously treated as a generic directory walk.
|
|
24
|
+
|
|
25
|
+
- Report hand-rolled dedents in the strings adoption kit (#330)
|
|
26
|
+
|
|
27
|
+
- Adds `no-joined-line-array`, which reports an array of string or template literals that spans several lines and is joined with a newline.
|
|
28
|
+
- Adds `no-layout-breaking-template`, which reports an untagged template literal whose later lines drop below the indentation of the line on which it opens.
|
|
29
|
+
|
|
30
|
+
### 🐛 Bug fixes
|
|
31
|
+
|
|
32
|
+
- Stop reading each segment of a probe's path as a separately probed name (#320)
|
|
33
|
+
|
|
34
|
+
- Fixes an issue in which `toolbelt.filesystem`'s ReadyUp adoption kit treated a loop over parent directories as a search for each directory's own `package.json` when the path checked at each level contained a `'package.json'` literal, as in `path.join(dir, 'node_modules', name, 'package.json')`, and so did not report the loop under `no-hand-rolled-directory-walk`.
|
|
35
|
+
|
|
36
|
+
- Make pickInteger draw once per call and keep seeded draws below 1 (#329)
|
|
37
|
+
|
|
38
|
+
- Fixes seeded draws that could return exactly 1, outside the documented range of [0, 1): With seed `1_000_000_856_026_238`, `pickInteger({ min: 0, max: 9, seed })` returned 10 and `pickItem` from `toolbelt.arrays` threw a `RangeError`.
|
|
39
|
+
- Changes only the draw that returned 1, which no `SeededRng` and no integer seed below 2³¹ could produce.
|
|
40
|
+
- Stops `pickInteger` from skipping its draw when `min` and `max` truncate to the same integer, which changes no return value but shifts the later values drawn from a `SeededRng` or seed function passed with such bounds.
|
|
41
|
+
|
|
42
|
+
### ♻️ Refactoring
|
|
43
|
+
|
|
44
|
+
- Move directory-walk recognition from filesystem into packages/adoption (#318)
|
|
45
|
+
|
|
46
|
+
- Adds `listDirectoryAscents` to `packages/adoption`, which reports each directory ascent once with the names probed by its innermost loop, and reduces `filesystem`'s `listChainWalkSites` to a partition of that output, so a `toolbelt.packaging` kit can partition the same ascents without its own copy of the recognition.
|
|
47
|
+
- Renames the hand-off rule `isProjectRootSearch` to `isManifestSearch` and rewrites the text in `packages/adoption` and `filesystem` that credited `findProjectRoot` alone with a `package.json` walk.
|
|
48
|
+
- Stops `filesystem`'s kit from reporting a loop that ascends one binding around an inner loop ascending another binding and probing for `package.json`, which is the only finding changed by the move.
|
|
49
|
+
|
|
50
|
+
### 🧪 Tests
|
|
51
|
+
|
|
52
|
+
- Move the rdy run report reader into the adoption test utilities (#323)
|
|
53
|
+
|
|
54
|
+
- Replaces the `rdy run --json` report reader copied into each of the twelve `pragma-suppression.tool.test.ts` suites with `listKitCheckReports`, a helper added to `@williamthorsen/toolbelt.adoption/test-utils` that runs a package's compiled kit over a fixture repo and returns its check reports, so a change to the shape of readyup's report needs one edit rather than twelve.
|
|
55
|
+
- Fixes the error thrown for a kit that does not load: Each copy discarded the load error recorded by `rdy` on the kit's entry and threw "the run reported no adoption checks", and the helper throws with `rdy`'s own message instead.
|
|
56
|
+
|
|
57
|
+
### ⚙️ Tooling
|
|
58
|
+
|
|
59
|
+
- Remove the stale repo-local cliff.toml and normalize changelog titles (#327)
|
|
60
|
+
|
|
61
|
+
- Stops `release-kit prepare` from printing a "skipped due to grouping error(s)" warning for each releasable workspace by letting it resolve the git-cliff template bundled with release-kit, previously overridden by the root `cliff.toml`.
|
|
62
|
+
- Excludes commits without a ticket prefix from future changelog entries.
|
|
63
|
+
- Renames the section titles in every `packages/*/.meta/changelog.json`, except `Dependency updates`, to the headings of release-kit's work-type taxonomy, such as "🎉 Features" and "🏗️ Internal features", and regenerates each `CHANGELOG.md` so that release-kit orders existing and new sections by the same rule.
|
|
64
|
+
- Causes the next `release-kit prepare` to plan patch releases of `dstructs`, `hof`, and `sets`, which had no other commits since their last release, because the changelog commit touches every workspace.
|
|
65
|
+
|
|
66
|
+
### 📚 Documentation
|
|
67
|
+
|
|
68
|
+
- Align prose with plain-speech doctrine and writing conventions (#306)
|
|
69
|
+
|
|
70
|
+
- Copy-edits prose across the repo: comments, test names, package READMEs, and `AGENTS.md`.
|
|
71
|
+
- Rewrites a few user-facing strings as well, among them `configure-project`'s help text and the errors from `parseProjectSpec`, `securityCommands`, and `hashString`.
|
|
72
|
+
|
|
73
|
+
## 7.1.0 — 2026-09-06
|
|
74
|
+
|
|
75
|
+
### 🎉 Features
|
|
76
|
+
|
|
77
|
+
- Add a ReadyUp adoption kit (#253)
|
|
78
|
+
|
|
79
|
+
Adds a ReadyUp adoption kit to `@williamthorsen/toolbelt.objects`. The kit recommends `Object.hasOwn` or the package's own `hasOwnProperty` in place of a call reached through `Object.prototype`, and `isRecord` or `isRecordOrArray` in place of a guard written as `typeof value === 'object' && value !== null`. Its third check warns that a comparison of two `JSON.stringify` calls is key-order dependent and should be replaced by `isEqual`.
|
|
80
|
+
|
|
81
|
+
- Add a ReadyUp adoption kit reporting hand-rolled sleeps (#303)
|
|
82
|
+
|
|
83
|
+
- Adds a ReadyUp adoption kit to `@williamthorsen/toolbelt.async` that recommends the use of `delay` to replace a hand-rolled sleep.
|
|
84
|
+
|
|
85
|
+
### 📚 Documentation
|
|
86
|
+
|
|
87
|
+
- Document the generated-source exemption in the adoption kits (#251)
|
|
88
|
+
|
|
89
|
+
Documents the generated- and vendored-source exemption in the `errors`, `numbers`, and `strings` READMEs.
|
|
90
|
+
|
|
91
|
+
- Repair reduced object relatives in passages recurring across files (#262)
|
|
92
|
+
|
|
93
|
+
Repairs the reduced object relative in the prose passages that recur across more than one file, in package READMEs, source comments, test titles, and the ReadyUp kits' check messages.
|
|
94
|
+
|
|
95
|
+
- Repair reduced object relatives in the READMEs and AGENTS.md (#263)
|
|
96
|
+
|
|
97
|
+
Repairs the reduced object relative in `AGENTS.md`, the root `README.md`, and the package READMEs.
|
|
98
|
+
|
|
99
|
+
- Repair reduced object relatives in packages/adoption (#264)
|
|
100
|
+
|
|
101
|
+
Repairs the reduced object relative in `packages/adoption`, in source comments, doc descriptions, and test titles.
|
|
102
|
+
|
|
103
|
+
- Repair reduced object relatives in the readiness modules and kits (#265)
|
|
104
|
+
|
|
105
|
+
Repairs the reduced object relative in the six kit-bearing packages' readiness modules and ReadyUp kit sources, across comments, doc descriptions, test titles, and the kits' check messages.
|
|
106
|
+
|
|
107
|
+
- Repair the repository's prose and record every rejection's ground (#290)
|
|
108
|
+
|
|
109
|
+
Applies one repo-wide `revise-prose` sweep across the repository's READMEs, `AGENTS.md`, source comments, doc descriptions, and test names.
|
|
110
|
+
|
|
5
111
|
## 7.0.3 — 2026-08-30
|
|
6
112
|
|
|
7
|
-
### Bug fixes
|
|
113
|
+
### 🐛 Bug fixes
|
|
8
114
|
|
|
9
115
|
- Reject a check declaring a kind its detector never produces (#246)
|
|
10
116
|
|
|
@@ -12,21 +118,15 @@ All notable changes to this project will be documented in this file.
|
|
|
12
118
|
|
|
13
119
|
## 7.0.2 — 2026-08-28
|
|
14
120
|
|
|
15
|
-
### Refactoring
|
|
121
|
+
### ♻️ Refactoring
|
|
16
122
|
|
|
17
123
|
- Upgrade eslint-config-typescript to v12.0.1 and satisfy its new rules (#236)
|
|
18
124
|
|
|
19
125
|
Upgrades `@williamthorsen/eslint-config-typescript` to v12 and fixes violations surfaced by the new rules banning unpublished barrels and floating disposables.
|
|
20
126
|
|
|
21
|
-
### Dependencies
|
|
22
|
-
|
|
23
|
-
- Upgrade all deps to latest version
|
|
24
|
-
- Upgrade deps to latest version
|
|
25
|
-
- Upgrade all deps to latest version
|
|
26
|
-
|
|
27
127
|
## 7.0.1 — 2026-08-24
|
|
28
128
|
|
|
29
|
-
### Bug fixes
|
|
129
|
+
### 🐛 Bug fixes
|
|
30
130
|
|
|
31
131
|
- Stop the adoption kits from blanking code after `++`, `!`, and keyword-named members (#208)
|
|
32
132
|
|
|
@@ -34,7 +134,7 @@ All notable changes to this project will be documented in this file.
|
|
|
34
134
|
|
|
35
135
|
`@williamthorsen/toolbelt.adoption` now takes `blankNonCode` and `getLineAtOffset` from `readyup/check-utils` in place of the copies it held, which had fallen behind readyup's on those three cases.
|
|
36
136
|
|
|
37
|
-
### Internal
|
|
137
|
+
### 🏗️ Internal features
|
|
38
138
|
|
|
39
139
|
- Migrate the adoption kits onto FindingOutcome and add check ids (#217)
|
|
40
140
|
|
|
@@ -50,7 +150,7 @@ All notable changes to this project will be documented in this file.
|
|
|
50
150
|
|
|
51
151
|
## 7.0.0 — 2026-08-21
|
|
52
152
|
|
|
53
|
-
### Features
|
|
153
|
+
### 🎉 Features
|
|
54
154
|
|
|
55
155
|
- 🚨 **Breaking:** Promote clamp to the candidate tier with a stricter bounds contract (#185)
|
|
56
156
|
|
|
@@ -62,25 +162,21 @@ All notable changes to this project will be documented in this file.
|
|
|
62
162
|
|
|
63
163
|
Adds a ReadyUp adoption kit to `@williamthorsen/toolbelt.numbers`. When run against a project, the kit identifies hand-rolled code that can be replaced by the package's `clamp`, `round`, or `pickInteger` functions.
|
|
64
164
|
|
|
65
|
-
### Bug fixes
|
|
165
|
+
### 🐛 Bug fixes
|
|
66
166
|
|
|
67
167
|
- Blank comments and literals before a detector reads a source (#191)
|
|
68
168
|
|
|
69
169
|
Fixes the issue that the `errors`, `numbers`, and `vitest` adoption kits' detectors did not distinguish code from comments and literals, and so flagged a pattern written in a comment or a string as a candidate replacement site. Each detector now blanks every comment, string, template literal, and regular expression before its anchor scan, leaving interpolated expressions intact.
|
|
70
170
|
|
|
71
|
-
### Refactoring
|
|
171
|
+
### ♻️ Refactoring
|
|
72
172
|
|
|
73
173
|
- Break the workspace dependency cycle by making `adoption` a leaf (#195)
|
|
74
174
|
|
|
75
175
|
Fixes a cyclic dependency among packages in the repo. `packages/adoption` is now a workspace leaf: It declares no workspace dependency, and its test scaffolding is held to node builtins. A new root test fails on any cycle in the workspace dependency graph.
|
|
76
176
|
|
|
77
|
-
### Dependencies
|
|
78
|
-
|
|
79
|
-
- Upgrade all deps to latest version
|
|
80
|
-
|
|
81
177
|
## 6.0.1 — 2026-08-13
|
|
82
178
|
|
|
83
|
-
### Tooling
|
|
179
|
+
### ⚙️ Tooling
|
|
84
180
|
|
|
85
181
|
- Remove redundant .gitignore files
|
|
86
182
|
- Populate manifest metadata and adopt a pnpm catalog (#140)
|
|
@@ -89,13 +185,13 @@ All notable changes to this project will be documented in this file.
|
|
|
89
185
|
|
|
90
186
|
## 6.0.0 — 2026-08-12
|
|
91
187
|
|
|
92
|
-
### Features
|
|
188
|
+
### 🎉 Features
|
|
93
189
|
|
|
94
190
|
- 🚨 **Breaking:** Rename get* functions by return kind and verb specificity (#119)
|
|
95
191
|
|
|
96
192
|
Renames thirteen functions across various packages to align with a consistent naming pattern.
|
|
97
193
|
|
|
98
|
-
### Refactoring
|
|
194
|
+
### ♻️ Refactoring
|
|
99
195
|
|
|
100
196
|
- Stop shipping test-only and dead support modules (#117)
|
|
101
197
|
|
|
@@ -107,17 +203,13 @@ All notable changes to this project will be documented in this file.
|
|
|
107
203
|
|
|
108
204
|
## 5.0.0 — 2026-08-08
|
|
109
205
|
|
|
110
|
-
### Features
|
|
206
|
+
### 🎉 Features
|
|
111
207
|
|
|
112
208
|
- 🚨 **Breaking:** Spawn seeded number generator of same subclass (#84)
|
|
113
209
|
|
|
114
210
|
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.
|
|
115
211
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
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.
|
|
119
|
-
|
|
120
|
-
### Refactoring
|
|
212
|
+
### ♻️ Refactoring
|
|
121
213
|
|
|
122
214
|
- Fixes violations surfaced by newly active lint rules (#84)
|
|
123
215
|
- Fix slug punctuation and require safe integers (#86)
|
|
@@ -126,9 +218,7 @@ All notable changes to this project will be documented in this file.
|
|
|
126
218
|
- 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.
|
|
127
219
|
- 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.
|
|
128
220
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
### Tooling
|
|
221
|
+
### ⚙️ Tooling
|
|
132
222
|
|
|
133
223
|
- Migrate Vitest configs to the nmr projects model (#73)
|
|
134
224
|
|
|
@@ -146,35 +236,31 @@ All notable changes to this project will be documented in this file.
|
|
|
146
236
|
|
|
147
237
|
All packages now have identical compiler settings, using the settings from the `@williamthorsen/tsconfig` base config without modification.
|
|
148
238
|
|
|
149
|
-
### Dependencies
|
|
150
|
-
|
|
151
|
-
- Upgrade all deps to latest version
|
|
152
|
-
|
|
153
239
|
## 4.3.8 — 2026-07-27
|
|
154
240
|
|
|
155
|
-
### Tooling
|
|
241
|
+
### ⚙️ Tooling
|
|
156
242
|
|
|
157
243
|
- Normalize Vitest, and lint configs
|
|
158
244
|
|
|
159
|
-
### Documentation
|
|
245
|
+
### 📚 Documentation
|
|
160
246
|
|
|
161
247
|
- Change license to ISC
|
|
162
248
|
|
|
163
249
|
## 4.3.7 — 2026-07-24
|
|
164
250
|
|
|
165
|
-
### Tooling
|
|
251
|
+
### ⚙️ Tooling
|
|
166
252
|
|
|
167
253
|
- Configure release-kit & repo labels
|
|
168
254
|
|
|
169
255
|
## 4.3.6 — 2026-07-20
|
|
170
256
|
|
|
171
|
-
### Bug fixes
|
|
257
|
+
### 🐛 Bug fixes
|
|
172
258
|
|
|
173
259
|
- Add repository field to package manifests for npm provenance (#65)
|
|
174
260
|
|
|
175
261
|
Fixes an issue that prevented every package from publishing to npm. Each package now links to its source repository from its npm page.
|
|
176
262
|
|
|
177
|
-
### Dependencies
|
|
263
|
+
### 📦 Dependencies
|
|
178
264
|
|
|
179
265
|
- Upgrade ESLint packages and migrate to TypeScript 6 (#67)
|
|
180
266
|
|
|
@@ -182,7 +268,7 @@ All notable changes to this project will be documented in this file.
|
|
|
182
268
|
|
|
183
269
|
## 4.3.5 — 2026-07-20
|
|
184
270
|
|
|
185
|
-
### Tooling
|
|
271
|
+
### ⚙️ Tooling
|
|
186
272
|
|
|
187
273
|
- Migrate to the nmr toolchain and resolve dependency vulnerabilities (#45)
|
|
188
274
|
|
|
@@ -190,37 +276,13 @@ All notable changes to this project will be documented in this file.
|
|
|
190
276
|
|
|
191
277
|
## 4.3.4 — 2026-03-19
|
|
192
278
|
|
|
193
|
-
### Formatting
|
|
279
|
+
### 🎨 Formatting
|
|
194
280
|
|
|
195
281
|
- Format changelogs
|
|
196
282
|
|
|
197
283
|
## 4.3.1 — 2026-03-10
|
|
198
284
|
|
|
199
|
-
###
|
|
200
|
-
|
|
201
|
-
- Add string functions
|
|
202
|
-
- Add number functions
|
|
203
|
-
- Add integer string functions
|
|
204
|
-
- Caller can specify fallback value for safeParseInteger
|
|
205
|
-
- Allow error as safeParseInteger fallback
|
|
206
|
-
- Add safeParseNumber
|
|
207
|
-
|
|
208
|
-
### Refactoring
|
|
209
|
-
|
|
210
|
-
- Rename functions
|
|
211
|
-
|
|
212
|
-
### Tests
|
|
213
|
-
|
|
214
|
-
- Adapt Deno tests to Vitest
|
|
215
|
-
|
|
216
|
-
### Tooling
|
|
217
|
-
|
|
218
|
-
- Scaffold the numbers workspace
|
|
219
|
-
- Enable incremental type generation
|
|
220
|
-
- Rename publish script to avoid recursion
|
|
221
|
-
- Change package registry from github to npmjs
|
|
222
|
-
|
|
223
|
-
### Dependencies
|
|
285
|
+
### 📦 Dependencies
|
|
224
286
|
|
|
225
287
|
- Adapt to dependency upgrades and bump Node engine to >=24 (#8)
|
|
226
288
|
|
|
@@ -262,8 +324,4 @@ All notable changes to this project will be documented in this file.
|
|
|
262
324
|
|
|
263
325
|
The build script used `pnpm --recursive run build` but no workspace package defines a `build` script — they all use `ws build` through the workspace script runner. Aligns with all other recursive commands.
|
|
264
326
|
|
|
265
|
-
### Documentation
|
|
266
|
-
|
|
267
|
-
- Fix lint
|
|
268
|
-
|
|
269
327
|
<!-- Generated by release-kit. Do not edit this file. Use .meta/changelog-overrides.json to override entries. -->
|
package/README.md
CHANGED
|
@@ -1,15 +1,46 @@
|
|
|
1
|
+
<!-- readme-type: library -->
|
|
2
|
+
|
|
1
3
|
# @williamthorsen/toolbelt.numbers
|
|
2
4
|
|
|
3
5
|
Utility functions for working with numbers.
|
|
4
6
|
|
|
5
7
|
<!-- section:release-notes -->
|
|
6
|
-
## Release notes — v7.0
|
|
8
|
+
## Release notes — v7.2.0 (2026-09-15)
|
|
9
|
+
|
|
10
|
+
### 🎉 Features
|
|
11
|
+
|
|
12
|
+
- Add a ReadyUp adoption kit reporting hand-rolled guards (#311)
|
|
13
|
+
|
|
14
|
+
- Adds a ReadyUp adoption kit to `@williamthorsen/toolbelt.guards` that reports every function whose entire body re-implements a guard published by the package.
|
|
15
|
+
|
|
16
|
+
- Add a ReadyUp adoption kit to toolbelt.filesystem (#316)
|
|
17
|
+
|
|
18
|
+
- Adds a ReadyUp adoption kit to `@williamthorsen/toolbelt.filesystem` that recommends `writeAtomic` where a project writes a file to a temporary path and renames it into place, and `listDirectoryChain`, `findDirectoryChainMatch`, or `listDirectoryChainMatches` where a loop ascends to the filesystem root by `path.dirname`.
|
|
19
|
+
- Promotes `writeAtomic` to the candidate tier.
|
|
20
|
+
|
|
21
|
+
Migration: Change any import of `writeAtomic` from `@williamthorsen/toolbelt.filesystem/proposed` to `@williamthorsen/toolbelt.filesystem/candidate`.
|
|
22
|
+
|
|
23
|
+
- Read a directory walk's probed name through a binding or constant (#324)
|
|
24
|
+
|
|
25
|
+
- Extends the ReadyUp adoption kit in `@williamthorsen/toolbelt.packaging` to recommend `findPackageRoot` or `resolveSelfVersion` for a hand-rolled `package.json` search that checks for the file through a variable declared once inside its loop and never reassigned there, or through a string constant declared once in the same file.
|
|
26
|
+
- Stops the kit in `@williamthorsen/toolbelt.filesystem` from reporting such a search, which it previously treated as a generic directory walk.
|
|
27
|
+
|
|
28
|
+
- Report hand-rolled dedents in the strings adoption kit (#330)
|
|
29
|
+
|
|
30
|
+
- Adds `no-joined-line-array`, which reports an array of string or template literals that spans several lines and is joined with a newline.
|
|
31
|
+
- Adds `no-layout-breaking-template`, which reports an untagged template literal whose later lines drop below the indentation of the line on which it opens.
|
|
32
|
+
|
|
33
|
+
### 🐛 Bug fixes
|
|
34
|
+
|
|
35
|
+
- Stop reading each segment of a probe's path as a separately probed name (#320)
|
|
7
36
|
|
|
8
|
-
|
|
37
|
+
- Fixes an issue in which `toolbelt.filesystem`'s ReadyUp adoption kit treated a loop over parent directories as a search for each directory's own `package.json` when the path checked at each level contained a `'package.json'` literal, as in `path.join(dir, 'node_modules', name, 'package.json')`, and so did not report the loop under `no-hand-rolled-directory-walk`.
|
|
9
38
|
|
|
10
|
-
-
|
|
39
|
+
- Make pickInteger draw once per call and keep seeded draws below 1 (#329)
|
|
11
40
|
|
|
12
|
-
Fixes
|
|
41
|
+
- Fixes seeded draws that could return exactly 1, outside the documented range of [0, 1): With seed `1_000_000_856_026_238`, `pickInteger({ min: 0, max: 9, seed })` returned 10 and `pickItem` from `toolbelt.arrays` threw a `RangeError`.
|
|
42
|
+
- Changes only the draw that returned 1, which no `SeededRng` and no integer seed below 2³¹ could produce.
|
|
43
|
+
- Stops `pickInteger` from skipping its draw when `min` and `max` truncate to the same integer, which changes no return value but shifts the later values drawn from a `SeededRng` or seed function passed with such bounds.
|
|
13
44
|
<!-- /section:release-notes -->
|
|
14
45
|
|
|
15
46
|
## Installation
|
|
@@ -28,7 +59,7 @@ Requires Node.js 24 or later.
|
|
|
28
59
|
clamp(value: number, bounds: { min?: number; max?: number }): number;
|
|
29
60
|
```
|
|
30
61
|
|
|
31
|
-
Returns the value constrained to the inclusive bounds; an omitted bound leaves that side unconstrained. A reversed range or a `NaN` bound throws a `RangeError`, where the `Math.max(min, Math.min(max, value))` idiom it replaces returns a value for both. A `NaN` value passes through.
|
|
62
|
+
Returns the value constrained to the inclusive bounds; an omitted bound leaves that side unconstrained. A reversed range or a `NaN` bound throws a `RangeError`, where the `Math.max(min, Math.min(max, value))` idiom that it replaces returns a value for both. A `NaN` value passes through.
|
|
32
63
|
|
|
33
64
|
```ts
|
|
34
65
|
import { clamp } from '@williamthorsen/toolbelt.numbers/candidate';
|
|
@@ -57,9 +88,9 @@ round(3.14159, 2); // 3.14
|
|
|
57
88
|
pickInteger(params?: { min?: number; max?: number; seed?: Seed }): number;
|
|
58
89
|
```
|
|
59
90
|
|
|
60
|
-
Returns a random integer between the bounds, **inclusive** of both, and truncates a non-integer bound. Passing a seed makes the draw deterministic, which
|
|
91
|
+
Returns a random integer between the bounds, **inclusive** of both, and truncates a non-integer bound. Passing a seed makes the draw deterministic, which a test wants. Each call draws exactly once, even when the bounds admit a single value.
|
|
61
92
|
|
|
62
|
-
Mind the bound when replacing `Math.floor(Math.random() * n)`:
|
|
93
|
+
Mind the bound when replacing `Math.floor(Math.random() * n)`: That idiom stops at `n - 1`, so the equivalent is `pickInteger({ max: n - 1 })`.
|
|
63
94
|
|
|
64
95
|
```ts
|
|
65
96
|
import { pickInteger } from '@williamthorsen/toolbelt.numbers/candidate';
|
|
@@ -75,11 +106,11 @@ The package ships a ReadyUp kit, so a project that installs it can ask how far i
|
|
|
75
106
|
rdy run --packages
|
|
76
107
|
```
|
|
77
108
|
|
|
78
|
-
The kit reads the project's tracked sources and reports every hand-rolled clamp, decimal rounding, and random integer in them, each counted against the calls the project already makes into this package. All three report at `recommend`:
|
|
109
|
+
The kit reads the project's tracked sources and reports every hand-rolled clamp, decimal rounding, and random integer in them, each counted against the calls that the project already makes into this package. All three report at `recommend`: They are correct code that a published utility expresses better, not defects.
|
|
79
110
|
|
|
80
111
|
A random integer used as an array subscript is left alone. That site belongs to `@williamthorsen/toolbelt.arrays`, whose `pickItem` covers it, and reporting it here would mean seeing one line twice under conflicting advice.
|
|
81
112
|
|
|
82
|
-
Bootstrap wrappers under `bin/` are exempt:
|
|
113
|
+
Bootstrap wrappers under `bin/` are exempt: Such a wrapper imports only builtins so its build-first message survives an incomplete install, and importing this package there would replace that message with a module-resolution failure. Tests are exempt too, since they compute these values deliberately. A source declared generated or vendored by the project in its own `.gitattributes`, under `linguist-generated` or `linguist-vendored`, is exempt as well: The sweep drops it before the kit sees it, so committed bundler output yields no advice that anyone could act on. The sweep is readyup's, so this holds on readyup 0.35.0 or later.
|
|
83
114
|
|
|
84
115
|
A reviewed site is silenced by an `rdy-ignore` pragma on its own line, or `rdy-ignore-next-line` on the line above. A pragma naming a check's id suppresses that check alone; with no id it covers every check on the line. A failed check prints its id ahead of its fraction, which is the form to write:
|
|
85
116
|
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Seed } from '../internal/evaluateSeed.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a number generator whose output, when invoked successively, is a pseudo-random
|
|
4
|
+
* series of numbers that deterministically depend on the initial seed (or pseudo-random if no seed is given).
|
|
5
|
+
* This is not intended to be a cryptographically secure random number generator.
|
|
6
|
+
*/
|
|
2
7
|
export declare function makeRng(seed?: Seed): () => number;
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the value constrained to the inclusive bounds; an omitted bound leaves that side unconstrained.
|
|
3
|
+
* A NaN value passes through, but a NaN bound or a min greater than max throws a RangeError.
|
|
4
|
+
*
|
|
5
|
+
* @category Number
|
|
6
|
+
* @experimental
|
|
7
|
+
* @stage candidate
|
|
8
|
+
*/
|
|
1
9
|
export declare function clamp(value: number, bounds: ClampBounds): number;
|
|
2
10
|
export interface ClampBounds {
|
|
3
11
|
readonly max?: number | undefined;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Seed } from '../internal/evaluateSeed.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a scaled random number in the range [min, max).
|
|
4
|
+
*
|
|
5
|
+
* @category Number
|
|
6
|
+
* @experimental
|
|
7
|
+
* @stage candidate
|
|
8
|
+
*/
|
|
2
9
|
export declare function generateRandom(options?: Options): number;
|
|
3
10
|
interface Options {
|
|
4
11
|
max?: number | undefined;
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import type { Maybe } from '../internal/internal.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if the value is a string that represents a valid integer.
|
|
4
|
+
*
|
|
5
|
+
* @category Type Guards
|
|
6
|
+
* @experimental
|
|
7
|
+
* @stage candidate
|
|
8
|
+
*/
|
|
2
9
|
export declare function isIntegerString(value: string | null | undefined): value is string;
|
|
10
|
+
/**
|
|
11
|
+
* Attempts to parse an integer from a string and returns the parsed integer.
|
|
12
|
+
* If the input does not represent a valid integer, returns the fallback value or throws the fallback error.
|
|
13
|
+
*
|
|
14
|
+
* @param value - The string to parse.
|
|
15
|
+
* @param fallback - The value to return if parsing fails.
|
|
16
|
+
* @returns The parsed integer or the fallback value.
|
|
17
|
+
*
|
|
18
|
+
* @category Type Guards
|
|
19
|
+
* @experimental
|
|
20
|
+
* @stage candidate
|
|
21
|
+
*/
|
|
3
22
|
export declare function safeParseInteger(value: Maybe<string>, fallback: number | Error): number;
|
|
4
23
|
export declare function safeParseInteger(value: Maybe<string>, fallback?: undefined): number | undefined;
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import type { Maybe } from '../internal/internal.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if the value is a string that represents a valid finite number.
|
|
4
|
+
*
|
|
5
|
+
* Accepts integers, decimals, and scientific notation (e.g., "1e3").
|
|
6
|
+
*
|
|
7
|
+
* @category Type Guards
|
|
8
|
+
* @experimental
|
|
9
|
+
* @stage candidate
|
|
10
|
+
*/
|
|
2
11
|
export declare function isNumericString(value: string | null | undefined): value is string;
|
|
12
|
+
/**
|
|
13
|
+
* Attempts to parse a number from a string and returns the parsed number.
|
|
14
|
+
* If the input does not represent a valid number, returns the fallback value or throws the fallback error.
|
|
15
|
+
*
|
|
16
|
+
* @param value - The string to parse.
|
|
17
|
+
* @param fallback - The value to return if parsing fails.
|
|
18
|
+
* @returns The parsed number or the fallback value.
|
|
19
|
+
*
|
|
20
|
+
* @category Type Guards
|
|
21
|
+
* @experimental
|
|
22
|
+
* @stage candidate
|
|
23
|
+
*/
|
|
3
24
|
export declare function safeParseNumber(value: Maybe<string>, fallback: number | Error): number;
|
|
4
25
|
export declare function safeParseNumber(value: Maybe<string>, fallback?: undefined): number | undefined;
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { Seed } from '../internal/evaluateSeed.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a random integer between the bounds inclusive.
|
|
4
|
+
* If the bounds are not integers, they are truncated to integers.
|
|
5
|
+
* Draws exactly once per call, even when the bounds admit a single value, so that a shared seed advances the same way
|
|
6
|
+
* whatever the bounds.
|
|
7
|
+
*
|
|
8
|
+
* @category Number
|
|
9
|
+
* @experimental
|
|
10
|
+
* @stage candidate
|
|
11
|
+
*/
|
|
2
12
|
export declare function pickInteger(params?: Params): number;
|
|
3
13
|
interface Params {
|
|
4
14
|
max?: number | undefined;
|
|
@@ -9,9 +9,6 @@ export function pickInteger(params = {}) {
|
|
|
9
9
|
}
|
|
10
10
|
const start = Math.trunc(Math.min(min, max));
|
|
11
11
|
const end = Math.trunc(Math.max(min, max));
|
|
12
|
-
if (start === end) {
|
|
13
|
-
return start;
|
|
14
|
-
}
|
|
15
12
|
const range = end - start + 1;
|
|
16
13
|
return start + Math.floor(generateRandom({ seed }) * range);
|
|
17
14
|
}
|
|
@@ -1 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the number rounded to the given number of decimal places, or 0 if no nDecimalPlaces is specified.
|
|
3
|
+
* @param value
|
|
4
|
+
* @param nDecimalPlaces
|
|
5
|
+
*
|
|
6
|
+
* @category Number
|
|
7
|
+
* @experimental
|
|
8
|
+
* @stage candidate
|
|
9
|
+
*/
|
|
1
10
|
export declare function round(value: number, nDecimalPlaces?: number): number;
|
|
@@ -1,9 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scales a number from one range to another.
|
|
3
|
+
*
|
|
4
|
+
* @category Number
|
|
5
|
+
* @experimental
|
|
6
|
+
* @stage candidate
|
|
7
|
+
*/
|
|
1
8
|
export declare function scale(value: number, toRange: Range, fromRange?: Partial<Range>): number;
|
|
9
|
+
/**
|
|
10
|
+
* Scales a number from one range to another and rounds it to the nearest integer.
|
|
11
|
+
* Throws a RangeError unless the target bounds are safe integers.
|
|
12
|
+
*
|
|
13
|
+
* @category Number
|
|
14
|
+
* @experimental
|
|
15
|
+
* @stage candidate
|
|
16
|
+
*/
|
|
2
17
|
export declare function scaleInt(value: number, toRange: Range, fromRange?: Partial<IntegerRange>): number;
|
|
3
18
|
interface Range {
|
|
4
19
|
min: number;
|
|
5
20
|
max: number;
|
|
6
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Represents a range of integers. Non-integer values should be truncated by the called function.
|
|
24
|
+
*/
|
|
7
25
|
interface IntegerRange {
|
|
8
26
|
min: number;
|
|
9
27
|
max: number;
|
|
@@ -1,24 +1,46 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Seed, type SeededGenerator } from '../internal/evaluateSeed.js';
|
|
2
|
+
/**
|
|
3
|
+
* Class that manages a pseudo-random number generator that behaves deterministically when given a seed.
|
|
4
|
+
*/
|
|
2
5
|
export declare class SeededRng implements SeededGenerator {
|
|
3
6
|
private _seed;
|
|
4
7
|
private baseSeed;
|
|
5
8
|
private nIncrements;
|
|
9
|
+
/**
|
|
10
|
+
* Constructor
|
|
11
|
+
*/
|
|
6
12
|
constructor(seed?: Seed);
|
|
7
13
|
get maxBase(): number;
|
|
8
14
|
get rng(): () => number;
|
|
9
15
|
get seed(): number;
|
|
10
16
|
static evaluateSeed(seed?: Seed): number | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a child from a seed without mutating the parent seed.
|
|
19
|
+
*/
|
|
11
20
|
static clone<T extends ThisConstructor<typeof SeededRng>>(this: T, seed: undefined, nIncrements?: number): undefined;
|
|
12
21
|
static clone<T extends ThisConstructor<typeof SeededRng>>(this: T, seed: Seed, nIncrements?: number): This<T>;
|
|
13
22
|
static clone<T extends ThisConstructor<typeof SeededRng>>(this: T, seed: Seed | undefined, nIncrements?: number): This<T> | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Clones the given seed or creates a new one if none is given.
|
|
25
|
+
*/
|
|
14
26
|
static cloneOrCreate<T extends ThisConstructor<typeof SeededRng>>(this: T, seed?: Seed, nIncrements?: number): This<T>;
|
|
15
27
|
static spawn<T extends ThisConstructor<typeof SeededRng>>(this: T, seed: undefined): undefined;
|
|
16
28
|
static spawn<T extends ThisConstructor<typeof SeededRng>>(this: T, seed: Seed): This<T>;
|
|
17
29
|
static spawn<T extends ThisConstructor<typeof SeededRng>>(this: T, seed: Seed | undefined): This<T> | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Given a seed-accepting function and an optional seed, returns a new function that passes the seed to the function.
|
|
32
|
+
* The spawned generator is an instance of the class on which the method is called, so subclasses supply their
|
|
33
|
+
* own sequence.
|
|
34
|
+
*/
|
|
18
35
|
static withSeed<TOptions extends object, R>(fn: (options?: OptionsWithSeed<TOptions> | OptionsWithSeed<EmptyObject>) => R, seed: Seed | undefined): (options?: TOptions) => R;
|
|
19
36
|
clone<T extends SeededRng>(this: T, nIncrements?: number): T;
|
|
37
|
+
/** Safely increments the seed by the given number of increments */
|
|
20
38
|
increment(nIncrements?: number): this;
|
|
21
39
|
next(n?: number): number;
|
|
40
|
+
/**
|
|
41
|
+
* Returns the next value in the pseudo-random sequence without incrementing the seed.
|
|
42
|
+
* For use in testing and debugging.
|
|
43
|
+
*/
|
|
22
44
|
peek(): number;
|
|
23
45
|
scaleDownSeed(seed: number): number;
|
|
24
46
|
scaleUpSeed(value: number): number;
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a seed to its numeric value, returning `undefined` when there is no seed.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
1
6
|
export declare function evaluateSeed(seed: Seed | undefined): number | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Narrows a seed to a generator.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
2
12
|
export declare function checkIsRngLike(seed: Seed | undefined): seed is SeededGenerator;
|
|
13
|
+
/**
|
|
14
|
+
* Interface describing an object that returns a sequence of numbers.
|
|
15
|
+
*/
|
|
3
16
|
export interface SeededGenerator {
|
|
4
17
|
seed: number;
|
|
5
18
|
next(): number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/toolbelt.numbers",
|
|
3
|
-
"version": "7.0
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"description": "Utility functions for working with numbers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"esm",
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"CHANGELOG.md"
|
|
47
47
|
],
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"esbuild": "0.28.2",
|
|
50
|
-
"readyup": "0.34.0",
|
|
51
49
|
"@williamthorsen/toolbelt.adoption": "0.1.0",
|
|
52
|
-
"@williamthorsen/toolbelt.testing": "0.
|
|
50
|
+
"@williamthorsen/toolbelt.testing": "0.6.0",
|
|
51
|
+
"esbuild": "0.28.2",
|
|
52
|
+
"readyup": "0.36.0"
|
|
53
53
|
},
|
|
54
54
|
"engines": {
|
|
55
55
|
"node": ">=24.0.0"
|