@fuzdev/fuz_util 0.42.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +19 -12
- package/dist/async.d.ts +2 -2
- package/dist/async.d.ts.map +1 -1
- package/dist/async.js +2 -2
- package/dist/benchmark.d.ts +179 -0
- package/dist/benchmark.d.ts.map +1 -0
- package/dist/benchmark.js +400 -0
- package/dist/benchmark_baseline.d.ts +195 -0
- package/dist/benchmark_baseline.d.ts.map +1 -0
- package/dist/benchmark_baseline.js +388 -0
- package/dist/benchmark_format.d.ts +87 -0
- package/dist/benchmark_format.d.ts.map +1 -0
- package/dist/benchmark_format.js +266 -0
- package/dist/benchmark_stats.d.ts +112 -0
- package/dist/benchmark_stats.d.ts.map +1 -0
- package/dist/benchmark_stats.js +219 -0
- package/dist/benchmark_types.d.ts +174 -0
- package/dist/benchmark_types.d.ts.map +1 -0
- package/dist/benchmark_types.js +1 -0
- package/dist/git.d.ts +12 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +14 -0
- package/dist/library_json.d.ts +3 -3
- package/dist/library_json.d.ts.map +1 -1
- package/dist/library_json.js +1 -1
- package/dist/maths.d.ts +4 -0
- package/dist/maths.d.ts.map +1 -1
- package/dist/maths.js +8 -0
- package/dist/object.js +1 -1
- package/dist/source_json.d.ts +4 -4
- package/dist/stats.d.ts +180 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +402 -0
- package/dist/string.d.ts +13 -0
- package/dist/string.d.ts.map +1 -1
- package/dist/string.js +58 -0
- package/dist/time.d.ts +165 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +264 -0
- package/dist/timings.d.ts +1 -7
- package/dist/timings.d.ts.map +1 -1
- package/dist/timings.js +16 -16
- package/package.json +21 -19
- package/src/lib/async.ts +3 -3
- package/src/lib/benchmark.ts +498 -0
- package/src/lib/benchmark_baseline.ts +538 -0
- package/src/lib/benchmark_format.ts +314 -0
- package/src/lib/benchmark_stats.ts +311 -0
- package/src/lib/benchmark_types.ts +197 -0
- package/src/lib/git.ts +24 -0
- package/src/lib/library_json.ts +3 -3
- package/src/lib/maths.ts +8 -0
- package/src/lib/object.ts +1 -1
- package/src/lib/stats.ts +534 -0
- package/src/lib/string.ts +66 -0
- package/src/lib/time.ts +319 -0
- package/src/lib/timings.ts +17 -17
- package/src/lib/types.ts +2 -2
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type {BenchmarkStats} from './benchmark_stats.js';
|
|
2
|
+
import type {Timer} from './time.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration options for a benchmark suite.
|
|
6
|
+
*/
|
|
7
|
+
export interface BenchmarkConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Target duration to run each benchmark task in milliseconds.
|
|
10
|
+
* The benchmark will run until this duration is reached or max_iterations is hit.
|
|
11
|
+
* Default: 1000ms
|
|
12
|
+
*/
|
|
13
|
+
duration_ms?: number;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Number of warmup iterations before actual measurements.
|
|
17
|
+
* Warmup helps stabilize JIT compilation and caches.
|
|
18
|
+
* Default: 5
|
|
19
|
+
*/
|
|
20
|
+
warmup_iterations?: number;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Cooldown time between tasks in milliseconds.
|
|
24
|
+
* Helps prevent interference between benchmarks.
|
|
25
|
+
* Default: 100ms
|
|
26
|
+
*/
|
|
27
|
+
cooldown_ms?: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Minimum number of iterations to run.
|
|
31
|
+
* Default: 10
|
|
32
|
+
*/
|
|
33
|
+
min_iterations?: number;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Maximum number of iterations to run.
|
|
37
|
+
* Prevents infinite loops if function is extremely fast.
|
|
38
|
+
* Default: 100000
|
|
39
|
+
*/
|
|
40
|
+
max_iterations?: number;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Custom timer to use for measurements.
|
|
44
|
+
* Default: timer_default (auto-detects environment)
|
|
45
|
+
*/
|
|
46
|
+
timer?: Timer;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Callback invoked after each iteration completes.
|
|
50
|
+
* Useful for triggering garbage collection, logging progress, early termination,
|
|
51
|
+
* or custom instrumentation.
|
|
52
|
+
*
|
|
53
|
+
* **Note**: The callback time is NOT included in iteration measurements - it runs
|
|
54
|
+
* after the timing capture. However, frequent GC calls will slow overall benchmark
|
|
55
|
+
* execution time.
|
|
56
|
+
*
|
|
57
|
+
* @param task_name - Name of the current task being benchmarked
|
|
58
|
+
* @param iteration - Current iteration number (1-indexed)
|
|
59
|
+
* @param abort - Call to stop the benchmark early for this task
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* // Trigger GC between iterations (run node with --expose-gc)
|
|
64
|
+
* new Benchmark({
|
|
65
|
+
* on_iteration: () => {
|
|
66
|
+
* if (globalThis.gc) globalThis.gc();
|
|
67
|
+
* }
|
|
68
|
+
* })
|
|
69
|
+
*
|
|
70
|
+
* // Log progress for long-running benchmarks
|
|
71
|
+
* new Benchmark({
|
|
72
|
+
* on_iteration: (name, iteration) => {
|
|
73
|
+
* if (iteration % 1000 === 0) {
|
|
74
|
+
* console.log(`${name}: ${iteration} iterations`);
|
|
75
|
+
* }
|
|
76
|
+
* }
|
|
77
|
+
* })
|
|
78
|
+
*
|
|
79
|
+
* // Stop early when converged
|
|
80
|
+
* new Benchmark({
|
|
81
|
+
* on_iteration: (name, iteration, abort) => {
|
|
82
|
+
* if (iteration > 1000 && has_stabilized()) abort();
|
|
83
|
+
* }
|
|
84
|
+
* })
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
on_iteration?: (task_name: string, iteration: number, abort: () => void) => void;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Callback invoked after each task completes.
|
|
91
|
+
* Useful for logging progress during long benchmark runs.
|
|
92
|
+
*
|
|
93
|
+
* @param result - The completed benchmark result
|
|
94
|
+
* @param index - Zero-based index of the completed task
|
|
95
|
+
* @param total - Total number of tasks to run
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* new Benchmark({
|
|
100
|
+
* on_task_complete: (result, index, total) => {
|
|
101
|
+
* console.log(`[${index + 1}/${total}] ${result.name}: ${result.stats.ops_per_second.toFixed(0)} ops/sec`);
|
|
102
|
+
* }
|
|
103
|
+
* })
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
on_task_complete?: (result: BenchmarkResult, index: number, total: number) => void;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A benchmark task to execute.
|
|
111
|
+
*/
|
|
112
|
+
export interface BenchmarkTask {
|
|
113
|
+
/** Name of the task (for display) */
|
|
114
|
+
name: string;
|
|
115
|
+
|
|
116
|
+
/** Function to benchmark (sync or async). Return values are ignored. */
|
|
117
|
+
fn: () => unknown;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Optional setup function run before benchmarking this task.
|
|
121
|
+
* Not included in timing measurements.
|
|
122
|
+
*/
|
|
123
|
+
setup?: () => void | Promise<void>;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Optional teardown function run after benchmarking this task.
|
|
127
|
+
* Not included in timing measurements.
|
|
128
|
+
*/
|
|
129
|
+
teardown?: () => void | Promise<void>;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* If true, skip this task during benchmark runs.
|
|
133
|
+
* Useful for temporarily disabling tasks during development.
|
|
134
|
+
*/
|
|
135
|
+
skip?: boolean;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* If true, run only this task (and other tasks marked `only`).
|
|
139
|
+
* Useful for focusing on specific tasks during development.
|
|
140
|
+
*/
|
|
141
|
+
only?: boolean;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Hint for whether the function is sync or async.
|
|
145
|
+
* If not provided, automatically detected during warmup.
|
|
146
|
+
* Setting this explicitly skips per-iteration promise checking for sync functions.
|
|
147
|
+
*/
|
|
148
|
+
async?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Result from running a single benchmark task.
|
|
153
|
+
*/
|
|
154
|
+
export interface BenchmarkResult {
|
|
155
|
+
/** Task name */
|
|
156
|
+
name: string;
|
|
157
|
+
|
|
158
|
+
/** Statistical analysis of the benchmark */
|
|
159
|
+
stats: BenchmarkStats;
|
|
160
|
+
|
|
161
|
+
/** Number of iterations executed */
|
|
162
|
+
iterations: number;
|
|
163
|
+
|
|
164
|
+
/** Total time spent benchmarking (including warmup) in milliseconds */
|
|
165
|
+
total_time_ms: number;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Raw timing data for each iteration in nanoseconds.
|
|
169
|
+
* Useful for custom statistical analysis, histogram generation,
|
|
170
|
+
* or exporting to external tools.
|
|
171
|
+
*/
|
|
172
|
+
timings_ns: Array<number>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Options for table formatting.
|
|
177
|
+
*/
|
|
178
|
+
export interface BenchmarkFormatTableOptions {
|
|
179
|
+
/**
|
|
180
|
+
* Group results by category using filter functions.
|
|
181
|
+
*/
|
|
182
|
+
groups?: Array<BenchmarkGroup>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* A group definition for organizing benchmark results.
|
|
187
|
+
*/
|
|
188
|
+
export interface BenchmarkGroup {
|
|
189
|
+
/** Display name for the group */
|
|
190
|
+
name: string;
|
|
191
|
+
|
|
192
|
+
/** Optional description shown below the group name */
|
|
193
|
+
description?: string;
|
|
194
|
+
|
|
195
|
+
/** Filter function to determine which results belong to this group */
|
|
196
|
+
filter: (result: BenchmarkResult) => boolean;
|
|
197
|
+
}
|
package/src/lib/git.ts
CHANGED
|
@@ -6,6 +6,30 @@ import type {Flavored} from './types.js';
|
|
|
6
6
|
import {to_file_path} from './path.js';
|
|
7
7
|
import {fs_exists} from './fs.js';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Basic git repository info.
|
|
11
|
+
*/
|
|
12
|
+
export interface GitInfo {
|
|
13
|
+
commit: string | null;
|
|
14
|
+
branch: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get basic git info (commit hash and branch name) without throwing.
|
|
19
|
+
* Returns null values if git commands fail (e.g., not in a git repo).
|
|
20
|
+
*/
|
|
21
|
+
export const git_info_get = async (options?: SpawnOptions): Promise<GitInfo> => {
|
|
22
|
+
const [commit_result, branch_result] = await Promise.all([
|
|
23
|
+
spawn_out('git', ['rev-parse', 'HEAD'], options).catch(() => ({stdout: null})),
|
|
24
|
+
spawn_out('git', ['rev-parse', '--abbrev-ref', 'HEAD'], options).catch(() => ({stdout: null})),
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
commit: commit_result.stdout?.trim() || null,
|
|
29
|
+
branch: branch_result.stdout?.trim() || null,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
9
33
|
export const GitOrigin = z.string();
|
|
10
34
|
export type GitOrigin = Flavored<string, 'GitOrigin'>;
|
|
11
35
|
|
package/src/lib/library_json.ts
CHANGED
|
@@ -13,11 +13,11 @@ import type {Url} from './url.js';
|
|
|
13
13
|
export interface LibraryJson {
|
|
14
14
|
package_json: PackageJson;
|
|
15
15
|
source_json: SourceJson;
|
|
16
|
-
/** Package name, e.g. `@
|
|
16
|
+
/** Package name, e.g. `@fuzdev/fuz_ui`. */
|
|
17
17
|
name: string;
|
|
18
18
|
/** Name without scope, e.g. `fuz`. */
|
|
19
19
|
repo_name: string;
|
|
20
|
-
/** GitHub repo URL, e.g. `https://github.com/
|
|
20
|
+
/** GitHub repo URL, e.g. `https://github.com/fuzdev/fuz_ui`. */
|
|
21
21
|
repo_url: Url;
|
|
22
22
|
/** GitHub user/org, e.g. `ryanatkn`. */
|
|
23
23
|
owner_name: string | null;
|
|
@@ -95,7 +95,7 @@ export const library_json_parse = (
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
* Extracts repo name from a package name, e.g. `@
|
|
98
|
+
* Extracts repo name from a package name, e.g. `@fuzdev/fuz_ui` → `fuz`.
|
|
99
99
|
*/
|
|
100
100
|
export const library_repo_name_parse = (name: string): string => {
|
|
101
101
|
if (name[0] === '@') {
|
package/src/lib/maths.ts
CHANGED
|
@@ -89,3 +89,11 @@ export const GR_9 = 76.01315561749645;
|
|
|
89
89
|
* golden ratio/mean constants, `1/(GR**9)`, useful for scaling: https://wikipedia.org/wiki/Golden_ratio
|
|
90
90
|
*/
|
|
91
91
|
export const GR_9i = 0.013155617496424835;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Format a number with fixed decimal places and thousands separators.
|
|
95
|
+
*/
|
|
96
|
+
export const format_number = (n: number, decimals: number = 2): string => {
|
|
97
|
+
if (!isFinite(n)) return String(n);
|
|
98
|
+
return n.toFixed(decimals).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
99
|
+
};
|
package/src/lib/object.ts
CHANGED
|
@@ -85,7 +85,7 @@ export const reorder = <T extends Record<K, any>, K extends string | number>(
|
|
|
85
85
|
/**
|
|
86
86
|
* Frozen empty object with no properties, good for options default values.
|
|
87
87
|
*/
|
|
88
|
-
export const EMPTY_OBJECT: Record<string | number | symbol, undefined> & object = Object.freeze({});
|
|
88
|
+
export const EMPTY_OBJECT: Record<string | number | symbol, undefined> & object = Object.freeze({});
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
91
|
* Performs a depth-first traversal of an object's enumerable properties,
|