@pnpm/fs.graceful-fs 1100.2.4 → 1100.2.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @pnpm/fs.graceful-fs
2
2
 
3
+ ## 1100.2.5
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies:
8
+ - @pnpm/error@1100.2.2
9
+
3
10
  ## 1100.2.4
4
11
 
5
12
  ### Patch Changes
package/lib/index.d.ts CHANGED
@@ -55,10 +55,10 @@ export declare function unlinkWithRetry(target: string): void;
55
55
  * Runs a filesystem operation with the retry policy of
56
56
  * {@link renameFileWithRetry}.
57
57
  */
58
- export declare function withFileLockRetry<T>(operation: () => T): T;
58
+ export declare function withFileLockRetry<Result>(operation: () => Result): Result;
59
59
  /**
60
60
  * Asynchronous {@link withFileLockRetry}, which waits between attempts
61
61
  * without blocking the event loop.
62
62
  */
63
- export declare function withFileLockRetryAsync<T>(operation: () => Promise<T>): Promise<T>;
63
+ export declare function withFileLockRetryAsync<Result>(operation: () => Promise<Result>): Promise<Result>;
64
64
  export declare function isTransientFileLockError(err: unknown): err is NodeJS.ErrnoException;
package/lib/index.js CHANGED
@@ -31,23 +31,33 @@ function withEagainRetry(fn, maxRetries = 15) {
31
31
  return (...args) => {
32
32
  let attempts = 0;
33
33
  while (attempts <= maxRetries) {
34
- try {
35
- return fn(...args);
36
- }
37
- catch (err) {
38
- if (isError(err) && 'code' in err && err.code === 'EAGAIN' && attempts < maxRetries) {
39
- attempts++;
40
- // Exponential backoff: wait 2^attempts milliseconds, max 300ms
41
- const delay = Math.min(Math.pow(2, attempts), 300);
42
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delay);
43
- continue;
44
- }
45
- throw err;
46
- }
34
+ const outcome = tryCallWithEagain(fn, args, attempts, maxRetries);
35
+ if (outcome.success)
36
+ return outcome.value;
37
+ attempts = outcome.attempts;
47
38
  }
48
39
  throw new Error('Unreachable');
49
40
  };
50
41
  }
42
+ function tryCallWithEagain(fn, args, attempts, maxRetries) {
43
+ try {
44
+ return { success: true, value: fn(...args) };
45
+ }
46
+ catch (err) {
47
+ if (!isEagainError(err) || attempts >= maxRetries)
48
+ throw err;
49
+ const nextAttempts = attempts + 1;
50
+ waitEagainBackoff(nextAttempts);
51
+ return { success: false, attempts: nextAttempts };
52
+ }
53
+ }
54
+ function isEagainError(err) {
55
+ return isError(err) && 'code' in err && err.code === 'EAGAIN';
56
+ }
57
+ function waitEagainBackoff(attempts) {
58
+ const delay = Math.min(Math.pow(2, attempts), 300);
59
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delay);
60
+ }
51
61
  /**
52
62
  * Renames `src` over `dest`, retrying Windows EBUSY errors for up to a minute.
53
63
  * Windows drives mounted into WSL get the same retries.
@@ -117,19 +127,22 @@ export async function withFileLockRetryAsync(operation) {
117
127
  const retry = createFileLockRetry();
118
128
  for (;;) {
119
129
  try {
120
- // eslint-disable-next-line no-await-in-loop
130
+ // eslint-disable-next-line no-await-in-loop -- the next attempt runs only after this one fails
121
131
  return await operation();
122
132
  }
123
133
  catch (err) {
124
- const delayMs = retry.delayBeforeNextAttempt(err);
125
- if (delayMs > 0) {
126
- // eslint-disable-next-line no-await-in-loop
127
- await new Promise((resolve) => setTimeout(resolve, delayMs));
128
- }
129
- retry.checkBudgetAfterDelay(err);
134
+ // eslint-disable-next-line no-await-in-loop -- backs off before the next attempt
135
+ await backOffWithFileLockRetryAsync(retry, err);
130
136
  }
131
137
  }
132
138
  }
139
+ async function backOffWithFileLockRetryAsync(retry, err) {
140
+ const delayMs = retry.delayBeforeNextAttempt(err);
141
+ if (delayMs > 0) {
142
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
143
+ }
144
+ retry.checkBudgetAfterDelay(err);
145
+ }
133
146
  function createFileLockRetry() {
134
147
  const startedAt = Date.now();
135
148
  let backoffMs = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/fs.graceful-fs",
3
- "version": "1100.2.4",
3
+ "version": "1100.2.5",
4
4
  "description": "Promisified graceful-fs",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -27,12 +27,12 @@
27
27
  "!*.map"
28
28
  ],
29
29
  "dependencies": {
30
- "@pnpm/error": "1100.2.1",
30
+ "@pnpm/error": "1100.2.2",
31
31
  "graceful-fs": "^4.2.11"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@jest/globals": "30.4.1",
35
- "@pnpm/fs.graceful-fs": "1100.2.4",
35
+ "@pnpm/fs.graceful-fs": "1100.2.5",
36
36
  "@types/graceful-fs": "^4.1.9"
37
37
  },
38
38
  "engines": {