@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
package/dist/time.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time utilities.
|
|
3
|
+
* Provides cross-platform high-resolution timing and measurement helpers.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Node.js high-resolution timer using process.hrtime.bigint().
|
|
7
|
+
* Provides true nanosecond precision.
|
|
8
|
+
*/
|
|
9
|
+
export const timer_node = {
|
|
10
|
+
now: () => {
|
|
11
|
+
const ns = process.hrtime.bigint();
|
|
12
|
+
return Number(ns); // Native nanoseconds
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Browser high-resolution timer using performance.now().
|
|
17
|
+
* Converts milliseconds to nanoseconds for consistent API.
|
|
18
|
+
*
|
|
19
|
+
* **Precision varies by browser due to Spectre/Meltdown mitigations:**
|
|
20
|
+
* - Chrome: ~100μs (coarsened)
|
|
21
|
+
* - Firefox: ~1ms (rounded)
|
|
22
|
+
* - Safari: ~100μs
|
|
23
|
+
* - Node.js: ~1μs
|
|
24
|
+
*
|
|
25
|
+
* For nanosecond-precision benchmarks, use Node.js with `timer_node`.
|
|
26
|
+
*/
|
|
27
|
+
export const timer_browser = {
|
|
28
|
+
now: () => {
|
|
29
|
+
return performance.now() * 1_000_000; // Convert ms to ns
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Detect the best available timer function for the current environment.
|
|
34
|
+
* Called once and cached for performance.
|
|
35
|
+
*/
|
|
36
|
+
const detect_timer_fn = () => {
|
|
37
|
+
// Check if we're in Node.js with hrtime.bigint support
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
39
|
+
if (typeof process !== 'undefined' && process.hrtime) {
|
|
40
|
+
try {
|
|
41
|
+
if (typeof process.hrtime.bigint !== 'undefined') {
|
|
42
|
+
return timer_node.now;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Ignore and fall through
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Fallback to performance.now() (works in browsers and modern Node.js)
|
|
50
|
+
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
|
51
|
+
return timer_browser.now;
|
|
52
|
+
}
|
|
53
|
+
// Last resort: Date.now() (millisecond precision only)
|
|
54
|
+
return () => Date.now() * 1_000_000;
|
|
55
|
+
};
|
|
56
|
+
// Cache the detected timer function
|
|
57
|
+
let _cached_timer_fn = null;
|
|
58
|
+
const get_timer_fn = () => {
|
|
59
|
+
if (_cached_timer_fn === null) {
|
|
60
|
+
_cached_timer_fn = detect_timer_fn();
|
|
61
|
+
}
|
|
62
|
+
return _cached_timer_fn;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Auto-detected timer based on environment.
|
|
66
|
+
* Uses process.hrtime in Node.js, performance.now() in browsers.
|
|
67
|
+
* The timer function is detected once and cached for performance.
|
|
68
|
+
*/
|
|
69
|
+
export const timer_default = {
|
|
70
|
+
now: () => get_timer_fn()(),
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Time units and conversions.
|
|
74
|
+
*/
|
|
75
|
+
export const TIME_NS_PER_US = 1_000;
|
|
76
|
+
export const TIME_NS_PER_MS = 1_000_000;
|
|
77
|
+
export const TIME_NS_PER_SEC = 1_000_000_000;
|
|
78
|
+
/**
|
|
79
|
+
* Convert nanoseconds to microseconds.
|
|
80
|
+
*/
|
|
81
|
+
export const time_ns_to_us = (ns) => ns / TIME_NS_PER_US;
|
|
82
|
+
/**
|
|
83
|
+
* Convert nanoseconds to milliseconds.
|
|
84
|
+
*/
|
|
85
|
+
export const time_ns_to_ms = (ns) => ns / TIME_NS_PER_MS;
|
|
86
|
+
/**
|
|
87
|
+
* Convert nanoseconds to seconds.
|
|
88
|
+
*/
|
|
89
|
+
export const time_ns_to_sec = (ns) => ns / TIME_NS_PER_SEC;
|
|
90
|
+
/**
|
|
91
|
+
* Display labels for time units (uses proper Unicode μ for microseconds).
|
|
92
|
+
*/
|
|
93
|
+
export const TIME_UNIT_DISPLAY = { ns: 'ns', us: 'μs', ms: 'ms', s: 's' };
|
|
94
|
+
/**
|
|
95
|
+
* Detect the best time unit for a set of nanosecond values.
|
|
96
|
+
* Chooses the unit where most values fall in the range 1-9999.
|
|
97
|
+
* @param values_ns - Array of times in nanoseconds
|
|
98
|
+
* @returns Best unit to use for all values
|
|
99
|
+
*/
|
|
100
|
+
export const time_unit_detect_best = (values_ns) => {
|
|
101
|
+
if (values_ns.length === 0)
|
|
102
|
+
return 'ms';
|
|
103
|
+
// Filter out invalid values
|
|
104
|
+
const valid = values_ns.filter((v) => isFinite(v) && v > 0);
|
|
105
|
+
if (valid.length === 0)
|
|
106
|
+
return 'ms';
|
|
107
|
+
// Find the median value (more stable than mean for outliers)
|
|
108
|
+
const sorted = [...valid].sort((a, b) => a - b);
|
|
109
|
+
const median = sorted[Math.floor(sorted.length / 2)];
|
|
110
|
+
// Choose unit based on median magnitude
|
|
111
|
+
if (median < 1_000) {
|
|
112
|
+
return 'ns'; // < 1μs
|
|
113
|
+
}
|
|
114
|
+
else if (median < 1_000_000) {
|
|
115
|
+
return 'us'; // < 1ms
|
|
116
|
+
}
|
|
117
|
+
else if (median < 1_000_000_000) {
|
|
118
|
+
return 'ms'; // < 1s
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
return 's'; // >= 1s
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Format time with a specific unit.
|
|
126
|
+
* @param ns - Time in nanoseconds
|
|
127
|
+
* @param unit - Unit to use ('ns', 'us', 'ms', 's')
|
|
128
|
+
* @param decimals - Number of decimal places (default: 2)
|
|
129
|
+
* @returns Formatted string like "3.87μs"
|
|
130
|
+
*/
|
|
131
|
+
export const time_format = (ns, unit, decimals = 2) => {
|
|
132
|
+
if (!isFinite(ns))
|
|
133
|
+
return String(ns);
|
|
134
|
+
switch (unit) {
|
|
135
|
+
case 'ns':
|
|
136
|
+
return `${ns.toFixed(decimals)}ns`;
|
|
137
|
+
case 'us':
|
|
138
|
+
return `${time_ns_to_us(ns).toFixed(decimals)}μs`;
|
|
139
|
+
case 'ms':
|
|
140
|
+
return `${time_ns_to_ms(ns).toFixed(decimals)}ms`;
|
|
141
|
+
case 's':
|
|
142
|
+
return `${time_ns_to_sec(ns).toFixed(decimals)}s`;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Format time with adaptive units (ns/μs/ms/s) based on magnitude.
|
|
147
|
+
* @param ns - Time in nanoseconds
|
|
148
|
+
* @param decimals - Number of decimal places (default: 2)
|
|
149
|
+
* @returns Formatted string like "3.87μs" or "1.23ms"
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* time_format_adaptive(1500) // "1.50μs"
|
|
154
|
+
* time_format_adaptive(3870) // "3.87μs"
|
|
155
|
+
* time_format_adaptive(1500000) // "1.50ms"
|
|
156
|
+
* time_format_adaptive(1500000000) // "1.50s"
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export const time_format_adaptive = (ns, decimals = 2) => {
|
|
160
|
+
if (!isFinite(ns))
|
|
161
|
+
return String(ns);
|
|
162
|
+
// Choose unit based on magnitude
|
|
163
|
+
if (ns < 1_000) {
|
|
164
|
+
return time_format(ns, 'ns', decimals);
|
|
165
|
+
}
|
|
166
|
+
else if (ns < 1_000_000) {
|
|
167
|
+
return time_format(ns, 'us', decimals);
|
|
168
|
+
}
|
|
169
|
+
else if (ns < 1_000_000_000) {
|
|
170
|
+
return time_format(ns, 'ms', decimals);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
return time_format(ns, 's', decimals);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Time an asynchronous function execution.
|
|
178
|
+
* @param fn - Async function to time
|
|
179
|
+
* @param timer - Timer to use (defaults to timer_default)
|
|
180
|
+
* @returns Object containing the function result and timing information
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* const {result, timing} = await time_async(async () => {
|
|
185
|
+
* await fetch('https://api.example.com/data');
|
|
186
|
+
* return 42;
|
|
187
|
+
* });
|
|
188
|
+
* console.log(`Result: ${result}, took ${time_format_adaptive(timing.elapsed_ns)}`);
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export const time_async = async (fn, timer = timer_default) => {
|
|
192
|
+
const started_at_ns = timer.now();
|
|
193
|
+
const result = await fn();
|
|
194
|
+
const ended_at_ns = timer.now();
|
|
195
|
+
const elapsed_ns = ended_at_ns - started_at_ns;
|
|
196
|
+
return {
|
|
197
|
+
result,
|
|
198
|
+
timing: {
|
|
199
|
+
elapsed_ns,
|
|
200
|
+
elapsed_us: time_ns_to_us(elapsed_ns),
|
|
201
|
+
elapsed_ms: time_ns_to_ms(elapsed_ns),
|
|
202
|
+
started_at_ns,
|
|
203
|
+
ended_at_ns,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Time a synchronous function execution.
|
|
209
|
+
* @param fn - Sync function to time
|
|
210
|
+
* @param timer - Timer to use (defaults to timer_default)
|
|
211
|
+
* @returns Object containing the function result and timing information
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```ts
|
|
215
|
+
* const {result, timing} = time_sync(() => {
|
|
216
|
+
* return expensive_computation();
|
|
217
|
+
* });
|
|
218
|
+
* console.log(`Result: ${result}, took ${time_format_adaptive(timing.elapsed_ns)}`);
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
export const time_sync = (fn, timer = timer_default) => {
|
|
222
|
+
const started_at_ns = timer.now();
|
|
223
|
+
const result = fn();
|
|
224
|
+
const ended_at_ns = timer.now();
|
|
225
|
+
const elapsed_ns = ended_at_ns - started_at_ns;
|
|
226
|
+
return {
|
|
227
|
+
result,
|
|
228
|
+
timing: {
|
|
229
|
+
elapsed_ns,
|
|
230
|
+
elapsed_us: time_ns_to_us(elapsed_ns),
|
|
231
|
+
elapsed_ms: time_ns_to_ms(elapsed_ns),
|
|
232
|
+
started_at_ns,
|
|
233
|
+
ended_at_ns,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Measure multiple executions of a function and return all timings.
|
|
239
|
+
* @param fn - Function to measure (sync or async)
|
|
240
|
+
* @param iterations - Number of times to execute
|
|
241
|
+
* @param timer - Timer to use (defaults to timer_default)
|
|
242
|
+
* @returns Array of elapsed times in nanoseconds
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* const timings_ns = await time_measure(async () => {
|
|
247
|
+
* await process_data();
|
|
248
|
+
* }, 100);
|
|
249
|
+
*
|
|
250
|
+
* import {BenchmarkStats} from './benchmark_stats.js';
|
|
251
|
+
* const stats = new BenchmarkStats(timings_ns);
|
|
252
|
+
* console.log(`Mean: ${time_format_adaptive(stats.mean_ns)}`);
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
export const time_measure = async (fn, iterations, timer = timer_default) => {
|
|
256
|
+
const timings = [];
|
|
257
|
+
for (let i = 0; i < iterations; i++) {
|
|
258
|
+
const started_at_ns = timer.now();
|
|
259
|
+
await fn(); // eslint-disable-line no-await-in-loop
|
|
260
|
+
const ended_at_ns = timer.now();
|
|
261
|
+
timings.push(ended_at_ns - started_at_ns);
|
|
262
|
+
}
|
|
263
|
+
return timings;
|
|
264
|
+
};
|
package/dist/timings.d.ts
CHANGED
|
@@ -9,19 +9,13 @@ export type TimingsKey = string | number;
|
|
|
9
9
|
* Allows starting, stopping, and retrieving timings with optional precision.
|
|
10
10
|
*/
|
|
11
11
|
export declare class Timings {
|
|
12
|
+
#private;
|
|
12
13
|
readonly decimals: number | undefined;
|
|
13
|
-
private readonly timings;
|
|
14
|
-
private readonly stopwatches;
|
|
15
14
|
constructor(decimals?: number);
|
|
16
15
|
/**
|
|
17
16
|
* Starts a timing operation for the given key.
|
|
18
17
|
*/
|
|
19
18
|
start(key: TimingsKey, decimals?: number | undefined): () => number;
|
|
20
|
-
private next_key;
|
|
21
|
-
/**
|
|
22
|
-
* Stops a timing operation and records the elapsed time.
|
|
23
|
-
*/
|
|
24
|
-
private stop;
|
|
25
19
|
get(key: TimingsKey): number;
|
|
26
20
|
entries(): IterableIterator<[TimingsKey, number | undefined]>;
|
|
27
21
|
/**
|
package/dist/timings.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timings.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/timings.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,MAAM,CAAC;AAEpD;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAAI,iBAAY,KAAG,SAQ/C,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzC;;;GAGG;AACH,qBAAa,OAAO
|
|
1
|
+
{"version":3,"file":"timings.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/timings.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,MAAM,CAAC;AAEpD;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAAI,iBAAY,KAAG,SAQ/C,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzC;;;GAGG;AACH,qBAAa,OAAO;;IACnB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;gBAK1B,QAAQ,CAAC,EAAE,MAAM;IAI7B;;OAEG;IACH,KAAK,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,qBAAgB,GAAG,MAAM,MAAM;IA4B9D,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM;IAM5B,OAAO,IAAI,gBAAgB,CAAC,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAI7D;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAc7B"}
|
package/dist/timings.js
CHANGED
|
@@ -18,8 +18,8 @@ export const create_stopwatch = (decimals = 2) => {
|
|
|
18
18
|
*/
|
|
19
19
|
export class Timings {
|
|
20
20
|
decimals;
|
|
21
|
-
timings = new Map();
|
|
22
|
-
stopwatches = new Map();
|
|
21
|
+
#timings = new Map();
|
|
22
|
+
#stopwatches = new Map();
|
|
23
23
|
constructor(decimals) {
|
|
24
24
|
this.decimals = decimals;
|
|
25
25
|
}
|
|
@@ -27,41 +27,41 @@ export class Timings {
|
|
|
27
27
|
* Starts a timing operation for the given key.
|
|
28
28
|
*/
|
|
29
29
|
start(key, decimals = this.decimals) {
|
|
30
|
-
const final_key = this
|
|
31
|
-
this
|
|
32
|
-
this
|
|
33
|
-
return () => this
|
|
30
|
+
const final_key = this.#next_key(key);
|
|
31
|
+
this.#stopwatches.set(final_key, create_stopwatch(decimals));
|
|
32
|
+
this.#timings.set(final_key, undefined); // initializing to preserve order
|
|
33
|
+
return () => this.#stop(final_key);
|
|
34
34
|
}
|
|
35
|
-
next_key(key) {
|
|
36
|
-
if (!this
|
|
35
|
+
#next_key(key) {
|
|
36
|
+
if (!this.#stopwatches.has(key))
|
|
37
37
|
return key;
|
|
38
38
|
let i = 2;
|
|
39
39
|
while (true) {
|
|
40
40
|
const next = key + '_' + i++;
|
|
41
|
-
if (!this
|
|
41
|
+
if (!this.#stopwatches.has(next))
|
|
42
42
|
return next;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
46
|
* Stops a timing operation and records the elapsed time.
|
|
47
47
|
*/
|
|
48
|
-
stop(key) {
|
|
49
|
-
const stopwatch = this
|
|
48
|
+
#stop(key) {
|
|
49
|
+
const stopwatch = this.#stopwatches.get(key);
|
|
50
50
|
if (!stopwatch)
|
|
51
51
|
return 0; // TODO maybe warn?
|
|
52
|
-
this
|
|
52
|
+
this.#stopwatches.delete(key);
|
|
53
53
|
const timing = stopwatch();
|
|
54
|
-
this
|
|
54
|
+
this.#timings.set(key, timing);
|
|
55
55
|
return timing;
|
|
56
56
|
}
|
|
57
57
|
get(key) {
|
|
58
|
-
const timing = this
|
|
58
|
+
const timing = this.#timings.get(key);
|
|
59
59
|
if (timing === undefined)
|
|
60
60
|
return 0; // TODO maybe warn?
|
|
61
61
|
return timing;
|
|
62
62
|
}
|
|
63
63
|
entries() {
|
|
64
|
-
return this
|
|
64
|
+
return this.#timings.entries();
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
67
|
* Merges other timings into this one,
|
|
@@ -69,7 +69,7 @@ export class Timings {
|
|
|
69
69
|
*/
|
|
70
70
|
merge(timings) {
|
|
71
71
|
for (const [key, timing] of timings.entries()) {
|
|
72
|
-
this
|
|
72
|
+
this.#timings.set(key, timing === undefined ? undefined : (this.#timings.get(key) ?? 0) + timing);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzdev/fuz_util",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"description": "utility belt for JS",
|
|
5
5
|
"glyph": "🦕",
|
|
6
6
|
"logo": "logo.svg",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"motto": "ancient not extinct",
|
|
9
9
|
"public": true,
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"homepage": "https://
|
|
11
|
+
"homepage": "https://util.fuz.dev/",
|
|
12
12
|
"author": {
|
|
13
13
|
"name": "Ryan Atkinson",
|
|
14
14
|
"email": "mail@ryanatkn.com",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/
|
|
19
|
+
"url": "git+https://github.com/fuzdev/fuz_util.git"
|
|
20
20
|
},
|
|
21
|
-
"bugs": "https://github.com/
|
|
21
|
+
"bugs": "https://github.com/fuzdev/fuz_util/issues",
|
|
22
22
|
"funding": "https://www.ryanatkn.com/funding",
|
|
23
23
|
"scripts": {
|
|
24
24
|
"start": "gro dev",
|
|
@@ -28,9 +28,11 @@
|
|
|
28
28
|
"test": "gro test",
|
|
29
29
|
"preview": "vite preview",
|
|
30
30
|
"deploy": "gro deploy",
|
|
31
|
-
"benchmark": "gro run src/benchmarks/
|
|
32
|
-
"benchmark
|
|
33
|
-
"
|
|
31
|
+
"benchmark": "gro run src/benchmarks/run.ts",
|
|
32
|
+
"benchmark:save": "gro run src/benchmarks/run.ts --save",
|
|
33
|
+
"benchmark_slugify": "gro run src/benchmarks/slugify.benchmark.ts",
|
|
34
|
+
"benchmark_deep_equal": "gro run src/benchmarks/deep_equal.benchmark.ts",
|
|
35
|
+
"benchmark_deep_equal_comparison": "gro run src/benchmarks/deep_equal_comparison.benchmark.ts"
|
|
34
36
|
},
|
|
35
37
|
"type": "module",
|
|
36
38
|
"engines": {
|
|
@@ -60,29 +62,29 @@
|
|
|
60
62
|
},
|
|
61
63
|
"devDependencies": {
|
|
62
64
|
"@changesets/changelog-git": "^0.2.1",
|
|
65
|
+
"@fuzdev/fuz_code": "^0.38.0",
|
|
66
|
+
"@fuzdev/fuz_css": "^0.42.1",
|
|
67
|
+
"@fuzdev/fuz_ui": "^0.172.0",
|
|
63
68
|
"@ryanatkn/eslint-config": "^0.9.0",
|
|
64
|
-
"@ryanatkn/
|
|
65
|
-
"@ryanatkn/fuz_code": "^0.36.0",
|
|
66
|
-
"@ryanatkn/gro": "^0.179.0",
|
|
67
|
-
"@ryanatkn/moss": "^0.39.0",
|
|
69
|
+
"@ryanatkn/gro": "^0.181.0",
|
|
68
70
|
"@sveltejs/adapter-static": "^3.0.10",
|
|
69
|
-
"@sveltejs/kit": "^2.49.
|
|
70
|
-
"@sveltejs/package": "^2.5.
|
|
71
|
+
"@sveltejs/kit": "^2.49.1",
|
|
72
|
+
"@sveltejs/package": "^2.5.7",
|
|
71
73
|
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
|
72
74
|
"@types/node": "^24.10.1",
|
|
73
75
|
"dequal": "^2.0.3",
|
|
74
76
|
"eslint": "^9.39.1",
|
|
75
|
-
"eslint-plugin-svelte": "^3.13.
|
|
77
|
+
"eslint-plugin-svelte": "^3.13.1",
|
|
76
78
|
"esm-env": "^1.2.2",
|
|
77
|
-
"
|
|
79
|
+
"fast-deep-equal": "^3.1.3",
|
|
80
|
+
"prettier": "^3.7.4",
|
|
78
81
|
"prettier-plugin-svelte": "^3.4.0",
|
|
79
|
-
"svelte": "^5.
|
|
82
|
+
"svelte": "^5.45.6",
|
|
80
83
|
"svelte-check": "^4.3.4",
|
|
81
|
-
"tinybench": "^5.1.0",
|
|
82
84
|
"tslib": "^2.8.1",
|
|
83
85
|
"typescript": "^5.9.3",
|
|
84
|
-
"typescript-eslint": "^8.48.
|
|
85
|
-
"vitest": "^4.0.
|
|
86
|
+
"typescript-eslint": "^8.48.1",
|
|
87
|
+
"vitest": "^4.0.15",
|
|
86
88
|
"zod": "^4.1.13"
|
|
87
89
|
},
|
|
88
90
|
"prettier": {
|
package/src/lib/async.ts
CHANGED
|
@@ -7,10 +7,10 @@ export const wait = (duration = 0): Promise<void> =>
|
|
|
7
7
|
new Promise((resolve) => setTimeout(resolve, duration));
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Checks if `value` is a `Promise
|
|
10
|
+
* Checks if `value` is a `Promise` (or thenable).
|
|
11
11
|
*/
|
|
12
|
-
export const is_promise = (value:
|
|
13
|
-
value && typeof value.then === 'function';
|
|
12
|
+
export const is_promise = (value: unknown): value is Promise<unknown> =>
|
|
13
|
+
value != null && typeof (value as Promise<unknown>).then === 'function';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Creates a deferred object with a promise and its resolve/reject handlers.
|