@rushstack/node-core-library
Advanced tools
+8
-1
| # Change Log - @rushstack/node-core-library | ||
| This log was last generated on Thu, 01 May 2025 00:11:12 GMT and should not be manually modified. | ||
| This log was last generated on Wed, 23 Jul 2025 20:55:57 GMT and should not be manually modified. | ||
| ## 5.14.0 | ||
| Wed, 23 Jul 2025 20:55:57 GMT | ||
| ### Minor changes | ||
| - Add a `Async.runWithTimeoutAsync` function that executes an async function, resolving if the specified timeout elapses first. | ||
| ## 5.13.1 | ||
@@ -6,0 +13,0 @@ Thu, 01 May 2025 00:11:12 GMT |
@@ -8,5 +8,5 @@ // This file is read by tools that parse documentation comments conforming to the TSDoc standard. | ||
| "packageName": "@microsoft/api-extractor", | ||
| "packageVersion": "7.52.5" | ||
| "packageVersion": "7.52.11" | ||
| } | ||
| ] | ||
| } |
+46
-2
@@ -18,6 +18,23 @@ /** | ||
| /** | ||
| * Optionally used with the {@link (Async:class).(forEachAsync:2)} to enable weighted operations where an operation can | ||
| * take up more or less than one concurrency unit. | ||
| * Optionally used with the {@link (Async:class).(forEachAsync:2)} to enable weighted operations where an | ||
| * operation can take up more or less than one concurrency unit. | ||
| */ | ||
| weighted?: boolean; | ||
| /** | ||
| * This option affects the handling of task weights, applying a softer policy that favors maximizing parallelism | ||
| * instead of avoiding overload. | ||
| * | ||
| * @remarks | ||
| * By default, a new task cannot start executing if doing so would push the total weight above the concurrency limit. | ||
| * Set `allowOversubscription` to true to relax this rule, allowing a new task to start as long as the current | ||
| * total weight is below the concurrency limit. Either way, a task cannot start if the total weight already equals | ||
| * the concurrency limit; therefore, `allowOversubscription` has no effect when all tasks have weight 1. | ||
| * | ||
| * Example: Suppose the concurrency limit is 8, and seven tasks are running whose weights are 1, so the current | ||
| * total weight is 7. If an available task has weight 2, that would push the total weight to 9, exceeding | ||
| * the limit. This task can start only if `allowOversubscription` is true. | ||
| * | ||
| * @defaultValue false | ||
| */ | ||
| allowOversubscription?: boolean; | ||
| } | ||
@@ -49,2 +66,22 @@ /** | ||
| * @remarks | ||
| * Used with {@link Async.runWithTimeoutAsync}. | ||
| * | ||
| * @public | ||
| */ | ||
| export interface IRunWithTimeoutOptions<TResult> { | ||
| /** | ||
| * The action to be performed. The action is executed with a timeout. | ||
| */ | ||
| action: () => Promise<TResult> | TResult; | ||
| /** | ||
| * The timeout in milliseconds. | ||
| */ | ||
| timeoutMs: number; | ||
| /** | ||
| * The message to use for the error if the timeout is reached. | ||
| */ | ||
| timeoutMessage?: string; | ||
| } | ||
| /** | ||
| * @remarks | ||
| * Used with {@link (Async:class).(forEachAsync:2)} and {@link (Async:class).(mapAsync:2)}. | ||
@@ -151,2 +188,3 @@ * | ||
| * and the first operation has a weight of 2, then only one more operation can be in progress. | ||
| * Operations may exceed the concurrency limit based on the `allowOversubscription` option. | ||
| * | ||
@@ -182,2 +220,8 @@ * If `callback` throws a synchronous exception, or if it returns a promise that rejects, | ||
| static getSignal(): [Promise<void>, () => void, (err: Error) => void]; | ||
| /** | ||
| * Runs a promise with a timeout. If the promise does not resolve within the specified timeout, | ||
| * it will reject with an error. | ||
| * @remarks If the action is completely synchronous, runWithTimeoutAsync doesn't do anything meaningful. | ||
| */ | ||
| static runWithTimeoutAsync<TResult>({ action, timeoutMs, timeoutMessage }: IRunWithTimeoutOptions<TResult>): Promise<TResult>; | ||
| } | ||
@@ -184,0 +228,0 @@ /** |
+35
-3
@@ -45,3 +45,6 @@ "use strict"; | ||
| let promiseHasResolvedOrRejected = false; | ||
| // iterator that is stored when the loop exits early due to not enough concurrency | ||
| let nextIterator = undefined; | ||
| async function queueOperationsAsync() { | ||
| var _a; | ||
| while (concurrentUnitsInProgress < concurrency && | ||
@@ -55,3 +58,3 @@ !iteratorIsComplete && | ||
| concurrentUnitsInProgress += limitedConcurrency; | ||
| const currentIteratorResult = await iterator.next(); | ||
| const currentIteratorResult = nextIterator !== null && nextIterator !== void 0 ? nextIterator : (await iterator.next()); | ||
| // eslint-disable-next-line require-atomic-updates | ||
@@ -66,4 +69,14 @@ iteratorIsComplete = !!currentIteratorResult.done; | ||
| // This should allow other operations to execute. | ||
| concurrentUnitsInProgress -= limitedConcurrency; | ||
| // Wait until there's enough capacity to run this job, this function will be re-entered as tasks call `onOperationCompletionAsync` | ||
| const wouldExceedConcurrency = concurrentUnitsInProgress + weight > concurrency; | ||
| const allowOversubscription = (_a = options === null || options === void 0 ? void 0 : options.allowOversubscription) !== null && _a !== void 0 ? _a : false; | ||
| if (!allowOversubscription && wouldExceedConcurrency) { | ||
| // eslint-disable-next-line require-atomic-updates | ||
| nextIterator = currentIteratorResult; | ||
| break; | ||
| } | ||
| // eslint-disable-next-line require-atomic-updates | ||
| nextIterator = undefined; | ||
| concurrentUnitsInProgress += weight; | ||
| concurrentUnitsInProgress -= limitedConcurrency; | ||
| Promise.resolve(callback(currentIteratorValue.element, arrayIndex++)) | ||
@@ -122,3 +135,2 @@ .then(async () => { | ||
| let retryCount = 0; | ||
| // eslint-disable-next-line no-constant-condition | ||
| while (true) { | ||
@@ -156,2 +168,22 @@ try { | ||
| } | ||
| /** | ||
| * Runs a promise with a timeout. If the promise does not resolve within the specified timeout, | ||
| * it will reject with an error. | ||
| * @remarks If the action is completely synchronous, runWithTimeoutAsync doesn't do anything meaningful. | ||
| */ | ||
| static async runWithTimeoutAsync({ action, timeoutMs, timeoutMessage = 'Operation timed out' }) { | ||
| let timeoutHandle; | ||
| const promise = Promise.resolve(action()); | ||
| const timeoutPromise = new Promise((resolve, reject) => { | ||
| timeoutHandle = setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs); | ||
| }); | ||
| try { | ||
| return Promise.race([promise, timeoutPromise]); | ||
| } | ||
| finally { | ||
| if (timeoutHandle) { | ||
| clearTimeout(timeoutHandle); | ||
| } | ||
| } | ||
| } | ||
| } | ||
@@ -158,0 +190,0 @@ exports.Async = Async; |
@@ -87,5 +87,3 @@ "use strict"; | ||
| const PARENT_PROCESS_ID_GROUP = 'ppid'; | ||
| // eslint-disable-next-line @rushstack/security/no-unsafe-regexp | ||
| const PROCESS_LIST_ENTRY_REGEX_WIN32 = new RegExp(`^(?<${NAME_GROUP}>.+?)\\s+(?<${PARENT_PROCESS_ID_GROUP}>\\d+)\\s+(?<${PROCESS_ID_GROUP}>\\d+)\\s*$`); | ||
| // eslint-disable-next-line @rushstack/security/no-unsafe-regexp | ||
| const PROCESS_LIST_ENTRY_REGEX_UNIX = new RegExp(`^\\s*(?<${PARENT_PROCESS_ID_GROUP}>\\d+)\\s+(?<${PROCESS_ID_GROUP}>\\d+)\\s+(?<${NAME_GROUP}>.+?)\\s*$`); | ||
@@ -357,3 +355,2 @@ function parseProcessInfoEntry(line, existingProcessInfoById, platform) { | ||
| } | ||
| /* eslint-enable @rushstack/no-new-null */ | ||
| /** | ||
@@ -360,0 +357,0 @@ * Get the list of processes currently running on the system, keyed by the process ID. |
@@ -1228,7 +1228,5 @@ "use strict"; | ||
| if (FileSystem.isFileDoesNotExistError(error)) { | ||
| // eslint-disable-line @typescript-eslint/no-use-before-define | ||
| error.message = `File does not exist: ${error.path}\n${error.message}`; | ||
| } | ||
| else if (FileSystem.isFolderDoesNotExistError(error)) { | ||
| // eslint-disable-line @typescript-eslint/no-use-before-define | ||
| error.message = `Folder does not exist: ${error.path}\n${error.message}`; | ||
@@ -1240,15 +1238,11 @@ } | ||
| const extendedError = error; | ||
| // eslint-disable-line @typescript-eslint/no-use-before-define | ||
| error.message = `File or folder already exists: ${extendedError.dest}\n${error.message}`; | ||
| } | ||
| else if (FileSystem.isUnlinkNotPermittedError(error)) { | ||
| // eslint-disable-line @typescript-eslint/no-use-before-define | ||
| error.message = `File or folder could not be deleted: ${error.path}\n${error.message}`; | ||
| } | ||
| else if (FileSystem.isDirectoryError(error)) { | ||
| // eslint-disable-line @typescript-eslint/no-use-before-define | ||
| error.message = `Target is a folder, not a file: ${error.path}\n${error.message}`; | ||
| } | ||
| else if (FileSystem.isNotDirectoryError(error)) { | ||
| // eslint-disable-line @typescript-eslint/no-use-before-define | ||
| error.message = `Target is not a folder: ${error.path}\n${error.message}`; | ||
@@ -1255,0 +1249,0 @@ } |
+1
-1
@@ -8,3 +8,3 @@ /// <reference types="node" preserve="true" /> | ||
| export { AlreadyReportedError } from './AlreadyReportedError'; | ||
| export { Async, AsyncQueue, type IAsyncParallelismOptions, type IRunWithRetriesOptions, type IWeighted } from './Async'; | ||
| export { Async, AsyncQueue, type IAsyncParallelismOptions, type IRunWithRetriesOptions, type IRunWithTimeoutOptions, type IWeighted } from './Async'; | ||
| export type { Brand } from './PrimitiveTypes'; | ||
@@ -11,0 +11,0 @@ export { FileConstants, FolderConstants } from './Constants'; |
+0
-2
@@ -141,3 +141,2 @@ "use strict"; | ||
| */ | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| static sortMapKeys(map, keyComparer = Sort.compareByValue) { | ||
@@ -198,3 +197,2 @@ // Sorting a map is expensive, so first check whether it's already sorted. | ||
| */ | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| static sortSet(set, comparer = Sort.compareByValue) { | ||
@@ -201,0 +199,0 @@ // Sorting a set is expensive, so first check whether it's already sorted. |
+4
-4
| { | ||
| "name": "@rushstack/node-core-library", | ||
| "version": "5.13.1", | ||
| "version": "5.14.0-pr5355.0", | ||
| "description": "Core libraries that every NodeJS toolchain project should use", | ||
@@ -24,3 +24,3 @@ "main": "lib/index.js", | ||
| "devDependencies": { | ||
| "@rushstack/heft": "0.73.2", | ||
| "@rushstack/heft": "0.74.3", | ||
| "@types/fs-extra": "7.0.0", | ||
@@ -30,4 +30,4 @@ "@types/jju": "1.4.1", | ||
| "@types/semver": "7.5.0", | ||
| "decoupled-local-node-rig": "1.0.0", | ||
| "local-eslint-config": "1.0.0" | ||
| "eslint": "~9.25.1", | ||
| "decoupled-local-node-rig": "1.0.0" | ||
| }, | ||
@@ -34,0 +34,0 @@ "peerDependencies": { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 3 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 3 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1244407
0.76%17678
0.69%2
100%119
12.26%