@rushstack/node-core-library
Advanced tools
Comparing version 5.8.0 to 5.9.0
# Change Log - @rushstack/node-core-library | ||
This log was last generated on Tue, 10 Sep 2024 20:08:11 GMT and should not be manually modified. | ||
This log was last generated on Fri, 13 Sep 2024 00:11:42 GMT and should not be manually modified. | ||
## 5.9.0 | ||
Fri, 13 Sep 2024 00:11:42 GMT | ||
### Minor changes | ||
- Add a `Sort.sortKeys` function for sorting keys in an object | ||
- Rename `LockFile.acquire` to `Lockfile.acquireAsync`. | ||
### Patches | ||
- Fix an issue where attempting to acquire multiple `LockFile`s at the same time on POSIX would cause the second to immediately be acquired without releasing the first. | ||
## 5.8.0 | ||
@@ -6,0 +18,0 @@ Tue, 10 Sep 2024 20:08:11 GMT |
@@ -45,2 +45,6 @@ /** | ||
/** | ||
* @deprecated Use {@link LockFile.acquireAsync} instead. | ||
*/ | ||
static acquire(resourceFolder: string, resourceName: string, maxWaitMs?: number): Promise<LockFile>; | ||
/** | ||
* Attempts to create the lockfile. Will continue to loop at every 100ms until the lock becomes available | ||
@@ -59,3 +63,4 @@ * or the maxWaitMs is surpassed. | ||
*/ | ||
static acquire(resourceFolder: string, resourceName: string, maxWaitMs?: number): Promise<LockFile>; | ||
static acquireAsync(resourceFolder: string, resourceName: string, maxWaitMs?: number): Promise<LockFile>; | ||
private static _tryAcquireInner; | ||
/** | ||
@@ -62,0 +67,0 @@ * Attempts to acquire the lock on a Linux or OSX machine |
@@ -81,3 +81,3 @@ "use strict"; | ||
// So the caller can safely use this value as part of a unique process id (on the machine, without comparing | ||
// accross reboots). | ||
// across reboots). | ||
return startTimeJiffies; | ||
@@ -141,3 +141,3 @@ } | ||
const psSplit = psStdout.split('\n'); | ||
// successfuly able to run "ps", but no process was found | ||
// successfully able to run "ps", but no process was found | ||
if (psSplit[1] === '') { | ||
@@ -155,2 +155,5 @@ return undefined; | ||
exports.getProcessStartTime = getProcessStartTime; | ||
// A set of locks that currently exist in the current process, to be used when | ||
// multiple locks are acquired in the same process. | ||
const IN_PROC_LOCKS = new Set(); | ||
/** | ||
@@ -171,2 +174,3 @@ * The `LockFile` implements a file-based mutex for synchronizing access to a shared resource | ||
this._dirtyWhenAcquired = dirtyWhenAcquired; | ||
IN_PROC_LOCKS.add(filePath); | ||
} | ||
@@ -183,11 +187,16 @@ /** | ||
throw new Error(`The resource name "${resourceName}" is invalid.` + | ||
` It must be an alphanumberic string with only "-" or "." It must start with an alphanumeric character.`); | ||
` It must be an alphanumeric string with only "-" or "." It must start and end with an alphanumeric character.`); | ||
} | ||
if (process.platform === 'win32') { | ||
return path.join(path.resolve(resourceFolder), `${resourceName}.lock`); | ||
switch (process.platform) { | ||
case 'win32': { | ||
return path.resolve(resourceFolder, `${resourceName}.lock`); | ||
} | ||
case 'linux': | ||
case 'darwin': { | ||
return path.resolve(resourceFolder, `${resourceName}#${pid}.lock`); | ||
} | ||
default: { | ||
throw new Error(`File locking not implemented for platform: "${process.platform}"`); | ||
} | ||
} | ||
else if (process.platform === 'linux' || process.platform === 'darwin') { | ||
return path.join(path.resolve(resourceFolder), `${resourceName}#${pid}.lock`); | ||
} | ||
throw new Error(`File locking not implemented for platform: "${process.platform}"`); | ||
} | ||
@@ -203,11 +212,12 @@ /** | ||
FileSystem_1.FileSystem.ensureFolder(resourceFolder); | ||
if (process.platform === 'win32') { | ||
return LockFile._tryAcquireWindows(resourceFolder, resourceName); | ||
} | ||
else if (process.platform === 'linux' || process.platform === 'darwin') { | ||
return LockFile._tryAcquireMacOrLinux(resourceFolder, resourceName); | ||
} | ||
throw new Error(`File locking not implemented for platform: "${process.platform}"`); | ||
const lockFilePath = LockFile.getLockFilePath(resourceFolder, resourceName); | ||
return LockFile._tryAcquireInner(resourceFolder, resourceName, lockFilePath); | ||
} | ||
/** | ||
* @deprecated Use {@link LockFile.acquireAsync} instead. | ||
*/ | ||
static acquire(resourceFolder, resourceName, maxWaitMs) { | ||
return LockFile.acquireAsync(resourceFolder, resourceName, maxWaitMs); | ||
} | ||
/** | ||
* Attempts to create the lockfile. Will continue to loop at every 100ms until the lock becomes available | ||
@@ -226,22 +236,38 @@ * or the maxWaitMs is surpassed. | ||
*/ | ||
static acquire(resourceFolder, resourceName, maxWaitMs) { | ||
static async acquireAsync(resourceFolder, resourceName, maxWaitMs) { | ||
const interval = 100; | ||
const startTime = Date.now(); | ||
const retryLoop = async () => { | ||
const lock = LockFile.tryAcquire(resourceFolder, resourceName); | ||
const timeoutTime = maxWaitMs ? startTime + maxWaitMs : undefined; | ||
await FileSystem_1.FileSystem.ensureFolderAsync(resourceFolder); | ||
const lockFilePath = LockFile.getLockFilePath(resourceFolder, resourceName); | ||
// eslint-disable-next-line no-unmodified-loop-condition | ||
while (!timeoutTime || Date.now() <= timeoutTime) { | ||
const lock = LockFile._tryAcquireInner(resourceFolder, resourceName, lockFilePath); | ||
if (lock) { | ||
return lock; | ||
} | ||
if (maxWaitMs && Date.now() > startTime + maxWaitMs) { | ||
throw new Error(`Exceeded maximum wait time to acquire lock for resource "${resourceName}"`); | ||
} | ||
await Async_1.Async.sleepAsync(interval); | ||
return retryLoop(); | ||
}; | ||
return retryLoop(); | ||
} | ||
throw new Error(`Exceeded maximum wait time to acquire lock for resource "${resourceName}"`); | ||
} | ||
static _tryAcquireInner(resourceFolder, resourceName, lockFilePath) { | ||
if (!IN_PROC_LOCKS.has(lockFilePath)) { | ||
switch (process.platform) { | ||
case 'win32': { | ||
return LockFile._tryAcquireWindows(lockFilePath); | ||
} | ||
case 'linux': | ||
case 'darwin': { | ||
return LockFile._tryAcquireMacOrLinux(resourceFolder, resourceName, lockFilePath); | ||
} | ||
default: { | ||
throw new Error(`File locking not implemented for platform: "${process.platform}"`); | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Attempts to acquire the lock on a Linux or OSX machine | ||
*/ | ||
static _tryAcquireMacOrLinux(resourceFolder, resourceName) { | ||
static _tryAcquireMacOrLinux(resourceFolder, resourceName, pidLockFilePath) { | ||
let dirtyWhenAcquired = false; | ||
@@ -254,3 +280,2 @@ // get the current process' pid | ||
} | ||
const pidLockFilePath = LockFile.getLockFilePath(resourceFolder, resourceName); | ||
let lockFileHandle; | ||
@@ -278,3 +303,3 @@ let lockFile; | ||
// we found at least one lockfile hanging around that isn't ours | ||
const fileInFolderPath = path.join(resourceFolder, fileInFolder); | ||
const fileInFolderPath = `${resourceFolder}/${fileInFolder}`; | ||
dirtyWhenAcquired = true; | ||
@@ -364,4 +389,3 @@ // console.log(`FOUND OTHER LOCKFILE: ${otherPid}`); | ||
*/ | ||
static _tryAcquireWindows(resourceFolder, resourceName) { | ||
const lockFilePath = LockFile.getLockFilePath(resourceFolder, resourceName); | ||
static _tryAcquireWindows(lockFilePath) { | ||
let dirtyWhenAcquired = false; | ||
@@ -409,2 +433,3 @@ let fileHandle; | ||
} | ||
IN_PROC_LOCKS.delete(this._filePath); | ||
this._fileWriter.close(); | ||
@@ -411,0 +436,0 @@ if (deleteFile) { |
@@ -99,3 +99,17 @@ /** | ||
static sortSet<T>(set: Set<T>, comparer?: (x: T, y: T) => number): void; | ||
/** | ||
* Sort the keys deeply given an object or an array. | ||
* | ||
* Doesn't handle cyclic reference. | ||
* | ||
* @param object - The object to be sorted | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* console.log(Sort.sortKeys({ c: 3, b: 2, a: 1 })); // { a: 1, b: 2, c: 3} | ||
* ``` | ||
*/ | ||
static sortKeys<T extends Partial<Record<string, unknown>> | unknown[]>(object: T): T; | ||
} | ||
//# sourceMappingURL=Sort.d.ts.map |
@@ -210,4 +210,58 @@ "use strict"; | ||
} | ||
/** | ||
* Sort the keys deeply given an object or an array. | ||
* | ||
* Doesn't handle cyclic reference. | ||
* | ||
* @param object - The object to be sorted | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* console.log(Sort.sortKeys({ c: 3, b: 2, a: 1 })); // { a: 1, b: 2, c: 3} | ||
* ``` | ||
*/ | ||
static sortKeys(object) { | ||
if (!isPlainObject(object) && !Array.isArray(object)) { | ||
throw new TypeError(`Expected object or array`); | ||
} | ||
return Array.isArray(object) ? innerSortArray(object) : innerSortKeys(object); | ||
} | ||
} | ||
exports.Sort = Sort; | ||
function isPlainObject(obj) { | ||
return obj !== null && typeof obj === 'object'; | ||
} | ||
function innerSortArray(arr) { | ||
const result = []; | ||
for (const entry of arr) { | ||
if (Array.isArray(entry)) { | ||
result.push(innerSortArray(entry)); | ||
} | ||
else if (isPlainObject(entry)) { | ||
result.push(innerSortKeys(entry)); | ||
} | ||
else { | ||
result.push(entry); | ||
} | ||
} | ||
return result; | ||
} | ||
function innerSortKeys(obj) { | ||
const result = {}; | ||
const keys = Object.keys(obj).sort(); | ||
for (const key of keys) { | ||
const value = obj[key]; | ||
if (Array.isArray(value)) { | ||
result[key] = innerSortArray(value); | ||
} | ||
else if (isPlainObject(value)) { | ||
result[key] = innerSortKeys(value); | ||
} | ||
else { | ||
result[key] = value; | ||
} | ||
} | ||
return result; | ||
} | ||
//# sourceMappingURL=Sort.js.map |
{ | ||
"name": "@rushstack/node-core-library", | ||
"version": "5.8.0", | ||
"version": "5.9.0", | ||
"description": "Core libraries that every NodeJS toolchain project should use", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1204675
17086