@mikeyt23/node-cli-utils 2.0.11 → 2.0.13
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/README.md +77 -76
- package/dist/cjs/generalUtils.d.ts +37 -1
- package/dist/cjs/generalUtils.d.ts.map +1 -1
- package/dist/cjs/generalUtils.js +52 -2
- package/dist/cjs/parallel.d.ts +118 -0
- package/dist/cjs/parallel.d.ts.map +1 -0
- package/dist/cjs/parallel.js +228 -0
- package/dist/esm/generalUtils.d.ts +37 -1
- package/dist/esm/generalUtils.d.ts.map +1 -1
- package/dist/esm/generalUtils.js +49 -1
- package/dist/esm/parallel.d.ts +118 -0
- package/dist/esm/parallel.d.ts.map +1 -0
- package/dist/esm/parallel.js +222 -0
- package/package.json +11 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A type guard useful for filtering results of `Promise.allSettled`.
|
|
3
|
+
* @example
|
|
4
|
+
* ```
|
|
5
|
+
* const settledPromises = await Promise.allSettled(promises)
|
|
6
|
+
* settledPromises.filter(isSettledRejected).map(r => r.reason)
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export declare const isSettledRejected: (input: PromiseSettledResult<unknown>) => input is PromiseRejectedResult;
|
|
10
|
+
/**
|
|
11
|
+
* A type guard useful for filtering results of `Promise.allSettled`.
|
|
12
|
+
* @example
|
|
13
|
+
* ```
|
|
14
|
+
* const settledPromises = await Promise.allSettled(promises)
|
|
15
|
+
* settledPromises.filter(isSettledFulfilled).map(r => r.value)
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare const isSettledFulfilled: <T>(input: PromiseSettledResult<T>) => input is PromiseFulfilledResult<T>;
|
|
19
|
+
/**
|
|
20
|
+
* The result from an individual operation during a call to {@link runParallel}.
|
|
21
|
+
*/
|
|
22
|
+
export interface ParallelItemResult<InputType, OutputType> {
|
|
23
|
+
/** The original item passed to the operation function. */
|
|
24
|
+
inputItem: InputType;
|
|
25
|
+
/** The result property will be undefined if the promise is rejected when operating on the item. */
|
|
26
|
+
outputResult?: OutputType;
|
|
27
|
+
/** Set to `true` if not skipped, the promise wasn't rejected and the success evaluation function returned true for the output, `false` otherwise. */
|
|
28
|
+
success: boolean;
|
|
29
|
+
/** The value of the promise rejection (often an Error object, but can technically be anything). */
|
|
30
|
+
rejectedReason?: unknown;
|
|
31
|
+
/**
|
|
32
|
+
* Items can be skipped if the {@link RunParallelOptions.shouldSkipFunc} is used and evaluated to `true` for an item, or if
|
|
33
|
+
* {@link RunParallelOptions.onlyFirstN} was passed and the item was not in range.
|
|
34
|
+
* */
|
|
35
|
+
skipped: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The result of {@link runParallel}.
|
|
39
|
+
*/
|
|
40
|
+
export declare class ParallelResult<InputType, OutputType> {
|
|
41
|
+
readonly allItemResults: ParallelItemResult<InputType, OutputType>[];
|
|
42
|
+
readonly onlyFirstN?: number;
|
|
43
|
+
constructor(allItemResults: ParallelItemResult<InputType, OutputType>[], onlyFirstN?: number);
|
|
44
|
+
get successfulItemResults(): ParallelItemResult<InputType, OutputType>[];
|
|
45
|
+
get allInputItems(): InputType[];
|
|
46
|
+
get allOutputResults(): OutputType[];
|
|
47
|
+
/** Does not include skipped items. */
|
|
48
|
+
get failedItemResults(): ParallelItemResult<InputType, OutputType>[];
|
|
49
|
+
get skippedItemResults(): ParallelItemResult<InputType, OutputType>[];
|
|
50
|
+
get rejectedItemResults(): ParallelItemResult<InputType, OutputType>[];
|
|
51
|
+
get numSuccessful(): number;
|
|
52
|
+
/** Note that this does not include promise rejections (see {@link numRejected}) or skipped items (see {@link numSkipped}). */
|
|
53
|
+
get numFailed(): number;
|
|
54
|
+
/** Note that this does not include successful promises with failure results - see {@link numFailed}. */
|
|
55
|
+
get numRejected(): number;
|
|
56
|
+
get numSkipped(): number;
|
|
57
|
+
get numTotalItems(): number;
|
|
58
|
+
/** Does not consider skipped items - only promise rejections and evaluated failures. */
|
|
59
|
+
get noFailures(): boolean;
|
|
60
|
+
logFinishedMessage(): void;
|
|
61
|
+
}
|
|
62
|
+
/** The async function to run on each item during a call to {@link runParallel}. */
|
|
63
|
+
export type OperationExecutor<OutputType, InputType> = (operationItem: InputType) => Promise<OutputType>;
|
|
64
|
+
/** The function that will determine whether each operation during a call to {@link runParallel} should be considered successful or not. */
|
|
65
|
+
export type SuccessChecker<OutputType> = (operationResult: OutputType) => boolean;
|
|
66
|
+
/** The sync or async function that determines whether a particular item input for {@link runParallel} should be skipped or not. */
|
|
67
|
+
export type SkipChecker<InputType> = ((operationItem: InputType) => Promise<boolean>) | ((operationItem: InputType) => boolean);
|
|
68
|
+
/** Additional options that can be passed to {@link runParallel}. */
|
|
69
|
+
export interface RunParallelOptions<InputType> {
|
|
70
|
+
/**
|
|
71
|
+
* Defaults to 10 - the maximum number of tasks that will be allowed to run concurrently. While NodeJS is single-threaded,
|
|
72
|
+
* it is worth controlling the number of running tasks to avoid overwhelming I/O or an external service.
|
|
73
|
+
*/
|
|
74
|
+
maxConcurrent: number;
|
|
75
|
+
/** If provided, this will be used to determine whether or not an item should be skipped. */
|
|
76
|
+
shouldSkipFunc?: SkipChecker<InputType>;
|
|
77
|
+
/**
|
|
78
|
+
* If provided, this will limit processing to the first N items. Useful for testing new functionality on a subset of items.
|
|
79
|
+
*
|
|
80
|
+
* Note that this will completely bypass any processing of items after the first N items. So for example, there won't be
|
|
81
|
+
* "skipped" items for those not processed - they simply won't be on the result object at all.
|
|
82
|
+
*/
|
|
83
|
+
onlyFirstN?: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Run an operation against an array of items.
|
|
87
|
+
* @template OutputType The output type of each call to `operationFunc`.
|
|
88
|
+
* @template InputType The input type for each item in the `itemsToOperateOn` array.
|
|
89
|
+
* @param itemsToOperateOn The array of items of type `InputType` to operate on.
|
|
90
|
+
* @param executorFunc The async function to call on each item in `itemsToOperateOn` - should return type `OutputType`.
|
|
91
|
+
* @param isResultSuccessFunc The boolean returning function to evaluate whether each item of type `OutputType` returned should be considered successful.
|
|
92
|
+
* @param skipCheckFunc An optional boolean async function to determine whether each item of type `InputType` should be operated on.
|
|
93
|
+
* @param onlyFirstN Optional. Stop after this number of items processed.
|
|
94
|
+
* @returns A {@link ParallelResult}
|
|
95
|
+
*/
|
|
96
|
+
export declare function runParallel<InputType, OutputType>(itemsToOperateOn: Iterable<InputType>, executorFunc: OperationExecutor<OutputType, InputType>, isResultSuccessFunc: SuccessChecker<OutputType>, options?: Partial<RunParallelOptions<InputType>>): Promise<ParallelResult<InputType, OutputType>>;
|
|
97
|
+
/** The return type of {@link ParallelExecutor.processQueue}. This is a generic wrapper for {@link Promise.allSettled} with results of type {@link ParallelItemResult}.
|
|
98
|
+
*
|
|
99
|
+
* To filter the results, use type guards {@link isSettledRejected} and {@link isSettledFulfilled}.
|
|
100
|
+
*/
|
|
101
|
+
export type AllSettledResult<ParallelItemResult> = Promise<PromiseSettledResult<Awaited<ParallelItemResult>>[]>;
|
|
102
|
+
/**
|
|
103
|
+
* This class simulates a semaphore, running as many of the tasks simultaneously as possible while staying under the limit of the value passed for `maxConcurrent`.
|
|
104
|
+
*
|
|
105
|
+
* Note that NodeJS is single-threaded, so this is more about prevention of overwhelming I/O systems and external services than it is about true parallel execution.
|
|
106
|
+
*/
|
|
107
|
+
export declare class ParallelExecutor<InputType, OutputType> {
|
|
108
|
+
private maxConcurrent;
|
|
109
|
+
private numExecuting;
|
|
110
|
+
private queue;
|
|
111
|
+
private operationPromises;
|
|
112
|
+
private taskCompletePromises;
|
|
113
|
+
constructor(maxConcurrent: number);
|
|
114
|
+
queueTask(item: InputType, operationFunc: OperationExecutor<OutputType, InputType>): void;
|
|
115
|
+
processQueue(): AllSettledResult<ParallelItemResult<InputType, OutputType>>;
|
|
116
|
+
private release;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=parallel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parallel.d.ts","sourceRoot":"","sources":["../../src/parallel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { Emoji, log, trace } from './generalUtils.js';
|
|
2
|
+
import { cyan } from './colors.js';
|
|
3
|
+
/**
|
|
4
|
+
* A type guard useful for filtering results of `Promise.allSettled`.
|
|
5
|
+
* @example
|
|
6
|
+
* ```
|
|
7
|
+
* const settledPromises = await Promise.allSettled(promises)
|
|
8
|
+
* settledPromises.filter(isSettledRejected).map(r => r.reason)
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export const isSettledRejected = (input) => input.status === 'rejected';
|
|
12
|
+
/**
|
|
13
|
+
* A type guard useful for filtering results of `Promise.allSettled`.
|
|
14
|
+
* @example
|
|
15
|
+
* ```
|
|
16
|
+
* const settledPromises = await Promise.allSettled(promises)
|
|
17
|
+
* settledPromises.filter(isSettledFulfilled).map(r => r.value)
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export const isSettledFulfilled = (input) => input.status === 'fulfilled';
|
|
21
|
+
/**
|
|
22
|
+
* The result of {@link runParallel}.
|
|
23
|
+
*/
|
|
24
|
+
export class ParallelResult {
|
|
25
|
+
allItemResults;
|
|
26
|
+
onlyFirstN;
|
|
27
|
+
constructor(allItemResults, onlyFirstN) {
|
|
28
|
+
this.allItemResults = allItemResults;
|
|
29
|
+
this.onlyFirstN = onlyFirstN;
|
|
30
|
+
}
|
|
31
|
+
get successfulItemResults() {
|
|
32
|
+
return this.allItemResults.filter(r => r.success);
|
|
33
|
+
}
|
|
34
|
+
get allInputItems() {
|
|
35
|
+
return this.allItemResults.map(r => r.inputItem);
|
|
36
|
+
}
|
|
37
|
+
get allOutputResults() {
|
|
38
|
+
return this.allItemResults.filter(r => r.outputResult !== undefined).map(r => r.outputResult);
|
|
39
|
+
}
|
|
40
|
+
/** Does not include skipped items. */
|
|
41
|
+
get failedItemResults() {
|
|
42
|
+
return this.allItemResults.filter(r => !r.success && !r.skipped);
|
|
43
|
+
}
|
|
44
|
+
get skippedItemResults() {
|
|
45
|
+
return this.allItemResults.filter(r => r.skipped);
|
|
46
|
+
}
|
|
47
|
+
get rejectedItemResults() {
|
|
48
|
+
return this.allItemResults.filter(r => r.rejectedReason);
|
|
49
|
+
}
|
|
50
|
+
get numSuccessful() {
|
|
51
|
+
return this.successfulItemResults.length;
|
|
52
|
+
}
|
|
53
|
+
/** Note that this does not include promise rejections (see {@link numRejected}) or skipped items (see {@link numSkipped}). */
|
|
54
|
+
get numFailed() {
|
|
55
|
+
return this.failedItemResults.length;
|
|
56
|
+
}
|
|
57
|
+
/** Note that this does not include successful promises with failure results - see {@link numFailed}. */
|
|
58
|
+
get numRejected() {
|
|
59
|
+
return this.rejectedItemResults.length;
|
|
60
|
+
}
|
|
61
|
+
get numSkipped() {
|
|
62
|
+
return this.skippedItemResults.length;
|
|
63
|
+
}
|
|
64
|
+
get numTotalItems() {
|
|
65
|
+
return this.allItemResults.length;
|
|
66
|
+
}
|
|
67
|
+
/** Does not consider skipped items - only promise rejections and evaluated failures. */
|
|
68
|
+
get noFailures() {
|
|
69
|
+
return this.numFailed === 0 && this.numRejected === 0;
|
|
70
|
+
}
|
|
71
|
+
logFinishedMessage() {
|
|
72
|
+
const divider = '---';
|
|
73
|
+
const onlyFirstNMessage = this.onlyFirstN !== undefined ? ` (onlyFirstN set to ${this.onlyFirstN})` : '';
|
|
74
|
+
log(`${this.noFailures ? Emoji.GreenCheck : Emoji.Warning} ${cyan('runParallel')} completed - ${this.numTotalItems - this.numSkipped} items processed${onlyFirstNMessage}`);
|
|
75
|
+
if (this.numSkipped > 0) {
|
|
76
|
+
log(`${Emoji.Info} skipped ${this.numSkipped}`);
|
|
77
|
+
}
|
|
78
|
+
if (this.numRejected > 0) {
|
|
79
|
+
log(`${Emoji.Stop} Warning: some calls were rejected instead of returning a result`);
|
|
80
|
+
log(divider);
|
|
81
|
+
for (const rejected of this.rejectedItemResults) {
|
|
82
|
+
log('item: ', rejected.inputItem);
|
|
83
|
+
log('reason: ', rejected.rejectedReason);
|
|
84
|
+
log(divider);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (this.numFailed > 0) {
|
|
88
|
+
log(`${Emoji.Warning} Number of failed results: ${this.numFailed}`);
|
|
89
|
+
log(divider);
|
|
90
|
+
for (const failed of this.failedItemResults) {
|
|
91
|
+
log('item: ', failed.inputItem);
|
|
92
|
+
log('output: ', failed.outputResult);
|
|
93
|
+
log(divider);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Run an operation against an array of items.
|
|
100
|
+
* @template OutputType The output type of each call to `operationFunc`.
|
|
101
|
+
* @template InputType The input type for each item in the `itemsToOperateOn` array.
|
|
102
|
+
* @param itemsToOperateOn The array of items of type `InputType` to operate on.
|
|
103
|
+
* @param executorFunc The async function to call on each item in `itemsToOperateOn` - should return type `OutputType`.
|
|
104
|
+
* @param isResultSuccessFunc The boolean returning function to evaluate whether each item of type `OutputType` returned should be considered successful.
|
|
105
|
+
* @param skipCheckFunc An optional boolean async function to determine whether each item of type `InputType` should be operated on.
|
|
106
|
+
* @param onlyFirstN Optional. Stop after this number of items processed.
|
|
107
|
+
* @returns A {@link ParallelResult}
|
|
108
|
+
*/
|
|
109
|
+
export async function runParallel(itemsToOperateOn, executorFunc, isResultSuccessFunc, options) {
|
|
110
|
+
const defaultOptions = { maxConcurrent: 10 };
|
|
111
|
+
const mergedOptions = { ...defaultOptions, ...options };
|
|
112
|
+
const parallel = new ParallelExecutor(mergedOptions.maxConcurrent);
|
|
113
|
+
const skippedItemResults = [];
|
|
114
|
+
let i = 0;
|
|
115
|
+
for (const item of itemsToOperateOn) {
|
|
116
|
+
if (mergedOptions.onlyFirstN !== undefined && i >= mergedOptions.onlyFirstN) {
|
|
117
|
+
trace(`stopping because 'onlyFirstN' param was passed with the value ${mergedOptions.onlyFirstN}`);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
i++;
|
|
121
|
+
if (mergedOptions.shouldSkipFunc !== undefined && await mergedOptions.shouldSkipFunc(item)) {
|
|
122
|
+
trace(`skipped item: `, item);
|
|
123
|
+
skippedItemResults.push({ inputItem: item, skipped: true, success: false });
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
parallel.queueTask(item, executorFunc);
|
|
127
|
+
}
|
|
128
|
+
const promiseResults = await parallel.processQueue();
|
|
129
|
+
const itemResults = promiseResults.filter(isSettledFulfilled).map(r => r.value);
|
|
130
|
+
const promiseRejections = promiseResults.filter(isSettledRejected).map(r => r.reason);
|
|
131
|
+
if (promiseRejections.length > 0) {
|
|
132
|
+
log('---');
|
|
133
|
+
log(`${Emoji.Exclamation} Warning: control flow functions that should normally not fail had promise rejections`);
|
|
134
|
+
for (const reject of promiseRejections) {
|
|
135
|
+
log(reject);
|
|
136
|
+
log('---');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
for (const itemResult of itemResults) {
|
|
140
|
+
itemResult.success = itemResult.rejectedReason === undefined
|
|
141
|
+
&& itemResult.outputResult !== undefined
|
|
142
|
+
&& isResultSuccessFunc(itemResult.outputResult);
|
|
143
|
+
}
|
|
144
|
+
const parallelResult = new ParallelResult([...itemResults, ...skippedItemResults], mergedOptions.onlyFirstN);
|
|
145
|
+
return parallelResult;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* This class simulates a semaphore, running as many of the tasks simultaneously as possible while staying under the limit of the value passed for `maxConcurrent`.
|
|
149
|
+
*
|
|
150
|
+
* Note that NodeJS is single-threaded, so this is more about prevention of overwhelming I/O systems and external services than it is about true parallel execution.
|
|
151
|
+
*/
|
|
152
|
+
export class ParallelExecutor {
|
|
153
|
+
maxConcurrent = 0;
|
|
154
|
+
numExecuting = 0;
|
|
155
|
+
queue = [];
|
|
156
|
+
operationPromises = [];
|
|
157
|
+
taskCompletePromises = [];
|
|
158
|
+
constructor(maxConcurrent) {
|
|
159
|
+
if (maxConcurrent <= 0) {
|
|
160
|
+
throw new Error('Invalid value passed for maxConcurrent - must be greater than 0');
|
|
161
|
+
}
|
|
162
|
+
this.maxConcurrent = maxConcurrent;
|
|
163
|
+
}
|
|
164
|
+
queueTask(item, operationFunc) {
|
|
165
|
+
let completionResolver;
|
|
166
|
+
const completionPromise = new Promise((resolve) => {
|
|
167
|
+
completionResolver = resolve;
|
|
168
|
+
});
|
|
169
|
+
this.taskCompletePromises.push(completionPromise);
|
|
170
|
+
const operationWrapper = async () => {
|
|
171
|
+
return new Promise(resolve => {
|
|
172
|
+
operationFunc(item)
|
|
173
|
+
.then(output => {
|
|
174
|
+
resolve({
|
|
175
|
+
inputItem: item,
|
|
176
|
+
skipped: false,
|
|
177
|
+
success: true,
|
|
178
|
+
rejectedReason: undefined,
|
|
179
|
+
outputResult: output
|
|
180
|
+
});
|
|
181
|
+
})
|
|
182
|
+
.catch(err => {
|
|
183
|
+
resolve({
|
|
184
|
+
inputItem: item,
|
|
185
|
+
skipped: false,
|
|
186
|
+
success: false,
|
|
187
|
+
rejectedReason: err,
|
|
188
|
+
outputResult: undefined
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
const parallelTask = () => {
|
|
194
|
+
trace(`Task called - queue.length: ${this.queue.length} - numExecuting: ${this.numExecuting}`);
|
|
195
|
+
this.numExecuting++;
|
|
196
|
+
const promise = operationWrapper();
|
|
197
|
+
this.operationPromises.push(promise);
|
|
198
|
+
promise.finally(() => {
|
|
199
|
+
this.release(completionResolver);
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
this.queue.push(parallelTask);
|
|
203
|
+
}
|
|
204
|
+
async processQueue() {
|
|
205
|
+
while (this.queue.length > 0 && this.numExecuting < this.maxConcurrent) {
|
|
206
|
+
const next = this.queue.shift();
|
|
207
|
+
if (next)
|
|
208
|
+
next();
|
|
209
|
+
}
|
|
210
|
+
await Promise.allSettled(this.taskCompletePromises);
|
|
211
|
+
return await Promise.allSettled(this.operationPromises);
|
|
212
|
+
}
|
|
213
|
+
release(completionResolver) {
|
|
214
|
+
trace(`Task released - queue.length: ${this.queue.length} - numExecuting: ${this.numExecuting}`);
|
|
215
|
+
this.numExecuting--;
|
|
216
|
+
completionResolver();
|
|
217
|
+
const next = this.queue.shift();
|
|
218
|
+
if (next)
|
|
219
|
+
next();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyYWxsZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcGFyYWxsZWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFDckQsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUVsQzs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxLQUFvQyxFQUFrQyxFQUFFLENBQUMsS0FBSyxDQUFDLE1BQU0sS0FBSyxVQUFVLENBQUE7QUFDdEk7Ozs7Ozs7R0FPRztBQUNILE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLENBQUksS0FBOEIsRUFBc0MsRUFBRSxDQUFDLEtBQUssQ0FBQyxNQUFNLEtBQUssV0FBVyxDQUFBO0FBcUJ6STs7R0FFRztBQUNILE1BQU0sT0FBTyxjQUFjO0lBQ2hCLGNBQWMsQ0FBNkM7SUFDM0QsVUFBVSxDQUFTO0lBRTVCLFlBQVksY0FBMkQsRUFBRSxVQUFtQjtRQUMxRixJQUFJLENBQUMsY0FBYyxHQUFHLGNBQWMsQ0FBQTtRQUNwQyxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQTtJQUM5QixDQUFDO0lBRUQsSUFBSSxxQkFBcUI7UUFDdkIsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUNuRCxDQUFDO0lBQ0QsSUFBSSxhQUFhO1FBQ2YsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQTtJQUNsRCxDQUFDO0lBQ0QsSUFBSSxnQkFBZ0I7UUFDbEIsT0FBTyxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxD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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikeyt23/node-cli-utils",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.13",
|
|
4
4
|
"description": "Some node cli utility functions",
|
|
5
5
|
"author": "Mike Thompson",
|
|
6
6
|
"license": "MIT",
|
|
@@ -95,6 +95,16 @@
|
|
|
95
95
|
"default": "./dist/cjs/hostFileUtils.js"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
|
+
"./parallel": {
|
|
99
|
+
"import": {
|
|
100
|
+
"types": "./dist/esm/parallel.d.ts",
|
|
101
|
+
"default": "./dist/esm/parallel.js"
|
|
102
|
+
},
|
|
103
|
+
"require": {
|
|
104
|
+
"types": "./dist/cjs/parallel.d.ts",
|
|
105
|
+
"default": "./dist/cjs/parallel.js"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
98
108
|
"./testUtils": {
|
|
99
109
|
"import": {
|
|
100
110
|
"types": "./dist/esm/testUtils.d.ts",
|