Comparing version 1.2.0 to 1.3.0
@@ -0,1 +1,8 @@ | ||
declare type Partial$1<T> = { | ||
[K in keyof T]?: T[K]; | ||
}; | ||
declare type KeysOnly<T> = { | ||
[K in keyof T]: K; | ||
}; | ||
declare type ms = number; | ||
@@ -202,15 +209,31 @@ declare type second = number; | ||
interface INames { | ||
[k: string]: string; | ||
} | ||
interface ITimer<TName> { | ||
start(...labelArr: string[]): void; | ||
end(...labelArr: string[]): void; | ||
switch(endLabel: string | string[], startLabel: string | string[]): void; | ||
log(prefix?: string): void; | ||
reset(): void; | ||
names: KeysOnly<TName>; | ||
displayNames: TName; | ||
} | ||
/** | ||
* Usage: | ||
* ```typescript | ||
* const timer = getTimer('Example'); | ||
* timer.start('TOTAL', 'intro'); | ||
* const timer = getTimer('Example', false, { | ||
* TOTAL: 'TOTAL', | ||
* INTRO: 'Action 1', | ||
* ENDING: 'Action 2' | ||
* }); | ||
* timer.start(timer.TOTAL, timer.INTRO); | ||
* await wait(seconds(4)); // do something async | ||
* timer.switch('intro', 'ending'); // same as calling end('intro') and start('ending') | ||
* timer.switch(timer.INTRO, timer.ENDING); // same as calling end(timer.INTRO) and start(timer.ENDING) | ||
* await wait(seconds(6)); // do something async | ||
* timer.end('TOTAL', 'ending'); | ||
* timer.end(timer.TOTAL, timer.ENDING); | ||
* timer.log(); | ||
@@ -223,23 +246,11 @@ * ``` | ||
* TOTAL: 10s | ||
* intro: 4s | ||
* ending: 6s | ||
* Action 1: 4s | ||
* Action 2: 6s | ||
* ``` | ||
*/ | ||
declare const getTimer: (name?: string) => { | ||
start(...labelArr: string[]): void; | ||
end(...labelArr: string[]): void; | ||
switch(endLabel: string | string[], startLabel: string | string[]): void; | ||
log(prefix?: string): void; | ||
reset(): void; | ||
}; | ||
declare const getTimer: <TName extends INames>(name?: string, verbose?: boolean, displayNames?: TName) => ITimer<TName> & KeysOnly<TName>; | ||
/** | ||
* Global timer | ||
*/ | ||
declare const timer: { | ||
start(...labelArr: string[]): void; | ||
end(...labelArr: string[]): void; | ||
switch(endLabel: string | string[], startLabel: string | string[]): void; | ||
log(prefix?: string): void; | ||
reset(): void; | ||
}; | ||
declare const timer: ITimer<INames> & KeysOnly<INames>; | ||
@@ -269,2 +280,15 @@ /** | ||
declare const printLn: (...text: any[]) => void; | ||
interface ProgressBarOptionsFull { | ||
prefix: string; | ||
maxWidth: number; | ||
chalk: any; | ||
wrapperFn: any; | ||
showCount: boolean; | ||
showPercent: boolean; | ||
progChar: string; | ||
emptyChar: string; | ||
prefixChar: string; | ||
suffixChar: string; | ||
} | ||
declare type ProgressBarOptions = Partial<ProgressBarOptionsFull>; | ||
/** | ||
@@ -278,3 +302,8 @@ * Usage: | ||
* | ||
* const progress = getProgressBar(5, 'ABC', 20, chalk, chalk.green); | ||
* const progress = getProgressBar(5, { | ||
* prefix: 'ABC', | ||
* maxWidth: 20, | ||
* chalk, | ||
* wrapperFn: chalk.green | ||
* }); | ||
* for (let i = 1; i <= 5; i++) { | ||
@@ -297,3 +326,3 @@ * progress.set(i); | ||
*/ | ||
declare const getProgressBar: (max: number, prefix?: string, maxWidth?: number, chalk?: any, wrapperFn?: any) => { | ||
declare const getProgressBar: (max: number, options?: ProgressBarOptions) => { | ||
next: () => string; | ||
@@ -307,2 +336,3 @@ set: (newCurrent: number) => string; | ||
declare const progressBar_printLn: typeof printLn; | ||
type progressBar_ProgressBarOptions = ProgressBarOptions; | ||
declare const progressBar_getProgressBar: typeof getProgressBar; | ||
@@ -312,2 +342,3 @@ declare namespace progressBar { | ||
progressBar_printLn as printLn, | ||
progressBar_ProgressBarOptions as ProgressBarOptions, | ||
progressBar_getProgressBar as getProgressBar, | ||
@@ -346,15 +377,20 @@ }; | ||
getDeferred: <T extends unknown>() => DeferredPromise<T>; | ||
allObj: <T_1 extends unknown>(input: { | ||
[key: string]: Promise<T_1>; | ||
all: <T_1 extends unknown>(promises: Promise<T_1>[]) => Promise<any>; | ||
allLimit: <T_2 extends unknown>(limit: number, items: ((index: number) => Promise<T_2>)[], noThrow?: boolean) => Promise<T_2[]>; | ||
each: <Ti extends unknown>(items: Ti[], func: (item: Ti, index: number, array: Ti[]) => Promise<any>) => Promise<any>; | ||
eachLimit: <Ti_1 extends unknown>(limit: number, items: Ti_1[], func: (item?: Ti_1, index?: number, array?: Ti_1[]) => Promise<any>) => Promise<any>; | ||
map: <Ti_2 extends unknown, To extends unknown>(items: Ti_2[], func: (item?: Ti_2, index?: number, array?: Ti_2[]) => Promise<To>) => Promise<To[]>; | ||
mapLimit: <Ti_3 extends unknown, To_1 extends unknown>(limit: number, items: Ti_3[], func: (item?: Ti_3, index?: number, array?: Ti_3[]) => Promise<To_1>) => Promise<To_1[]>; | ||
allObj: <T_3 extends unknown>(input: { | ||
[key: string]: Promise<T_3>; | ||
}) => Promise<{ | ||
[key: string]: T_1; | ||
}>; | ||
allLimit: <T_2 extends unknown>(items: ((index: number) => Promise<T_2>)[], limit?: number, noThrow?: boolean) => Promise<T_2[]>; | ||
allLimitObj: <T_3 extends unknown>(input: { | ||
[key: string]: (index: number) => Promise<T_3>; | ||
}, limit?: number, noThrow?: boolean) => Promise<{ | ||
[key: string]: T_3; | ||
}>; | ||
allLimitObj: <T_4 extends unknown>(limit: number, input: { | ||
[key: string]: (index: number) => Promise<T_4>; | ||
}, noThrow?: boolean) => Promise<{ | ||
[key: string]: T_4; | ||
}>; | ||
}; | ||
export { CENTURY, DAY, DECADE, DeferredPromise, HOUR, MILLENNIUM, MILLISECOND, MINUTE, MONTH, PromiseUtils, SECOND, WEEK, YEAR, centuries, century, day, days, decade, decades, getDeferred, getProgressBar, getTimer, hour, hours, interval, millennium, millenniums, milliseconds, minute, minutes, month, months, ms, printLn, progressBar, second, seconds, stopInterval, timer, times, wait, waitEvery, waitFor, waitUntil, waiters, week, weeks, year, years }; | ||
export { CENTURY, DAY, DECADE, DeferredPromise, HOUR, KeysOnly, MILLENNIUM, MILLISECOND, MINUTE, MONTH, Partial$1 as Partial, ProgressBarOptions, PromiseUtils, SECOND, WEEK, YEAR, centuries, century, day, days, decade, decades, getDeferred, getProgressBar, getTimer, hour, hours, interval, millennium, millenniums, milliseconds, minute, minutes, month, months, ms, printLn, progressBar, second, seconds, stopInterval, timer, times, wait, waitEvery, waitFor, waitUntil, waiters, week, weeks, year, years }; |
@@ -5,5 +5,5 @@ var __defProp = Object.defineProperty; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __export = (target, all) => { | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
var __export = (target, all2) => { | ||
for (var name in all2) | ||
__defProp(target, name, { get: all2[name], enumerable: true }); | ||
}; | ||
@@ -157,6 +157,31 @@ var __copyProps = (to, from, except, desc) => { | ||
// src/tools/timer.ts | ||
var getTimer = (name) => { | ||
var formatDuration = (duration) => { | ||
const seconds2 = duration / SECOND; | ||
let extra = ""; | ||
let secsEx = Math.round(seconds2); | ||
let minsEx = Math.floor(secsEx / 60); | ||
if (minsEx >= 1) { | ||
secsEx %= 60; | ||
extra = `${minsEx}m ${secsEx}s`; | ||
let hoursEx = Math.floor(minsEx / 60); | ||
if (hoursEx >= 1) { | ||
minsEx %= 60; | ||
extra = `${hoursEx}h ${minsEx}m ${secsEx}s`; | ||
} | ||
} | ||
return `${extra}${extra ? ` (${seconds2}s)` : `${seconds2}s`}`; | ||
}; | ||
var getTimer = (name, verbose = false, displayNames) => { | ||
let startTimes = {}; | ||
let endTimes = {}; | ||
let dispNames = displayNames || { TOTAL: "TOTAL" }; | ||
const names = Object.fromEntries(Object.keys(dispNames).map((key) => [key, key])); | ||
const logLine = (label, prefix = "") => { | ||
const start = startTimes[label]; | ||
const end = endTimes[label] || Date.now(); | ||
const duration = end - start; | ||
console.log(`${prefix}${dispNames[label] || label}: ${formatDuration(duration)}`); | ||
}; | ||
return { | ||
...names, | ||
start(...labelArr) { | ||
@@ -170,2 +195,6 @@ for (let label of labelArr) { | ||
endTimes[label] = Date.now(); | ||
if (verbose) { | ||
logLine(label); | ||
console.log(""); | ||
} | ||
} | ||
@@ -183,6 +212,3 @@ }, | ||
for (let label of Object.keys(startTimes)) { | ||
const start = startTimes[label]; | ||
const end = endTimes[label] || Date.now(); | ||
const duration = end - start; | ||
console.log(` ${label}: ${duration / SECOND}s`); | ||
logLine(label, " "); | ||
} | ||
@@ -194,3 +220,5 @@ console.log(""); | ||
endTimes = {}; | ||
} | ||
}, | ||
names, | ||
displayNames: dispNames | ||
}; | ||
@@ -211,15 +239,2 @@ }; | ||
}; | ||
var getBarString = (current, max, width = 50, chalk = noChalk, progChar = "\u2588", emptyChar = " ", prefix = "\u2595", suffix = "\u258F") => { | ||
const numProgChars = Math.round(width * (Math.max(0, Math.min(current / max, 1)) / 1)); | ||
const numEmptyChars = width - numProgChars; | ||
const body = `${progChar.repeat(numProgChars)}${emptyChar.repeat(numEmptyChars)}`; | ||
return `${chalk.dim(prefix)}${chalk.bold(body)}${chalk.dim(suffix)}`; | ||
}; | ||
var getDefaultWidth = () => { | ||
if (process == null ? void 0 : process.stdout) { | ||
return process.stdout.columns; | ||
} else { | ||
return 100; | ||
} | ||
}; | ||
var printLn = (...text) => { | ||
@@ -246,8 +261,47 @@ if (process == null ? void 0 : process.stdout) { | ||
}; | ||
var getProgressBar = (max, prefix = "", maxWidth = getDefaultWidth(), chalk = noChalk, wrapperFn = noChalk) => { | ||
var getBarString = (current, max, width, opts) => { | ||
const { progChar, emptyChar, prefixChar, suffixChar, chalk } = opts; | ||
const numProgChars = Math.round(width * (Math.max(0, Math.min(current / max, 1)) / 1)); | ||
const numEmptyChars = width - numProgChars; | ||
const body = `${progChar.repeat(numProgChars)}${emptyChar.repeat(numEmptyChars)}`; | ||
return `${chalk.dim(prefixChar)}${chalk.bold(body)}${chalk.dim(suffixChar)}`; | ||
}; | ||
var getSuffix = (current, max, opts) => { | ||
let items = [""]; | ||
if (opts.showCount) { | ||
items.push(`[${current.toString().padStart(max.toString().length, " ")} / ${max}]`); | ||
} | ||
if (opts.showPercent) { | ||
const percent = Math.round(current / max * 100); | ||
items.push(`(${percent.toString().padStart("100".toString().length, " ")}%)`); | ||
} | ||
const joined = items.filter((x) => x).join(" "); | ||
return joined.length ? " " + joined : ""; | ||
}; | ||
var getFullOptions = (opts = {}) => ({ | ||
maxWidth: (process == null ? void 0 : process.stdout) ? process.stdout.columns : 100, | ||
chalk: noChalk, | ||
wrapperFn: noWrap, | ||
showCount: true, | ||
showPercent: false, | ||
progChar: "\u2588", | ||
emptyChar: " ", | ||
prefixChar: "\u2595", | ||
suffixChar: "\u258F", | ||
...opts, | ||
prefix: (opts.prefix || "").length ? opts.prefix + " " : "" | ||
}); | ||
var getProgressBar = (max, options = {}) => { | ||
const opts = getFullOptions(options); | ||
const { prefix, maxWidth, wrapperFn, prefixChar, suffixChar } = opts; | ||
let current = 0; | ||
let finished = false; | ||
const update = () => { | ||
const suffix = `[${current.toString().padStart(max.toString().length, " ")} / ${max}]`; | ||
const output = `${prefix} ${getBarString(current, max, Math.max(0, maxWidth - (prefix.length + suffix.length + 4)), chalk)} ${suffix}`; | ||
const suffix = getSuffix(current, max, opts); | ||
const output = `${prefix}${getBarString( | ||
current, | ||
max, | ||
Math.max(0, maxWidth - [prefix, suffix, prefixChar, suffixChar].join("").length), | ||
opts | ||
)}${suffix}`; | ||
print(output, wrapperFn); | ||
@@ -274,3 +328,3 @@ return output; | ||
const output = update(); | ||
print(); | ||
printLn(); | ||
return output; | ||
@@ -306,3 +360,6 @@ }; | ||
}; | ||
var allLimit = (items, limit = items.length, noThrow = false) => { | ||
var all = async (promises) => { | ||
await Promise.all(promises); | ||
}; | ||
var allLimit = (limit, items, noThrow = false) => { | ||
let runningCount = 0; | ||
@@ -348,8 +405,34 @@ let errors = []; | ||
}; | ||
var each = async (items, func) => { | ||
await Promise.all(items.map((item, index, array) => func(item, index, array))); | ||
}; | ||
var eachLimit = async (limit, items, func) => { | ||
await allLimit( | ||
limit, | ||
items.map((item, index, array) => () => func(item, index, array)) | ||
); | ||
}; | ||
var map = async (items, func) => { | ||
const result = []; | ||
await Promise.all( | ||
items.map(async (item, index, array) => { | ||
const res = await func(item, index, array); | ||
result[index] = res; | ||
}) | ||
); | ||
return result; | ||
}; | ||
var mapLimit = async (limit, items, func) => await allLimit( | ||
limit, | ||
items.map((item, index, array) => () => { | ||
const res = func(item, index, array); | ||
return res; | ||
}) | ||
); | ||
var allObj = async (input) => { | ||
return objectify(Promise.all, input); | ||
}; | ||
var allLimitObj = async (input, limit, noThrow = false) => { | ||
var allLimitObj = async (limit, input, noThrow = false) => { | ||
return objectify((items) => { | ||
return allLimit(items, limit, noThrow); | ||
return allLimit(limit, items, noThrow); | ||
}, input); | ||
@@ -359,4 +442,9 @@ }; | ||
getDeferred, | ||
all, | ||
allLimit, | ||
each, | ||
eachLimit, | ||
map, | ||
mapLimit, | ||
allObj, | ||
allLimit, | ||
allLimitObj | ||
@@ -363,0 +451,0 @@ }; |
{ | ||
"name": "swiss-ak", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"author": "Jack Cannon <jackc@annon.co.uk> (http://c.annon.co.uk/)", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
197
README.md
@@ -116,14 +116,16 @@ # swiss-ak (Swiss Army Knife) | ||
```typescript | ||
import { getTimer } from 'swiss-ak'; | ||
const timer = getTimer('Example', false, { | ||
TOTAL: 'TOTAL', | ||
INTRO: 'Action 1', | ||
ENDING: 'Action 2' | ||
}); | ||
timer.start(timer.TOTAL, timer.INTRO); | ||
const timer = getTimer('Example'); | ||
timer.start('TOTAL', 'intro'); | ||
await wait(seconds(4)); // do something async | ||
timer.switch('intro', 'ending'); // same as calling end('intro') and start('ending') | ||
timer.switch(timer.INTRO, timer.ENDING); // same as calling end(timer.INTRO) and start(timer.ENDING) | ||
await wait(seconds(6)); // do something async | ||
timer.end('TOTAL', 'ending'); | ||
timer.end(timer.TOTAL, timer.ENDING); | ||
timer.log(); | ||
@@ -137,4 +139,4 @@ ``` | ||
TOTAL: 10s | ||
intro: 4s | ||
ending: 6s | ||
Action 1: 4s | ||
Action 2: 6s | ||
``` | ||
@@ -149,3 +151,3 @@ | ||
timer.start('TOTAL'); | ||
timer.start(timer.TOTAL); | ||
``` | ||
@@ -177,6 +179,12 @@ | ||
### PromiseUtils.allObj | ||
### PromiseUtils.all | ||
Like Promise.all, but takes/gives an object instead of an array | ||
An alias for Promise.all | ||
### PromiseUtils.allLimit | ||
Like Promise.all, but limits the numbers of concurrently running items. | ||
Takes an array of functions (that return Promises), rather than an array of Promises | ||
```typescript | ||
@@ -191,29 +199,99 @@ import { PromiseUtils, timer, ms, seconds } from 'swiss-ak'; | ||
timer.start('allObj', 'a', 'b', 'c'); | ||
timer.start('allLimit', 'a', 'b', 'c', 'd'); | ||
const results = PromiseUtils.allObj<number>({ | ||
a: give(seconds(10), 1, 'a'), | ||
b: give(seconds(15), 2, 'b'), | ||
c: give(seconds(20), 3, 'c') | ||
}); | ||
const results = PromiseUtils.allLimit<number>(2, [ | ||
give(seconds(5), 1, 'a'), | ||
give(seconds(5), 2, 'b'), | ||
give(seconds(5), 3, 'c'), | ||
give(seconds(5), 4, 'd') | ||
]); | ||
timer.end('allObj'); | ||
timer.end('allLimit'); | ||
console.log(results); // { a: 1, b: 2, c: 3 } | ||
console.log(results); // [ 1, 2, 3, 4 ] | ||
timer.log(); | ||
// Times: | ||
// allObj: 20s | ||
// a: 10s | ||
// b: 15s | ||
// c: 20s | ||
// allLimit: 10s | ||
// a: 5s | ||
// b: 5s | ||
// c: 10s | ||
// d: 10s | ||
``` | ||
### PromiseUtils.allLimit | ||
### PromiseUtils.each | ||
Like Promise.all, but limits the numbers of concurrently running items. | ||
Run an async function against each item in an array | ||
Takes an array of functions (that return Promises), rather than an array of Promises | ||
```typescript | ||
import { PromiseUtils, ms, seconds, wait } from 'swiss-ak'; | ||
const arr = [1, 2, 3, 4]; | ||
await PromiseUtils.each<number>(arr, async (val: number) => { | ||
await wait(seconds(2)); | ||
sendToSomewhere(val); | ||
}); | ||
console.log(''); // after 2 seconds | ||
``` | ||
### PromiseUtils.eachLimit | ||
Run an async function against each item in an array, limiting the number of items that can run concurrently. | ||
See PromiseUtils.allLimit for information about limited functions. | ||
```typescript | ||
import { PromiseUtils, ms, seconds, wait } from 'swiss-ak'; | ||
const arr = [1, 2, 3, 4]; | ||
await PromiseUtils.eachLimit<number>(2, arr, async (val: number) => { | ||
await wait(seconds(2)); | ||
sendToSomewhere(val); | ||
}); | ||
console.log(''); // after 4 seconds | ||
``` | ||
### PromiseUtils.map | ||
Run an async map function against each item in an array, mapping the results to a returned array | ||
```typescript | ||
import { PromiseUtils, ms, seconds, wait } from 'swiss-ak'; | ||
const arr = [1, 2, 3, 4]; | ||
const mapped = await PromiseUtils.map<number>(arr, async (val: number) => { | ||
await wait(seconds(2)); | ||
return val * 2; | ||
}); | ||
console.log(mapped); // [2, 4, 6, 8] (after 2 seconds) | ||
``` | ||
### PromiseUtils.mapLimit | ||
Run an async map function against each item in an array, mapping the results to a returned array, and limiting the number of items that can run concurrently. | ||
See PromiseUtils.allLimit for information about limited functions. | ||
```typescript | ||
import { PromiseUtils, ms, seconds, wait } from 'swiss-ak'; | ||
const arr = [1, 2, 3, 4]; | ||
const mapped = await PromiseUtils.mapLimit<number>(2, arr, async (val: number) => { | ||
await wait(seconds(2)); | ||
return val * 2; | ||
}); | ||
console.log(mapped); // [2, 4, 6, 8] (after 4 seconds) | ||
``` | ||
### PromiseUtils.allObj | ||
Like Promise.all, but takes/gives an object instead of an array | ||
```typescript | ||
import { PromiseUtils, timer, ms, seconds } from 'swiss-ak'; | ||
@@ -227,21 +305,20 @@ | ||
timer.start('allLimit', 'a', 'b', 'c', 'd'); | ||
timer.start('allObj', 'a', 'b', 'c'); | ||
const results = PromiseUtils.allLimit<number>( | ||
[give(seconds(5), 1, 'a'), give(seconds(5), 2, 'b'), give(seconds(5), 3, 'c'), give(seconds(5), 4, 'd')], | ||
2, | ||
true | ||
); | ||
const results = PromiseUtils.allObj<number>({ | ||
a: give(seconds(10), 1, 'a'), | ||
b: give(seconds(15), 2, 'b'), | ||
c: give(seconds(20), 3, 'c') | ||
}); | ||
timer.end('allLimit'); | ||
timer.end('allObj'); | ||
console.log(results); // [ 1, 2, 3, 4 ] | ||
console.log(results); // { a: 1, b: 2, c: 3 } | ||
timer.log(); | ||
// Times: | ||
// allLimit: 10s | ||
// a: 5s | ||
// b: 5s | ||
// c: 10s | ||
// d: 10s | ||
// allObj: 20s | ||
// a: 10s | ||
// b: 15s | ||
// c: 20s | ||
``` | ||
@@ -266,12 +343,8 @@ | ||
const results = PromiseUtils.allLimitObj<number>( | ||
{ | ||
a: give(seconds(5), 1, 'a'), | ||
b: give(seconds(5), 2, 'b'), | ||
c: give(seconds(5), 3, 'c'), | ||
d: give(seconds(5), 4, 'd') | ||
}, | ||
2, | ||
true | ||
); | ||
const results = PromiseUtils.allLimitObj<number>(2, { | ||
a: give(seconds(5), 1, 'a'), | ||
b: give(seconds(5), 2, 'b'), | ||
c: give(seconds(5), 3, 'c'), | ||
d: give(seconds(5), 4, 'd') | ||
}); | ||
@@ -295,2 +368,19 @@ timer.end('allLimitObj'); | ||
### Options | ||
All options are optional. | ||
| Property | Default | Description | | ||
| ----------- | --------------------------------- | ----------------------------------------------------- | | ||
| prefix | `''` | String to show to left of progress bar | | ||
| maxWidth | `process.stdout.columns` or `100` | The maximum width the entire string may extend | | ||
| chalk | nothing | the `chalk` module, if available | | ||
| wrapperFn | nothing | function to wrap the printed string (eg `chalk.cyan)` | | ||
| showCount | `true` | Show numerical values of the count - `[11 / 15]` | | ||
| showPercent | `false` | Show percentage completed - `( 69%)` | | ||
| progChar | `'█'` | Character to use for progress section of bar | | ||
| emptyChar | `' '` | Character to use for empty (rail) section of bar | | ||
| prefixChar | `'▕'` | Character to start the progress bar with | | ||
| suffixChar | `'▏'` | Character to end the progress bar with | | ||
### Usage | ||
@@ -304,3 +394,8 @@ | ||
const progress = getProgressBar(5, 'ABC', 20, chalk, chalk.green); | ||
const progress = getProgressBar(5, { | ||
prefix: 'ABC', | ||
maxWidth: 20, | ||
chalk, | ||
wrapperFn: chalk.green | ||
}); | ||
for (let i = 1; i <= 5; i++) { | ||
@@ -312,3 +407,3 @@ progress.set(i); | ||
Output | ||
Output: | ||
@@ -315,0 +410,0 @@ ``` |
@@ -0,1 +1,2 @@ | ||
export * from './tools/types'; | ||
export * from './tools/times'; | ||
@@ -2,0 +3,0 @@ export * from './tools/waiters'; |
@@ -7,27 +7,2 @@ const noWrap = (x: any) => x; | ||
const getBarString = ( | ||
current: number, | ||
max: number, | ||
width: number = 50, | ||
chalk: any = noChalk, | ||
progChar: string = '█', | ||
emptyChar: string = ' ', | ||
prefix: string = '▕', | ||
suffix: string = '▏' | ||
) => { | ||
const numProgChars = Math.round(width * (Math.max(0, Math.min(current / max, 1)) / 1)); | ||
const numEmptyChars = width - numProgChars; | ||
const body = `${progChar.repeat(numProgChars)}${emptyChar.repeat(numEmptyChars)}`; | ||
return `${chalk.dim(prefix)}${chalk.bold(body)}${chalk.dim(suffix)}`; | ||
}; | ||
const getDefaultWidth = () => { | ||
if (process?.stdout) { | ||
return process.stdout.columns; | ||
} else { | ||
return 100; | ||
} | ||
}; | ||
/** | ||
@@ -78,2 +53,51 @@ * Can use instead of console.log | ||
const getBarString = (current: number, max: number, width: number, opts: ProgressBarOptionsFull) => { | ||
const { progChar, emptyChar, prefixChar, suffixChar, chalk } = opts; | ||
const numProgChars = Math.round(width * (Math.max(0, Math.min(current / max, 1)) / 1)); | ||
const numEmptyChars = width - numProgChars; | ||
const body = `${progChar.repeat(numProgChars)}${emptyChar.repeat(numEmptyChars)}`; | ||
return `${chalk.dim(prefixChar)}${chalk.bold(body)}${chalk.dim(suffixChar)}`; | ||
}; | ||
const getSuffix = (current: number, max: number, opts: ProgressBarOptionsFull) => { | ||
let items = ['']; | ||
if (opts.showCount) { | ||
items.push(`[${current.toString().padStart(max.toString().length, ' ')} / ${max}]`); | ||
} | ||
if (opts.showPercent) { | ||
const percent = Math.round((current / max) * 100); | ||
items.push(`(${percent.toString().padStart('100'.toString().length, ' ')}%)`); | ||
} | ||
const joined = items.filter((x) => x).join(' '); | ||
return joined.length ? ' ' + joined : ''; | ||
}; | ||
interface ProgressBarOptionsFull { | ||
prefix: string; | ||
maxWidth: number; | ||
chalk: any; | ||
wrapperFn: any; | ||
showCount: boolean; | ||
showPercent: boolean; | ||
progChar: string; | ||
emptyChar: string; | ||
prefixChar: string; | ||
suffixChar: string; | ||
} | ||
export type ProgressBarOptions = Partial<ProgressBarOptionsFull>; | ||
const getFullOptions = (opts: ProgressBarOptions = {}): ProgressBarOptionsFull => ({ | ||
maxWidth: process?.stdout ? process.stdout.columns : 100, | ||
chalk: noChalk, | ||
wrapperFn: noWrap, | ||
showCount: true, | ||
showPercent: false, | ||
progChar: '█', | ||
emptyChar: ' ', | ||
prefixChar: '▕', | ||
suffixChar: '▏', | ||
...opts, | ||
prefix: (opts.prefix || '').length ? opts.prefix + ' ' : '' | ||
}); | ||
/** | ||
@@ -87,3 +111,8 @@ * Usage: | ||
* | ||
* const progress = getProgressBar(5, 'ABC', 20, chalk, chalk.green); | ||
* const progress = getProgressBar(5, { | ||
* prefix: 'ABC', | ||
* maxWidth: 20, | ||
* chalk, | ||
* wrapperFn: chalk.green | ||
* }); | ||
* for (let i = 1; i <= 5; i++) { | ||
@@ -106,9 +135,5 @@ * progress.set(i); | ||
*/ | ||
export const getProgressBar = ( | ||
max: number, | ||
prefix: string = '', | ||
maxWidth: number = getDefaultWidth(), | ||
chalk: any = noChalk, | ||
wrapperFn: any = noChalk | ||
) => { | ||
export const getProgressBar = (max: number, options: ProgressBarOptions = {}) => { | ||
const opts = getFullOptions(options); | ||
const { prefix, maxWidth, wrapperFn, prefixChar, suffixChar } = opts; | ||
let current = 0; | ||
@@ -118,4 +143,9 @@ let finished = false; | ||
const update = () => { | ||
const suffix = `[${current.toString().padStart(max.toString().length, ' ')} / ${max}]`; | ||
const output = `${prefix} ${getBarString(current, max, Math.max(0, maxWidth - (prefix.length + suffix.length + 4)), chalk)} ${suffix}`; | ||
const suffix = getSuffix(current, max, opts); | ||
const output = `${prefix}${getBarString( | ||
current, | ||
max, | ||
Math.max(0, maxWidth - [prefix, suffix, prefixChar, suffixChar].join('').length), | ||
opts | ||
)}${suffix}`; | ||
@@ -145,3 +175,3 @@ print(output, wrapperFn); | ||
const output = update(); | ||
print(); // blank/new line | ||
printLn(); // blank/new line | ||
return output; | ||
@@ -148,0 +178,0 @@ }; |
@@ -47,5 +47,13 @@ export interface DeferredPromise<T> { | ||
/** | ||
* Like Promise.all, but limits the number of items that can run concurrently. | ||
* Takes array of functions that return Promises, not an array of Promises. | ||
* An alias for Promise.all | ||
*/ | ||
const all = async <T extends unknown>(promises: Promise<T>[]): Promise<any> => { | ||
await Promise.all(promises); | ||
}; | ||
/** | ||
* Like Promise.all, but limits the numbers of concurrently running items. | ||
* | ||
* Takes an array of functions (that return Promises), rather than an array of Promises | ||
* | ||
* ```typescript | ||
@@ -62,7 +70,8 @@ * import { PromiseUtils, timer, ms, seconds } from 'swiss-ak'; | ||
* | ||
* const results = PromiseUtils.allLimit<number>( | ||
* [give(seconds(5), 1, 'a'), give(seconds(5), 2, 'b'), give(seconds(5), 3, 'c'), give(seconds(5), 4, 'd')], | ||
* 2, | ||
* true | ||
* ); | ||
* const results = PromiseUtils.allLimit<number>(2, [ | ||
* give(seconds(5), 1, 'a'), | ||
* give(seconds(5), 2, 'b'), | ||
* give(seconds(5), 3, 'c'), | ||
* give(seconds(5), 4, 'd') | ||
* ]); | ||
* | ||
@@ -82,7 +91,3 @@ * timer.end('allLimit'); | ||
*/ | ||
const allLimit = <T extends unknown>( | ||
items: ((index: number) => Promise<T>)[], | ||
limit: number = items.length, | ||
noThrow: boolean = false | ||
): Promise<T[]> => { | ||
const allLimit = <T extends unknown>(limit: number, items: ((index: number) => Promise<T>)[], noThrow: boolean = false): Promise<T[]> => { | ||
let runningCount: number = 0; | ||
@@ -134,2 +139,112 @@ let errors: any[] = []; | ||
/** | ||
* Run an async function against each item in an array | ||
* | ||
* ```typescript | ||
* import { PromiseUtils, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* await PromiseUtils.each<number>(arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* sendToSomewhere(val); | ||
* }); | ||
* console.log(''); // after 2 seconds | ||
* ``` | ||
*/ | ||
const each = async <Ti extends unknown>(items: Ti[], func: (item: Ti, index: number, array: Ti[]) => Promise<any>): Promise<any> => { | ||
await Promise.all(items.map((item: Ti, index: number, array: Ti[]) => func(item, index, array))); | ||
}; | ||
/** | ||
* Run an async function against each item in an array, limiting the number of items that can run concurrently. | ||
* | ||
* See PromiseUtils.allLimit for information about limited functions. | ||
* | ||
* ```typescript | ||
* import { PromiseUtils, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* await PromiseUtils.eachLimit<number>(2, arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* sendToSomewhere(val); | ||
* }); | ||
* console.log(''); // after 4 seconds | ||
* ``` | ||
*/ | ||
const eachLimit = async <Ti extends unknown>( | ||
limit: number, | ||
items: Ti[], | ||
func: (item?: Ti, index?: number, array?: Ti[]) => Promise<any> | ||
): Promise<any> => { | ||
await allLimit( | ||
limit, | ||
items.map((item: Ti, index: number, array: Ti[]) => () => func(item, index, array)) | ||
); | ||
}; | ||
/** | ||
* Run an async map function against each item in an array, mapping the results to a returned array | ||
* | ||
* ```typescript | ||
* import { PromiseUtils, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* const mapped = await PromiseUtils.map<number>(arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* return val * 2; | ||
* }); | ||
* | ||
* console.log(mapped); // [2, 4, 6, 8] (after 2 seconds) | ||
* ``` | ||
*/ | ||
const map = async <Ti extends unknown, To extends unknown>( | ||
items: Ti[], | ||
func: (item?: Ti, index?: number, array?: Ti[]) => Promise<To> | ||
): Promise<To[]> => { | ||
const result: To[] = []; | ||
await Promise.all( | ||
items.map(async (item: Ti, index: number, array: Ti[]) => { | ||
const res = await func(item, index, array); | ||
result[index] = res; | ||
}) | ||
); | ||
return result; | ||
}; | ||
/** | ||
* Run an async map function against each item in an array, mapping the results to a returned array, and limiting the number of items that can run concurrently. | ||
* | ||
* See PromiseUtils.allLimit for information about limited functions. | ||
* | ||
* ```typescript | ||
* import { PromiseUtils, ms, seconds, wait } from 'swiss-ak'; | ||
* | ||
* const arr = [1, 2, 3, 4]; | ||
* | ||
* const mapped = await PromiseUtils.mapLimit<number>(2, arr, async (val: number) => { | ||
* await wait(seconds(2)); | ||
* return val * 2; | ||
* }); | ||
* | ||
* console.log(mapped); // [2, 4, 6, 8] (after 4 seconds) | ||
* ``` | ||
*/ | ||
const mapLimit = async <Ti extends unknown, To extends unknown>( | ||
limit: number, | ||
items: Ti[], | ||
func: (item?: Ti, index?: number, array?: Ti[]) => Promise<To> | ||
): Promise<To[]> => | ||
await allLimit( | ||
limit, | ||
items.map((item: Ti, index: number, array: Ti[]) => () => { | ||
const res = func(item, index, array); | ||
return res; | ||
}) | ||
); | ||
/** | ||
* Like Promise.all, but pass/receive objects rather than arrays | ||
@@ -171,4 +286,6 @@ * | ||
/** | ||
* Like PromiseUtils.allLimit, but pass/receive objects rather than arrays | ||
* A mix of allObj and allLimit. | ||
* | ||
* Takes an array of functions (that return Promises), and limits the numbers of concurrently running items. | ||
* | ||
* ```typescript | ||
@@ -185,12 +302,8 @@ * import { PromiseUtils, timer, ms, seconds } from 'swiss-ak'; | ||
* | ||
* const results = PromiseUtils.allLimitObj<number>( | ||
* { | ||
* a: give(seconds(5), 1, 'a'), | ||
* b: give(seconds(5), 2, 'b'), | ||
* c: give(seconds(5), 3, 'c'), | ||
* d: give(seconds(5), 4, 'd') | ||
* }, | ||
* 2, | ||
* true | ||
* ); | ||
* const results = PromiseUtils.allLimitObj<number>(2, { | ||
* a: give(seconds(5), 1, 'a'), | ||
* b: give(seconds(5), 2, 'b'), | ||
* c: give(seconds(5), 3, 'c'), | ||
* d: give(seconds(5), 4, 'd') | ||
* }); | ||
* | ||
@@ -211,8 +324,8 @@ * timer.end('allLimitObj'); | ||
const allLimitObj = async <T extends unknown>( | ||
limit: number, | ||
input: { [key: string]: (index: number) => Promise<T> }, | ||
limit?: number, | ||
noThrow: boolean = false | ||
): Promise<{ [key: string]: T }> => { | ||
return objectify((items: ((index: number) => Promise<T>)[]) => { | ||
return allLimit(items, limit, noThrow); | ||
return allLimit(limit, items, noThrow); | ||
}, input); | ||
@@ -223,5 +336,10 @@ }; | ||
getDeferred, | ||
all, | ||
allLimit, | ||
each, | ||
eachLimit, | ||
map, | ||
mapLimit, | ||
allObj, | ||
allLimit, | ||
allLimitObj | ||
}; |
import { ms, SECOND } from './times'; | ||
import { KeysOnly } from './types'; | ||
// Hacky little display function | ||
const formatDuration = (duration: ms) => { | ||
const seconds = duration / SECOND; | ||
let extra = ''; | ||
let secsEx = Math.round(seconds); | ||
let minsEx = Math.floor(secsEx / 60); | ||
if (minsEx >= 1) { | ||
secsEx %= 60; | ||
extra = `${minsEx}m ${secsEx}s`; | ||
let hoursEx = Math.floor(minsEx / 60); | ||
if (hoursEx >= 1) { | ||
minsEx %= 60; | ||
extra = `${hoursEx}h ${minsEx}m ${secsEx}s`; | ||
} | ||
} | ||
return `${extra}${extra ? ` (${seconds}s)` : `${seconds}s`}`; | ||
}; | ||
interface INames { | ||
[k: string]: string; | ||
} | ||
interface ITimer<TName> { | ||
start(...labelArr: string[]): void; | ||
end(...labelArr: string[]): void; | ||
switch(endLabel: string | string[], startLabel: string | string[]): void; | ||
log(prefix?: string): void; | ||
reset(): void; | ||
names: KeysOnly<TName>; | ||
displayNames: TName; | ||
} | ||
/** | ||
* Usage: | ||
* ```typescript | ||
* const timer = getTimer('Example'); | ||
* timer.start('TOTAL', 'intro'); | ||
* const timer = getTimer('Example', false, { | ||
* TOTAL: 'TOTAL', | ||
* INTRO: 'Action 1', | ||
* ENDING: 'Action 2' | ||
* }); | ||
* timer.start(timer.TOTAL, timer.INTRO); | ||
* await wait(seconds(4)); // do something async | ||
* timer.switch('intro', 'ending'); // same as calling end('intro') and start('ending') | ||
* timer.switch(timer.INTRO, timer.ENDING); // same as calling end(timer.INTRO) and start(timer.ENDING) | ||
* await wait(seconds(6)); // do something async | ||
* timer.end('TOTAL', 'ending'); | ||
* timer.end(timer.TOTAL, timer.ENDING); | ||
* timer.log(); | ||
@@ -23,11 +63,22 @@ * ``` | ||
* TOTAL: 10s | ||
* intro: 4s | ||
* ending: 6s | ||
* Action 1: 4s | ||
* Action 2: 6s | ||
* ``` | ||
*/ | ||
export const getTimer = (name?: string) => { | ||
export const getTimer = <TName extends INames>(name?: string, verbose: boolean = false, displayNames?: TName): ITimer<TName> & KeysOnly<TName> => { | ||
let startTimes: { [label: string]: ms } = {}; | ||
let endTimes: { [label: string]: ms } = {}; | ||
let dispNames = (displayNames || { TOTAL: 'TOTAL' }) as TName; | ||
const names = Object.fromEntries(Object.keys(dispNames).map((key) => [key, key])) as KeysOnly<TName>; | ||
const logLine = (label: string, prefix: string = '') => { | ||
const start = startTimes[label]; | ||
const end = endTimes[label] || Date.now(); | ||
const duration = end - start; | ||
console.log(`${prefix}${dispNames[label] || label}: ${formatDuration(duration)}`); | ||
}; | ||
return { | ||
...names, | ||
start(...labelArr: string[]) { | ||
@@ -41,2 +92,6 @@ for (let label of labelArr) { | ||
endTimes[label] = Date.now(); | ||
if (verbose) { | ||
logLine(label); | ||
console.log(''); | ||
} | ||
} | ||
@@ -52,6 +107,3 @@ }, | ||
for (let label of Object.keys(startTimes)) { | ||
const start = startTimes[label]; | ||
const end = endTimes[label] || Date.now(); | ||
const duration = end - start; | ||
console.log(` ${label}: ${duration / SECOND}s`); | ||
logLine(label, ' '); | ||
} | ||
@@ -63,3 +115,5 @@ console.log(''); | ||
endTimes = {}; | ||
} | ||
}, | ||
names, | ||
displayNames: dispNames | ||
}; | ||
@@ -66,0 +120,0 @@ }; |
Sorry, the diff of this file is not supported yet
74177
14
2258
440