qznt 2.1.1 → 2.2.1
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/dist/index.cjs +49 -3
- package/dist/index.d.cts +33 -7
- package/dist/index.d.ts +33 -7
- package/dist/index.js +48 -3
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -63,6 +63,7 @@ __export(index_exports, {
|
|
|
63
63
|
isSortedArray: () => isSortedArray,
|
|
64
64
|
isString: () => isString,
|
|
65
65
|
isToday: () => isToday,
|
|
66
|
+
keyedQueue: () => keyedQueue,
|
|
66
67
|
lerp: () => lerp,
|
|
67
68
|
memoize: () => memoize,
|
|
68
69
|
merge: () => merge,
|
|
@@ -604,6 +605,24 @@ function unique(array, key) {
|
|
|
604
605
|
}
|
|
605
606
|
|
|
606
607
|
// src/async.ts
|
|
608
|
+
function keyedQueue() {
|
|
609
|
+
const queues = /* @__PURE__ */ new Map();
|
|
610
|
+
return {
|
|
611
|
+
run(key, work) {
|
|
612
|
+
const previous = queues.get(key) ?? Promise.resolve();
|
|
613
|
+
const result = previous.then(work);
|
|
614
|
+
const release = () => {
|
|
615
|
+
if (queues.get(key) === tail) queues.delete(key);
|
|
616
|
+
};
|
|
617
|
+
const tail = result.then(release, release);
|
|
618
|
+
queues.set(key, tail);
|
|
619
|
+
return result;
|
|
620
|
+
},
|
|
621
|
+
get size() {
|
|
622
|
+
return queues.size;
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
}
|
|
607
626
|
async function retryPromise(fn, options = {}) {
|
|
608
627
|
const { attempts = 3, delay = 500, timeout, signal } = options;
|
|
609
628
|
if (signal?.aborted) throw new Error("Operation aborted");
|
|
@@ -719,9 +738,28 @@ function throttle(fn, limit) {
|
|
|
719
738
|
function memoize(fn, options = {}) {
|
|
720
739
|
const cache = /* @__PURE__ */ new Map();
|
|
721
740
|
const { resolver, maxAge } = options;
|
|
741
|
+
let lastSweep = Date.now();
|
|
742
|
+
const getKey = (...args) => resolver ? resolver(...args) : JSON.stringify(args);
|
|
743
|
+
const sweep = (now) => {
|
|
744
|
+
if (!maxAge || now - lastSweep < maxAge) return;
|
|
745
|
+
lastSweep = now;
|
|
746
|
+
for (const [key, entry] of cache) {
|
|
747
|
+
if (now - entry.timestamp >= maxAge) cache.delete(key);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
const cacheValue = (key, value, timestamp) => {
|
|
751
|
+
const entry = { value, timestamp };
|
|
752
|
+
cache.set(key, entry);
|
|
753
|
+
if (value !== null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function") {
|
|
754
|
+
void Promise.resolve(value).catch(() => {
|
|
755
|
+
if (cache.get(key) === entry) cache.delete(key);
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
};
|
|
722
759
|
const memoized = function(...args) {
|
|
723
|
-
const key =
|
|
760
|
+
const key = getKey(...args);
|
|
724
761
|
const now = Date.now();
|
|
762
|
+
sweep(now);
|
|
725
763
|
const entry = cache.get(key);
|
|
726
764
|
if (entry) {
|
|
727
765
|
if (!maxAge || now - entry.timestamp < maxAge) {
|
|
@@ -730,10 +768,17 @@ function memoize(fn, options = {}) {
|
|
|
730
768
|
cache.delete(key);
|
|
731
769
|
}
|
|
732
770
|
const result = fn.apply(this, args);
|
|
733
|
-
|
|
771
|
+
cacheValue(key, result, now);
|
|
734
772
|
return result;
|
|
735
773
|
};
|
|
736
774
|
memoized.clear = () => cache.clear();
|
|
775
|
+
memoized.delete = (...args) => cache.delete(getKey(...args));
|
|
776
|
+
memoized.set = (value, ...args) => {
|
|
777
|
+
const key = getKey(...args);
|
|
778
|
+
const now = Date.now();
|
|
779
|
+
sweep(now);
|
|
780
|
+
cacheValue(key, value, now);
|
|
781
|
+
};
|
|
737
782
|
return memoized;
|
|
738
783
|
}
|
|
739
784
|
|
|
@@ -784,7 +829,7 @@ function formatDuration(target, style = "hms", options = {}) {
|
|
|
784
829
|
if (diff === 0) return style === "digital" ? "00:00" : "now";
|
|
785
830
|
const s = Math.floor(diff / 1e3) % 60;
|
|
786
831
|
const m = Math.floor(diff / 6e4) % 60;
|
|
787
|
-
const h = Math.floor(diff / 36e5) % 24;
|
|
832
|
+
const h = style === "hms" ? Math.floor(diff / 36e5) : Math.floor(diff / 36e5) % 24;
|
|
788
833
|
const d = Math.floor(diff / 864e5);
|
|
789
834
|
if (style === "digital") {
|
|
790
835
|
const parts = [m, s].map((v) => String(v).padStart(2, "0"));
|
|
@@ -1080,6 +1125,7 @@ function toTitleCase(str, smart = true) {
|
|
|
1080
1125
|
isSortedArray,
|
|
1081
1126
|
isString,
|
|
1082
1127
|
isToday,
|
|
1128
|
+
keyedQueue,
|
|
1083
1129
|
lerp,
|
|
1084
1130
|
memoize,
|
|
1085
1131
|
merge,
|
package/dist/index.d.cts
CHANGED
|
@@ -208,6 +208,20 @@ interface RetryOptions {
|
|
|
208
208
|
/** An AbortSignal to cancel the entire operation. */
|
|
209
209
|
signal?: AbortSignal;
|
|
210
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Creates a queue that runs tasks in call order for each key, while different keys run concurrently.
|
|
213
|
+
* Task errors reach their callers without blocking later tasks. Drained keys are removed automatically.
|
|
214
|
+
* @example
|
|
215
|
+
* const queue = keyedQueue();
|
|
216
|
+
* const first = queue.run("user-1", () => saveInventory("user-1"));
|
|
217
|
+
* const second = queue.run("user-1", () => readInventory("user-1"));
|
|
218
|
+
* await Promise.all([first, second]); // The read runs after the save finishes
|
|
219
|
+
* console.log(queue.size); // 0 active keys
|
|
220
|
+
*/
|
|
221
|
+
declare function keyedQueue(): {
|
|
222
|
+
run<T>(key: string | number, work: () => T | Promise<T>): Promise<T>;
|
|
223
|
+
readonly size: number;
|
|
224
|
+
};
|
|
211
225
|
/**
|
|
212
226
|
* Retries an async function until the maximum number of attempts is reached.
|
|
213
227
|
*
|
|
@@ -275,17 +289,23 @@ declare function debounce<T extends (...args: unknown[]) => unknown>(fn: T, wait
|
|
|
275
289
|
*/
|
|
276
290
|
declare function throttle<T extends (...args: unknown[]) => unknown>(fn: T, limit: number): (...args: Parameters<T>) => void;
|
|
277
291
|
|
|
278
|
-
interface MemoizedFunction<T extends (...args:
|
|
292
|
+
interface MemoizedFunction<T extends (...args: never[]) => unknown> {
|
|
279
293
|
(...args: Parameters<T>): ReturnType<T>;
|
|
280
294
|
/** Clears the cache. */
|
|
281
295
|
clear: () => void;
|
|
296
|
+
/** Removes the entry for these arguments, returning whether it existed. */
|
|
297
|
+
delete: (...args: Parameters<T>) => boolean;
|
|
298
|
+
/** Caches a value for these arguments, starting a fresh TTL. */
|
|
299
|
+
set: (value: ReturnType<T>, ...args: Parameters<T>) => void;
|
|
282
300
|
}
|
|
283
301
|
/**
|
|
284
302
|
* Memoizes a function by caching its results based on the input arguments.
|
|
285
303
|
* A resolver function can be provided to customize the key generation.
|
|
286
304
|
* If no resolver is provided, the key will be generated by JSON stringify-ing the input arguments.
|
|
305
|
+
* Rejected promises are evicted. Expired entries are swept lazily on calls and writes,
|
|
306
|
+
* at most once per maxAge interval; no cleanup timers are created.
|
|
287
307
|
* @param fn The function to memoize
|
|
288
|
-
* @param
|
|
308
|
+
* @param options Optional key resolver and cache lifetime in milliseconds
|
|
289
309
|
* @example
|
|
290
310
|
* // 1. Basic usage
|
|
291
311
|
* const heavyCalc = memoize((n: number) => {
|
|
@@ -306,8 +326,14 @@ interface MemoizedFunction<T extends (...args: any[]) => any> {
|
|
|
306
326
|
* @example
|
|
307
327
|
* // 4. Manual cache clearing
|
|
308
328
|
* heavyCalc.clear();
|
|
329
|
+
* @example
|
|
330
|
+
* // 5. Seed or invalidate one entry using the original arguments
|
|
331
|
+
* const getFlag = memoize(async (id: string) => readFlag(id));
|
|
332
|
+
* await writeFlag("drops", true);
|
|
333
|
+
* getFlag.set(Promise.resolve(true), "drops");
|
|
334
|
+
* getFlag.delete("drops");
|
|
309
335
|
*/
|
|
310
|
-
declare function memoize<T extends (...args:
|
|
336
|
+
declare function memoize<T extends (...args: never[]) => unknown>(fn: T, options?: {
|
|
311
337
|
/** Function to generate a cache key from the input arguments. */
|
|
312
338
|
resolver?: (...args: Parameters<T>) => string;
|
|
313
339
|
/** The time to wait in milliseconds before expiring a cache entry. */
|
|
@@ -368,9 +394,9 @@ declare function FormatNumberCompact(num: number, locale?: Intl.LocalesArgument)
|
|
|
368
394
|
* Duration formatter.
|
|
369
395
|
*
|
|
370
396
|
* Available styles:
|
|
371
|
-
* -
|
|
372
|
-
* -
|
|
373
|
-
* -
|
|
397
|
+
* - "digital": MM:SS, HH:MM:SS, or D:HH:MM:SS.
|
|
398
|
+
* - "hms": hours, minutes, and seconds, with total hours that can exceed 23.
|
|
399
|
+
* - "ymdhms": days, hours, minutes, and seconds, with hours capped at 23.
|
|
374
400
|
* @param target The target time to calculate from
|
|
375
401
|
* @param style The output style to use
|
|
376
402
|
* @param options Formatting options
|
|
@@ -673,4 +699,4 @@ declare function pluralize(count: number, singular: string, plural?: string): st
|
|
|
673
699
|
*/
|
|
674
700
|
declare function toTitleCase(str: string, smart?: boolean): string;
|
|
675
701
|
|
|
676
|
-
export { type AlphaCasing, type AnyFunc, Cache, type DebouncedFunction, type DeepPartial, type FormatDurationOptions, FormatNumberCompact, Loop, type LoopEvents, type MemoizedFunction, type ParseTimeOptions, Pipe, type ReadDirOptions, type RetryOptions, type RndArrayOptions, type RndStrOptions, type SequentialMapContext, Storage, type TypedEmitter, chunk, chunkAdj, clampNumber, cluster, compactArray, createSampler, debounce, escapeRegex, forceArray, formatCurrency, formatDuration, formatETA, formatMemory, formatNumber, formatOrdinal, getAge, getFlag, getProp, hasFlag, hasProp, inverseLerp, isDefined, isEmpty, isInRange, isObject, isSortedArray, isString, isToday, lerp, memoize, merge, msToSecs, omit, parseTime, percent, pick, pluralize, prng, readDir, remap, retryPromise, rndChance, rndChoice, rndFloat, rndIndex, rndInt, rndString, searchSorted, secsToMs, seqMap, setProp, shuffle, sortBy, sum, throttle, toTitleCase, unique, wait, weightedRnd, wrap };
|
|
702
|
+
export { type AlphaCasing, type AnyFunc, Cache, type DebouncedFunction, type DeepPartial, type FormatDurationOptions, FormatNumberCompact, Loop, type LoopEvents, type MemoizedFunction, type ParseTimeOptions, Pipe, type ReadDirOptions, type RetryOptions, type RndArrayOptions, type RndStrOptions, type SequentialMapContext, Storage, type TypedEmitter, chunk, chunkAdj, clampNumber, cluster, compactArray, createSampler, debounce, escapeRegex, forceArray, formatCurrency, formatDuration, formatETA, formatMemory, formatNumber, formatOrdinal, getAge, getFlag, getProp, hasFlag, hasProp, inverseLerp, isDefined, isEmpty, isInRange, isObject, isSortedArray, isString, isToday, keyedQueue, lerp, memoize, merge, msToSecs, omit, parseTime, percent, pick, pluralize, prng, readDir, remap, retryPromise, rndChance, rndChoice, rndFloat, rndIndex, rndInt, rndString, searchSorted, secsToMs, seqMap, setProp, shuffle, sortBy, sum, throttle, toTitleCase, unique, wait, weightedRnd, wrap };
|
package/dist/index.d.ts
CHANGED
|
@@ -208,6 +208,20 @@ interface RetryOptions {
|
|
|
208
208
|
/** An AbortSignal to cancel the entire operation. */
|
|
209
209
|
signal?: AbortSignal;
|
|
210
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Creates a queue that runs tasks in call order for each key, while different keys run concurrently.
|
|
213
|
+
* Task errors reach their callers without blocking later tasks. Drained keys are removed automatically.
|
|
214
|
+
* @example
|
|
215
|
+
* const queue = keyedQueue();
|
|
216
|
+
* const first = queue.run("user-1", () => saveInventory("user-1"));
|
|
217
|
+
* const second = queue.run("user-1", () => readInventory("user-1"));
|
|
218
|
+
* await Promise.all([first, second]); // The read runs after the save finishes
|
|
219
|
+
* console.log(queue.size); // 0 active keys
|
|
220
|
+
*/
|
|
221
|
+
declare function keyedQueue(): {
|
|
222
|
+
run<T>(key: string | number, work: () => T | Promise<T>): Promise<T>;
|
|
223
|
+
readonly size: number;
|
|
224
|
+
};
|
|
211
225
|
/**
|
|
212
226
|
* Retries an async function until the maximum number of attempts is reached.
|
|
213
227
|
*
|
|
@@ -275,17 +289,23 @@ declare function debounce<T extends (...args: unknown[]) => unknown>(fn: T, wait
|
|
|
275
289
|
*/
|
|
276
290
|
declare function throttle<T extends (...args: unknown[]) => unknown>(fn: T, limit: number): (...args: Parameters<T>) => void;
|
|
277
291
|
|
|
278
|
-
interface MemoizedFunction<T extends (...args:
|
|
292
|
+
interface MemoizedFunction<T extends (...args: never[]) => unknown> {
|
|
279
293
|
(...args: Parameters<T>): ReturnType<T>;
|
|
280
294
|
/** Clears the cache. */
|
|
281
295
|
clear: () => void;
|
|
296
|
+
/** Removes the entry for these arguments, returning whether it existed. */
|
|
297
|
+
delete: (...args: Parameters<T>) => boolean;
|
|
298
|
+
/** Caches a value for these arguments, starting a fresh TTL. */
|
|
299
|
+
set: (value: ReturnType<T>, ...args: Parameters<T>) => void;
|
|
282
300
|
}
|
|
283
301
|
/**
|
|
284
302
|
* Memoizes a function by caching its results based on the input arguments.
|
|
285
303
|
* A resolver function can be provided to customize the key generation.
|
|
286
304
|
* If no resolver is provided, the key will be generated by JSON stringify-ing the input arguments.
|
|
305
|
+
* Rejected promises are evicted. Expired entries are swept lazily on calls and writes,
|
|
306
|
+
* at most once per maxAge interval; no cleanup timers are created.
|
|
287
307
|
* @param fn The function to memoize
|
|
288
|
-
* @param
|
|
308
|
+
* @param options Optional key resolver and cache lifetime in milliseconds
|
|
289
309
|
* @example
|
|
290
310
|
* // 1. Basic usage
|
|
291
311
|
* const heavyCalc = memoize((n: number) => {
|
|
@@ -306,8 +326,14 @@ interface MemoizedFunction<T extends (...args: any[]) => any> {
|
|
|
306
326
|
* @example
|
|
307
327
|
* // 4. Manual cache clearing
|
|
308
328
|
* heavyCalc.clear();
|
|
329
|
+
* @example
|
|
330
|
+
* // 5. Seed or invalidate one entry using the original arguments
|
|
331
|
+
* const getFlag = memoize(async (id: string) => readFlag(id));
|
|
332
|
+
* await writeFlag("drops", true);
|
|
333
|
+
* getFlag.set(Promise.resolve(true), "drops");
|
|
334
|
+
* getFlag.delete("drops");
|
|
309
335
|
*/
|
|
310
|
-
declare function memoize<T extends (...args:
|
|
336
|
+
declare function memoize<T extends (...args: never[]) => unknown>(fn: T, options?: {
|
|
311
337
|
/** Function to generate a cache key from the input arguments. */
|
|
312
338
|
resolver?: (...args: Parameters<T>) => string;
|
|
313
339
|
/** The time to wait in milliseconds before expiring a cache entry. */
|
|
@@ -368,9 +394,9 @@ declare function FormatNumberCompact(num: number, locale?: Intl.LocalesArgument)
|
|
|
368
394
|
* Duration formatter.
|
|
369
395
|
*
|
|
370
396
|
* Available styles:
|
|
371
|
-
* -
|
|
372
|
-
* -
|
|
373
|
-
* -
|
|
397
|
+
* - "digital": MM:SS, HH:MM:SS, or D:HH:MM:SS.
|
|
398
|
+
* - "hms": hours, minutes, and seconds, with total hours that can exceed 23.
|
|
399
|
+
* - "ymdhms": days, hours, minutes, and seconds, with hours capped at 23.
|
|
374
400
|
* @param target The target time to calculate from
|
|
375
401
|
* @param style The output style to use
|
|
376
402
|
* @param options Formatting options
|
|
@@ -673,4 +699,4 @@ declare function pluralize(count: number, singular: string, plural?: string): st
|
|
|
673
699
|
*/
|
|
674
700
|
declare function toTitleCase(str: string, smart?: boolean): string;
|
|
675
701
|
|
|
676
|
-
export { type AlphaCasing, type AnyFunc, Cache, type DebouncedFunction, type DeepPartial, type FormatDurationOptions, FormatNumberCompact, Loop, type LoopEvents, type MemoizedFunction, type ParseTimeOptions, Pipe, type ReadDirOptions, type RetryOptions, type RndArrayOptions, type RndStrOptions, type SequentialMapContext, Storage, type TypedEmitter, chunk, chunkAdj, clampNumber, cluster, compactArray, createSampler, debounce, escapeRegex, forceArray, formatCurrency, formatDuration, formatETA, formatMemory, formatNumber, formatOrdinal, getAge, getFlag, getProp, hasFlag, hasProp, inverseLerp, isDefined, isEmpty, isInRange, isObject, isSortedArray, isString, isToday, lerp, memoize, merge, msToSecs, omit, parseTime, percent, pick, pluralize, prng, readDir, remap, retryPromise, rndChance, rndChoice, rndFloat, rndIndex, rndInt, rndString, searchSorted, secsToMs, seqMap, setProp, shuffle, sortBy, sum, throttle, toTitleCase, unique, wait, weightedRnd, wrap };
|
|
702
|
+
export { type AlphaCasing, type AnyFunc, Cache, type DebouncedFunction, type DeepPartial, type FormatDurationOptions, FormatNumberCompact, Loop, type LoopEvents, type MemoizedFunction, type ParseTimeOptions, Pipe, type ReadDirOptions, type RetryOptions, type RndArrayOptions, type RndStrOptions, type SequentialMapContext, Storage, type TypedEmitter, chunk, chunkAdj, clampNumber, cluster, compactArray, createSampler, debounce, escapeRegex, forceArray, formatCurrency, formatDuration, formatETA, formatMemory, formatNumber, formatOrdinal, getAge, getFlag, getProp, hasFlag, hasProp, inverseLerp, isDefined, isEmpty, isInRange, isObject, isSortedArray, isString, isToday, keyedQueue, lerp, memoize, merge, msToSecs, omit, parseTime, percent, pick, pluralize, prng, readDir, remap, retryPromise, rndChance, rndChoice, rndFloat, rndIndex, rndInt, rndString, searchSorted, secsToMs, seqMap, setProp, shuffle, sortBy, sum, throttle, toTitleCase, unique, wait, weightedRnd, wrap };
|
package/dist/index.js
CHANGED
|
@@ -504,6 +504,24 @@ function unique(array, key) {
|
|
|
504
504
|
}
|
|
505
505
|
|
|
506
506
|
// src/async.ts
|
|
507
|
+
function keyedQueue() {
|
|
508
|
+
const queues = /* @__PURE__ */ new Map();
|
|
509
|
+
return {
|
|
510
|
+
run(key, work) {
|
|
511
|
+
const previous = queues.get(key) ?? Promise.resolve();
|
|
512
|
+
const result = previous.then(work);
|
|
513
|
+
const release = () => {
|
|
514
|
+
if (queues.get(key) === tail) queues.delete(key);
|
|
515
|
+
};
|
|
516
|
+
const tail = result.then(release, release);
|
|
517
|
+
queues.set(key, tail);
|
|
518
|
+
return result;
|
|
519
|
+
},
|
|
520
|
+
get size() {
|
|
521
|
+
return queues.size;
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
}
|
|
507
525
|
async function retryPromise(fn, options = {}) {
|
|
508
526
|
const { attempts = 3, delay = 500, timeout, signal } = options;
|
|
509
527
|
if (signal?.aborted) throw new Error("Operation aborted");
|
|
@@ -619,9 +637,28 @@ function throttle(fn, limit) {
|
|
|
619
637
|
function memoize(fn, options = {}) {
|
|
620
638
|
const cache = /* @__PURE__ */ new Map();
|
|
621
639
|
const { resolver, maxAge } = options;
|
|
640
|
+
let lastSweep = Date.now();
|
|
641
|
+
const getKey = (...args) => resolver ? resolver(...args) : JSON.stringify(args);
|
|
642
|
+
const sweep = (now) => {
|
|
643
|
+
if (!maxAge || now - lastSweep < maxAge) return;
|
|
644
|
+
lastSweep = now;
|
|
645
|
+
for (const [key, entry] of cache) {
|
|
646
|
+
if (now - entry.timestamp >= maxAge) cache.delete(key);
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
const cacheValue = (key, value, timestamp) => {
|
|
650
|
+
const entry = { value, timestamp };
|
|
651
|
+
cache.set(key, entry);
|
|
652
|
+
if (value !== null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function") {
|
|
653
|
+
void Promise.resolve(value).catch(() => {
|
|
654
|
+
if (cache.get(key) === entry) cache.delete(key);
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
};
|
|
622
658
|
const memoized = function(...args) {
|
|
623
|
-
const key =
|
|
659
|
+
const key = getKey(...args);
|
|
624
660
|
const now = Date.now();
|
|
661
|
+
sweep(now);
|
|
625
662
|
const entry = cache.get(key);
|
|
626
663
|
if (entry) {
|
|
627
664
|
if (!maxAge || now - entry.timestamp < maxAge) {
|
|
@@ -630,10 +667,17 @@ function memoize(fn, options = {}) {
|
|
|
630
667
|
cache.delete(key);
|
|
631
668
|
}
|
|
632
669
|
const result = fn.apply(this, args);
|
|
633
|
-
|
|
670
|
+
cacheValue(key, result, now);
|
|
634
671
|
return result;
|
|
635
672
|
};
|
|
636
673
|
memoized.clear = () => cache.clear();
|
|
674
|
+
memoized.delete = (...args) => cache.delete(getKey(...args));
|
|
675
|
+
memoized.set = (value, ...args) => {
|
|
676
|
+
const key = getKey(...args);
|
|
677
|
+
const now = Date.now();
|
|
678
|
+
sweep(now);
|
|
679
|
+
cacheValue(key, value, now);
|
|
680
|
+
};
|
|
637
681
|
return memoized;
|
|
638
682
|
}
|
|
639
683
|
|
|
@@ -684,7 +728,7 @@ function formatDuration(target, style = "hms", options = {}) {
|
|
|
684
728
|
if (diff === 0) return style === "digital" ? "00:00" : "now";
|
|
685
729
|
const s = Math.floor(diff / 1e3) % 60;
|
|
686
730
|
const m = Math.floor(diff / 6e4) % 60;
|
|
687
|
-
const h = Math.floor(diff / 36e5) % 24;
|
|
731
|
+
const h = style === "hms" ? Math.floor(diff / 36e5) : Math.floor(diff / 36e5) % 24;
|
|
688
732
|
const d = Math.floor(diff / 864e5);
|
|
689
733
|
if (style === "digital") {
|
|
690
734
|
const parts = [m, s].map((v) => String(v).padStart(2, "0"));
|
|
@@ -979,6 +1023,7 @@ export {
|
|
|
979
1023
|
isSortedArray,
|
|
980
1024
|
isString,
|
|
981
1025
|
isToday,
|
|
1026
|
+
keyedQueue,
|
|
982
1027
|
lerp,
|
|
983
1028
|
memoize,
|
|
984
1029
|
merge,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qznt",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "A lightweight typed utility toolkit for modern TypeScript environments.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "xsqu1znt",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "tsup",
|
|
30
|
+
"test": "pnpm build && node --test tests/*.test.mjs",
|
|
30
31
|
"check": "tsc --noEmit",
|
|
31
32
|
"format": "prettier --write \"./**/*.{ts,json}\""
|
|
32
33
|
}
|