forked from sashin/sashinexists
run npm install to generate a package lock
This commit is contained in:
10
node_modules/stubborn-fs/.editorconfig
generated
vendored
Normal file
10
node_modules/stubborn-fs/.editorconfig
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
3
node_modules/stubborn-fs/dist/attemptify.d.ts
generated
vendored
Normal file
3
node_modules/stubborn-fs/dist/attemptify.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
declare const attemptifyAsync: <FN extends Function>(fn: FN, onError: (error: unknown) => undefined) => FN;
|
||||
declare const attemptifySync: <FN extends Function>(fn: FN, onError: (error: unknown) => undefined) => FN;
|
||||
export { attemptifyAsync, attemptifySync };
|
||||
19
node_modules/stubborn-fs/dist/attemptify.js
generated
vendored
Normal file
19
node_modules/stubborn-fs/dist/attemptify.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/* MAIN */
|
||||
//FIXME: The return type of these functions is wrong, it doesn't account for returning "undefined", but a correct type cannot be written because generics cannot be extended properly, it seems
|
||||
const attemptifyAsync = (fn, onError) => {
|
||||
return function attemptified(...args) {
|
||||
return fn.apply(undefined, args).catch(onError);
|
||||
};
|
||||
};
|
||||
const attemptifySync = (fn, onError) => {
|
||||
return function attemptified(...args) {
|
||||
try {
|
||||
return fn.apply(undefined, args);
|
||||
}
|
||||
catch (error) {
|
||||
return onError(error);
|
||||
}
|
||||
};
|
||||
};
|
||||
/* EXPORT */
|
||||
export { attemptifyAsync, attemptifySync };
|
||||
4
node_modules/stubborn-fs/dist/constants.d.ts
generated
vendored
Normal file
4
node_modules/stubborn-fs/dist/constants.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
declare const IS_USER_ROOT: boolean;
|
||||
declare const LIMIT_FILES_DESCRIPTORS = 10000;
|
||||
declare const NOOP: () => undefined;
|
||||
export { IS_USER_ROOT, LIMIT_FILES_DESCRIPTORS, NOOP };
|
||||
8
node_modules/stubborn-fs/dist/constants.js
generated
vendored
Normal file
8
node_modules/stubborn-fs/dist/constants.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/* IMPORT */
|
||||
import process from 'node:process';
|
||||
/* MAIN */
|
||||
const IS_USER_ROOT = process.getuid ? !process.getuid() : false;
|
||||
const LIMIT_FILES_DESCRIPTORS = 10000; //TODO: Fetch the real limit from the filesystem, somehow
|
||||
const NOOP = () => undefined;
|
||||
/* EXPORT */
|
||||
export { IS_USER_ROOT, LIMIT_FILES_DESCRIPTORS, NOOP };
|
||||
8
node_modules/stubborn-fs/dist/handlers.d.ts
generated
vendored
Normal file
8
node_modules/stubborn-fs/dist/handlers.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/// <reference types="node" />
|
||||
declare const Handlers: {
|
||||
isChangeErrorOk: (error: unknown) => boolean;
|
||||
isNodeError: (error: unknown) => error is NodeJS.ErrnoException;
|
||||
isRetriableError: (error: unknown) => boolean;
|
||||
onChangeError: (error: unknown) => undefined;
|
||||
};
|
||||
export default Handlers;
|
||||
36
node_modules/stubborn-fs/dist/handlers.js
generated
vendored
Normal file
36
node_modules/stubborn-fs/dist/handlers.js
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/* IMPORT */
|
||||
import { IS_USER_ROOT } from './constants.js';
|
||||
/* MAIN */
|
||||
const Handlers = {
|
||||
/* API */
|
||||
isChangeErrorOk: (error) => {
|
||||
if (!Handlers.isNodeError(error))
|
||||
return false;
|
||||
const { code } = error;
|
||||
if (code === 'ENOSYS')
|
||||
return true;
|
||||
if (!IS_USER_ROOT && (code === 'EINVAL' || code === 'EPERM'))
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
isNodeError: (error) => {
|
||||
return (error instanceof Error);
|
||||
},
|
||||
isRetriableError: (error) => {
|
||||
if (!Handlers.isNodeError(error))
|
||||
return false;
|
||||
const { code } = error;
|
||||
if (code === 'EMFILE' || code === 'ENFILE' || code === 'EAGAIN' || code === 'EBUSY' || code === 'EACCESS' || code === 'EACCES' || code === 'EACCS' || code === 'EPERM')
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
onChangeError: (error) => {
|
||||
if (!Handlers.isNodeError(error))
|
||||
throw error;
|
||||
if (Handlers.isChangeErrorOk(error))
|
||||
return;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
/* EXPORT */
|
||||
export default Handlers;
|
||||
42
node_modules/stubborn-fs/dist/index.d.ts
generated
vendored
Normal file
42
node_modules/stubborn-fs/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/// <reference types="node" />
|
||||
import fs from 'node:fs';
|
||||
declare const FS: {
|
||||
attempt: {
|
||||
chmod: typeof fs.chmod.__promisify__;
|
||||
chown: typeof fs.chown.__promisify__;
|
||||
close: typeof fs.close.__promisify__;
|
||||
fsync: typeof fs.fsync.__promisify__;
|
||||
mkdir: typeof fs.mkdir.__promisify__;
|
||||
realpath: typeof fs.realpath.__promisify__;
|
||||
stat: typeof fs.stat.__promisify__;
|
||||
unlink: typeof fs.unlink.__promisify__;
|
||||
chmodSync: typeof fs.chmodSync;
|
||||
chownSync: typeof fs.chownSync;
|
||||
closeSync: typeof fs.closeSync;
|
||||
existsSync: typeof fs.existsSync;
|
||||
fsyncSync: typeof fs.fsync;
|
||||
mkdirSync: typeof fs.mkdirSync;
|
||||
realpathSync: typeof fs.realpathSync;
|
||||
statSync: fs.StatSyncFn;
|
||||
unlinkSync: typeof fs.unlinkSync;
|
||||
};
|
||||
retry: {
|
||||
close: (timeout: number) => typeof fs.close.__promisify__;
|
||||
fsync: (timeout: number) => typeof fs.fsync.__promisify__;
|
||||
open: (timeout: number) => typeof fs.open.__promisify__;
|
||||
readFile: (timeout: number) => typeof fs.readFile.__promisify__;
|
||||
rename: (timeout: number) => typeof fs.rename.__promisify__;
|
||||
stat: (timeout: number) => typeof fs.stat.__promisify__;
|
||||
write: (timeout: number) => typeof fs.write.__promisify__;
|
||||
writeFile: (timeout: number) => typeof fs.writeFile.__promisify__;
|
||||
closeSync: (timeout: number) => typeof fs.closeSync;
|
||||
fsyncSync: (timeout: number) => typeof fs.fsyncSync;
|
||||
openSync: (timeout: number) => typeof fs.openSync;
|
||||
readFileSync: (timeout: number) => typeof fs.readFileSync;
|
||||
renameSync: (timeout: number) => typeof fs.renameSync;
|
||||
statSync: (timeout: number) => fs.StatSyncFn;
|
||||
writeSync: (timeout: number) => typeof fs.writeSync;
|
||||
writeFileSync: (timeout: number) => typeof fs.writeFileSync;
|
||||
};
|
||||
};
|
||||
export default FS;
|
||||
53
node_modules/stubborn-fs/dist/index.js
generated
vendored
Normal file
53
node_modules/stubborn-fs/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/* IMPORT */
|
||||
import fs from 'node:fs';
|
||||
import { promisify } from 'node:util';
|
||||
import { attemptifyAsync, attemptifySync } from './attemptify.js';
|
||||
import { NOOP } from './constants.js';
|
||||
import Handlers from './handlers.js';
|
||||
import { retryifyAsync, retryifySync } from './retryify.js';
|
||||
/* MAIN */
|
||||
const FS = {
|
||||
attempt: {
|
||||
/* ASYNC */
|
||||
chmod: attemptifyAsync(promisify(fs.chmod), Handlers.onChangeError),
|
||||
chown: attemptifyAsync(promisify(fs.chown), Handlers.onChangeError),
|
||||
close: attemptifyAsync(promisify(fs.close), NOOP),
|
||||
fsync: attemptifyAsync(promisify(fs.fsync), NOOP),
|
||||
mkdir: attemptifyAsync(promisify(fs.mkdir), NOOP),
|
||||
realpath: attemptifyAsync(promisify(fs.realpath), NOOP),
|
||||
stat: attemptifyAsync(promisify(fs.stat), NOOP),
|
||||
unlink: attemptifyAsync(promisify(fs.unlink), NOOP),
|
||||
/* SYNC */
|
||||
chmodSync: attemptifySync(fs.chmodSync, Handlers.onChangeError),
|
||||
chownSync: attemptifySync(fs.chownSync, Handlers.onChangeError),
|
||||
closeSync: attemptifySync(fs.closeSync, NOOP),
|
||||
existsSync: attemptifySync(fs.existsSync, NOOP),
|
||||
fsyncSync: attemptifySync(fs.fsync, NOOP),
|
||||
mkdirSync: attemptifySync(fs.mkdirSync, NOOP),
|
||||
realpathSync: attemptifySync(fs.realpathSync, NOOP),
|
||||
statSync: attemptifySync(fs.statSync, NOOP),
|
||||
unlinkSync: attemptifySync(fs.unlinkSync, NOOP)
|
||||
},
|
||||
retry: {
|
||||
/* ASYNC */
|
||||
close: retryifyAsync(promisify(fs.close), Handlers.isRetriableError),
|
||||
fsync: retryifyAsync(promisify(fs.fsync), Handlers.isRetriableError),
|
||||
open: retryifyAsync(promisify(fs.open), Handlers.isRetriableError),
|
||||
readFile: retryifyAsync(promisify(fs.readFile), Handlers.isRetriableError),
|
||||
rename: retryifyAsync(promisify(fs.rename), Handlers.isRetriableError),
|
||||
stat: retryifyAsync(promisify(fs.stat), Handlers.isRetriableError),
|
||||
write: retryifyAsync(promisify(fs.write), Handlers.isRetriableError),
|
||||
writeFile: retryifyAsync(promisify(fs.writeFile), Handlers.isRetriableError),
|
||||
/* SYNC */
|
||||
closeSync: retryifySync(fs.closeSync, Handlers.isRetriableError),
|
||||
fsyncSync: retryifySync(fs.fsyncSync, Handlers.isRetriableError),
|
||||
openSync: retryifySync(fs.openSync, Handlers.isRetriableError),
|
||||
readFileSync: retryifySync(fs.readFileSync, Handlers.isRetriableError),
|
||||
renameSync: retryifySync(fs.renameSync, Handlers.isRetriableError),
|
||||
statSync: retryifySync(fs.statSync, Handlers.isRetriableError),
|
||||
writeSync: retryifySync(fs.writeSync, Handlers.isRetriableError),
|
||||
writeFileSync: retryifySync(fs.writeFileSync, Handlers.isRetriableError)
|
||||
}
|
||||
};
|
||||
/* EXPORT */
|
||||
export default FS;
|
||||
3
node_modules/stubborn-fs/dist/retryify.d.ts
generated
vendored
Normal file
3
node_modules/stubborn-fs/dist/retryify.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
declare const retryifyAsync: <FN extends Function>(fn: FN, isRetriableError: (error: unknown) => boolean | void) => (timeout: number) => FN;
|
||||
declare const retryifySync: <FN extends Function>(fn: FN, isRetriableError: (error: unknown) => boolean | void) => (timeout: number) => FN;
|
||||
export { retryifyAsync, retryifySync };
|
||||
46
node_modules/stubborn-fs/dist/retryify.js
generated
vendored
Normal file
46
node_modules/stubborn-fs/dist/retryify.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/* IMPORT */
|
||||
import RetryfyQueue from './retryify_queue.js';
|
||||
/* MAIN */
|
||||
//FIXME: There are a boatload of anys here, but apparently generics cannot be extended properly, so...
|
||||
const retryifyAsync = (fn, isRetriableError) => {
|
||||
return function retrified(timestamp) {
|
||||
return function attempt(...args) {
|
||||
return RetryfyQueue.schedule().then(cleanup => {
|
||||
const onResolve = (result) => {
|
||||
cleanup();
|
||||
return result;
|
||||
};
|
||||
const onReject = (error) => {
|
||||
cleanup();
|
||||
if (Date.now() >= timestamp)
|
||||
throw error;
|
||||
if (isRetriableError(error)) {
|
||||
const delay = Math.round(100 * Math.random());
|
||||
const delayPromise = new Promise(resolve => setTimeout(resolve, delay));
|
||||
return delayPromise.then(() => attempt.apply(undefined, args));
|
||||
}
|
||||
throw error;
|
||||
};
|
||||
return fn.apply(undefined, args).then(onResolve, onReject);
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
||||
const retryifySync = (fn, isRetriableError) => {
|
||||
return function retrified(timestamp) {
|
||||
return function attempt(...args) {
|
||||
try {
|
||||
return fn.apply(undefined, args);
|
||||
}
|
||||
catch (error) {
|
||||
if (Date.now() > timestamp)
|
||||
throw error;
|
||||
if (isRetriableError(error))
|
||||
return attempt.apply(undefined, args);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
/* EXPORT */
|
||||
export { retryifyAsync, retryifySync };
|
||||
15
node_modules/stubborn-fs/dist/retryify_queue.d.ts
generated
vendored
Normal file
15
node_modules/stubborn-fs/dist/retryify_queue.d.ts
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
declare class RetryfyQueue {
|
||||
private interval;
|
||||
private intervalId?;
|
||||
private limit;
|
||||
private queueActive;
|
||||
private queueWaiting;
|
||||
init: () => void;
|
||||
reset: () => void;
|
||||
add: (fn: Function) => void;
|
||||
remove: (fn: Function) => void;
|
||||
schedule: () => Promise<Function>;
|
||||
tick: () => void;
|
||||
}
|
||||
declare const _default: RetryfyQueue;
|
||||
export default _default;
|
||||
62
node_modules/stubborn-fs/dist/retryify_queue.js
generated
vendored
Normal file
62
node_modules/stubborn-fs/dist/retryify_queue.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
/* IMPORT */
|
||||
import { LIMIT_FILES_DESCRIPTORS } from './constants.js';
|
||||
/* MAIN */
|
||||
class RetryfyQueue {
|
||||
constructor() {
|
||||
/* VARIABLES */
|
||||
this.interval = 25;
|
||||
this.intervalId = undefined;
|
||||
this.limit = LIMIT_FILES_DESCRIPTORS;
|
||||
this.queueActive = new Set();
|
||||
this.queueWaiting = new Set();
|
||||
/* LIFECYCLE API */
|
||||
this.init = () => {
|
||||
if (this.intervalId)
|
||||
return;
|
||||
this.intervalId = setInterval(this.tick, this.interval);
|
||||
};
|
||||
this.reset = () => {
|
||||
if (!this.intervalId)
|
||||
return;
|
||||
clearInterval(this.intervalId);
|
||||
delete this.intervalId;
|
||||
};
|
||||
/* API */
|
||||
this.add = (fn) => {
|
||||
this.queueWaiting.add(fn);
|
||||
if (this.queueActive.size < (this.limit / 2)) { // Active queue not under preassure, executing immediately
|
||||
this.tick();
|
||||
}
|
||||
else {
|
||||
this.init();
|
||||
}
|
||||
};
|
||||
this.remove = (fn) => {
|
||||
this.queueWaiting.delete(fn);
|
||||
this.queueActive.delete(fn);
|
||||
};
|
||||
this.schedule = () => {
|
||||
return new Promise(resolve => {
|
||||
const cleanup = () => this.remove(resolver);
|
||||
const resolver = () => resolve(cleanup);
|
||||
this.add(resolver);
|
||||
});
|
||||
};
|
||||
this.tick = () => {
|
||||
if (this.queueActive.size >= this.limit)
|
||||
return;
|
||||
if (!this.queueWaiting.size)
|
||||
return this.reset();
|
||||
for (const fn of this.queueWaiting) {
|
||||
if (this.queueActive.size >= this.limit)
|
||||
break;
|
||||
this.queueWaiting.delete(fn);
|
||||
this.queueActive.add(fn);
|
||||
fn();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
;
|
||||
/* EXPORT */
|
||||
export default new RetryfyQueue();
|
||||
21
node_modules/stubborn-fs/license
generated
vendored
Normal file
21
node_modules/stubborn-fs/license
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2022-present Fabio Spampinato
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
28
node_modules/stubborn-fs/package.json
generated
vendored
Executable file
28
node_modules/stubborn-fs/package.json
generated
vendored
Executable file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "stubborn-fs",
|
||||
"repository": "github:fabiospampinato/stubborn-fs",
|
||||
"description": "Stubborn versions of Node's fs functions that try really hard to do their job.",
|
||||
"version": "1.2.5",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"exports": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"clean": "tsex clean",
|
||||
"compile": "tsex compile",
|
||||
"compile:watch": "tsex compile --watch",
|
||||
"prepublishOnly": "tsex prepare"
|
||||
},
|
||||
"keywords": [
|
||||
"fs",
|
||||
"attempt",
|
||||
"retry",
|
||||
"stubborn",
|
||||
"reliable"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.13.0",
|
||||
"tsex": "^2.1.0",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
}
|
||||
69
node_modules/stubborn-fs/readme.md
generated
vendored
Normal file
69
node_modules/stubborn-fs/readme.md
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
# Stubborn FS
|
||||
|
||||
Stubborn versions of Node's `fs` functions that try really hard to do their job.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm install --save stubborn-fs
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The following functions are currently provided, if you need others please open an issue.
|
||||
|
||||
- `attempt` functions swallow some errors that may occur.
|
||||
- `retry` functions are executed in a loop until they succeed or the timeout is reached, in which case an error is thrown.
|
||||
|
||||
```ts
|
||||
import fs from 'stubborn-fs';
|
||||
|
||||
// Attempt functions (async)
|
||||
|
||||
fs.attempt.chmod;
|
||||
fs.attempt.chown;
|
||||
fs.attempt.close;
|
||||
fs.attempt.fsync;
|
||||
fs.attempt.mkdir;
|
||||
fs.attempt.realpath;
|
||||
fs.attempt.stat;
|
||||
fs.attempt.unlink;
|
||||
|
||||
// Attempt functions (sync)
|
||||
|
||||
fs.attempt.chmodSync;
|
||||
fs.attempt.chownSync;
|
||||
fs.attempt.closeSync;
|
||||
fs.attempt.existsSync;
|
||||
fs.attempt.fsyncSync;
|
||||
fs.attempt.mkdirSync;
|
||||
fs.attempt.realpathSync;
|
||||
fs.attempt.statSync;
|
||||
fs.attempt.unlinkSync;
|
||||
|
||||
// Retry functions (async)
|
||||
|
||||
fs.retry.close;
|
||||
fs.retry.fsync;
|
||||
fs.retry.open;
|
||||
fs.retry.readFile;
|
||||
fs.retry.rename;
|
||||
fs.retry.stat;
|
||||
fs.retry.write;
|
||||
fs.retry.writeFile;
|
||||
|
||||
// Retry functions (sync)
|
||||
|
||||
fs.retry.closeSync;
|
||||
fs.retry.fsyncSync;
|
||||
fs.retry.openSync;
|
||||
fs.retry.readFileSync;
|
||||
fs.retry.renameSync;
|
||||
fs.retry.statSync;
|
||||
fs.retry.writeSync;
|
||||
fs.retry.writeFileSync;
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT © Fabio Spampinato
|
||||
36
node_modules/stubborn-fs/src/attemptify.ts
generated
vendored
Normal file
36
node_modules/stubborn-fs/src/attemptify.ts
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
/* MAIN */
|
||||
|
||||
//FIXME: The return type of these functions is wrong, it doesn't account for returning "undefined", but a correct type cannot be written because generics cannot be extended properly, it seems
|
||||
|
||||
const attemptifyAsync = <FN extends Function> ( fn: FN, onError: (( error: unknown ) => undefined) ): FN => {
|
||||
|
||||
return function attemptified ( ...args: any ): any {
|
||||
|
||||
return fn.apply ( undefined, args ).catch ( onError );
|
||||
|
||||
} as any;
|
||||
|
||||
};
|
||||
|
||||
const attemptifySync = <FN extends Function> ( fn: FN, onError: (( error: unknown ) => undefined) ): FN => {
|
||||
|
||||
return function attemptified ( ...args: any ): any {
|
||||
|
||||
try {
|
||||
|
||||
return fn.apply ( undefined, args );
|
||||
|
||||
} catch ( error: unknown ) {
|
||||
|
||||
return onError ( error );
|
||||
|
||||
}
|
||||
|
||||
} as any;
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export {attemptifyAsync, attemptifySync};
|
||||
16
node_modules/stubborn-fs/src/constants.ts
generated
vendored
Normal file
16
node_modules/stubborn-fs/src/constants.ts
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import process from 'node:process';
|
||||
|
||||
/* MAIN */
|
||||
|
||||
const IS_USER_ROOT = process.getuid ? !process.getuid () : false;
|
||||
|
||||
const LIMIT_FILES_DESCRIPTORS = 10_000; //TODO: Fetch the real limit from the filesystem, somehow
|
||||
|
||||
const NOOP = () => undefined;
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export {IS_USER_ROOT, LIMIT_FILES_DESCRIPTORS, NOOP};
|
||||
58
node_modules/stubborn-fs/src/handlers.ts
generated
vendored
Normal file
58
node_modules/stubborn-fs/src/handlers.ts
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import {IS_USER_ROOT} from './constants';
|
||||
|
||||
/* MAIN */
|
||||
|
||||
const Handlers = {
|
||||
|
||||
/* API */
|
||||
|
||||
isChangeErrorOk: ( error: unknown ): boolean => { //URL: https://github.com/isaacs/node-graceful-fs/blob/master/polyfills.js#L315-L342
|
||||
|
||||
if ( !Handlers.isNodeError ( error ) ) return false;
|
||||
|
||||
const {code} = error;
|
||||
|
||||
if ( code === 'ENOSYS' ) return true;
|
||||
|
||||
if ( !IS_USER_ROOT && ( code === 'EINVAL' || code === 'EPERM' ) ) return true;
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
isNodeError: ( error: unknown ): error is NodeJS.ErrnoException => {
|
||||
|
||||
return ( error instanceof Error );
|
||||
|
||||
},
|
||||
|
||||
isRetriableError: ( error: unknown ): boolean => {
|
||||
|
||||
if ( !Handlers.isNodeError ( error ) ) return false;
|
||||
|
||||
const {code} = error;
|
||||
|
||||
if ( code === 'EMFILE' || code === 'ENFILE' || code === 'EAGAIN' || code === 'EBUSY' || code === 'EACCESS' || code === 'EACCES' || code === 'EACCS' || code === 'EPERM' ) return true;
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
onChangeError: ( error: unknown ): undefined => {
|
||||
|
||||
if ( !Handlers.isNodeError ( error ) ) throw error;
|
||||
|
||||
if ( Handlers.isChangeErrorOk ( error ) ) return;
|
||||
|
||||
throw error;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export default Handlers;
|
||||
59
node_modules/stubborn-fs/src/index.ts
generated
vendored
Normal file
59
node_modules/stubborn-fs/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import fs from 'node:fs';
|
||||
import {promisify} from 'node:util';
|
||||
import {attemptifyAsync, attemptifySync} from './attemptify';
|
||||
import {NOOP} from './constants';
|
||||
import Handlers from './handlers';
|
||||
import {retryifyAsync, retryifySync} from './retryify';
|
||||
|
||||
/* MAIN */
|
||||
|
||||
const FS = {
|
||||
attempt: {
|
||||
/* ASYNC */
|
||||
chmod: attemptifyAsync ( promisify ( fs.chmod ), Handlers.onChangeError ),
|
||||
chown: attemptifyAsync ( promisify ( fs.chown ), Handlers.onChangeError ),
|
||||
close: attemptifyAsync ( promisify ( fs.close ), NOOP ),
|
||||
fsync: attemptifyAsync ( promisify ( fs.fsync ), NOOP ),
|
||||
mkdir: attemptifyAsync ( promisify ( fs.mkdir ), NOOP ),
|
||||
realpath: attemptifyAsync ( promisify ( fs.realpath ), NOOP ),
|
||||
stat: attemptifyAsync ( promisify ( fs.stat ), NOOP ),
|
||||
unlink: attemptifyAsync ( promisify ( fs.unlink ), NOOP ),
|
||||
/* SYNC */
|
||||
chmodSync: attemptifySync ( fs.chmodSync, Handlers.onChangeError ),
|
||||
chownSync: attemptifySync ( fs.chownSync, Handlers.onChangeError ),
|
||||
closeSync: attemptifySync ( fs.closeSync, NOOP ),
|
||||
existsSync: attemptifySync ( fs.existsSync, NOOP ),
|
||||
fsyncSync: attemptifySync ( fs.fsync, NOOP ),
|
||||
mkdirSync: attemptifySync ( fs.mkdirSync, NOOP ),
|
||||
realpathSync: attemptifySync ( fs.realpathSync, NOOP ),
|
||||
statSync: attemptifySync ( fs.statSync, NOOP ),
|
||||
unlinkSync: attemptifySync ( fs.unlinkSync, NOOP )
|
||||
},
|
||||
retry: {
|
||||
/* ASYNC */
|
||||
close: retryifyAsync ( promisify ( fs.close ), Handlers.isRetriableError ),
|
||||
fsync: retryifyAsync ( promisify ( fs.fsync ), Handlers.isRetriableError ),
|
||||
open: retryifyAsync ( promisify ( fs.open ), Handlers.isRetriableError ),
|
||||
readFile: retryifyAsync ( promisify ( fs.readFile ), Handlers.isRetriableError ),
|
||||
rename: retryifyAsync ( promisify ( fs.rename ), Handlers.isRetriableError ),
|
||||
stat: retryifyAsync ( promisify ( fs.stat ), Handlers.isRetriableError ),
|
||||
write: retryifyAsync ( promisify ( fs.write ), Handlers.isRetriableError ),
|
||||
writeFile: retryifyAsync ( promisify ( fs.writeFile ), Handlers.isRetriableError ),
|
||||
/* SYNC */
|
||||
closeSync: retryifySync ( fs.closeSync, Handlers.isRetriableError ),
|
||||
fsyncSync: retryifySync ( fs.fsyncSync, Handlers.isRetriableError ),
|
||||
openSync: retryifySync ( fs.openSync, Handlers.isRetriableError ),
|
||||
readFileSync: retryifySync ( fs.readFileSync, Handlers.isRetriableError ),
|
||||
renameSync: retryifySync ( fs.renameSync, Handlers.isRetriableError ),
|
||||
statSync: retryifySync ( fs.statSync, Handlers.isRetriableError ),
|
||||
writeSync: retryifySync ( fs.writeSync, Handlers.isRetriableError ),
|
||||
writeFileSync: retryifySync ( fs.writeFileSync, Handlers.isRetriableError )
|
||||
}
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export default FS;
|
||||
83
node_modules/stubborn-fs/src/retryify.ts
generated
vendored
Normal file
83
node_modules/stubborn-fs/src/retryify.ts
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import RetryfyQueue from './retryify_queue';
|
||||
|
||||
/* MAIN */
|
||||
|
||||
//FIXME: There are a boatload of anys here, but apparently generics cannot be extended properly, so...
|
||||
|
||||
const retryifyAsync = <FN extends Function> ( fn: FN, isRetriableError: (( error: unknown ) => boolean | void) ): (( timeout: number ) => FN) => {
|
||||
|
||||
return function retrified ( timestamp: number ) {
|
||||
|
||||
return function attempt ( ...args: any ): any {
|
||||
|
||||
return RetryfyQueue.schedule ().then ( cleanup => {
|
||||
|
||||
const onResolve = ( result: any ): Promise<any> => {
|
||||
|
||||
cleanup ();
|
||||
|
||||
return result;
|
||||
|
||||
};
|
||||
|
||||
const onReject = ( error: unknown ): Promise<any> => {
|
||||
|
||||
cleanup ();
|
||||
|
||||
if ( Date.now () >= timestamp ) throw error;
|
||||
|
||||
if ( isRetriableError ( error ) ) {
|
||||
|
||||
const delay = Math.round ( 100 * Math.random () );
|
||||
const delayPromise = new Promise ( resolve => setTimeout ( resolve, delay ) );
|
||||
|
||||
return delayPromise.then ( () => attempt.apply ( undefined, args ) );
|
||||
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
||||
};
|
||||
|
||||
return fn.apply ( undefined, args ).then ( onResolve, onReject );
|
||||
|
||||
});
|
||||
|
||||
} as any;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
const retryifySync = <FN extends Function> ( fn: FN, isRetriableError: (( error: unknown ) => boolean | void) ): (( timeout: number ) => FN) => {
|
||||
|
||||
return function retrified ( timestamp: number ) {
|
||||
|
||||
return function attempt ( ...args: any ): any {
|
||||
|
||||
try {
|
||||
|
||||
return fn.apply ( undefined, args );
|
||||
|
||||
} catch ( error: unknown ) {
|
||||
|
||||
if ( Date.now () > timestamp ) throw error;
|
||||
|
||||
if ( isRetriableError ( error ) ) return attempt.apply ( undefined, args );
|
||||
|
||||
throw error;
|
||||
|
||||
}
|
||||
|
||||
} as any;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export {retryifyAsync, retryifySync};
|
||||
101
node_modules/stubborn-fs/src/retryify_queue.ts
generated
vendored
Normal file
101
node_modules/stubborn-fs/src/retryify_queue.ts
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import {LIMIT_FILES_DESCRIPTORS} from './constants';
|
||||
|
||||
/* MAIN */
|
||||
|
||||
class RetryfyQueue {
|
||||
|
||||
/* VARIABLES */
|
||||
|
||||
private interval: number = 25;
|
||||
private intervalId?: NodeJS.Timeout = undefined;
|
||||
private limit: number = LIMIT_FILES_DESCRIPTORS;
|
||||
private queueActive: Set<Function> = new Set ();
|
||||
private queueWaiting: Set<Function> = new Set ();
|
||||
|
||||
/* LIFECYCLE API */
|
||||
|
||||
init = (): void => {
|
||||
|
||||
if ( this.intervalId ) return;
|
||||
|
||||
this.intervalId = setInterval ( this.tick, this.interval );
|
||||
|
||||
};
|
||||
|
||||
reset = (): void => {
|
||||
|
||||
if ( !this.intervalId ) return;
|
||||
|
||||
clearInterval ( this.intervalId );
|
||||
|
||||
delete this.intervalId;
|
||||
|
||||
};
|
||||
|
||||
/* API */
|
||||
|
||||
add = ( fn: Function ): void => {
|
||||
|
||||
this.queueWaiting.add ( fn );
|
||||
|
||||
if ( this.queueActive.size < ( this.limit / 2 ) ) { // Active queue not under preassure, executing immediately
|
||||
|
||||
this.tick ();
|
||||
|
||||
} else {
|
||||
|
||||
this.init ();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
remove = ( fn: Function ): void => {
|
||||
|
||||
this.queueWaiting.delete ( fn );
|
||||
|
||||
this.queueActive.delete ( fn );
|
||||
|
||||
};
|
||||
|
||||
schedule = (): Promise<Function> => {
|
||||
|
||||
return new Promise ( resolve => {
|
||||
|
||||
const cleanup = () => this.remove ( resolver );
|
||||
|
||||
const resolver = () => resolve ( cleanup );
|
||||
|
||||
this.add ( resolver );
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
tick = (): void => {
|
||||
|
||||
if ( this.queueActive.size >= this.limit ) return;
|
||||
|
||||
if ( !this.queueWaiting.size ) return this.reset ();
|
||||
|
||||
for ( const fn of this.queueWaiting ) {
|
||||
|
||||
if ( this.queueActive.size >= this.limit ) break;
|
||||
|
||||
this.queueWaiting.delete ( fn );
|
||||
this.queueActive.add ( fn );
|
||||
|
||||
fn ();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export default new RetryfyQueue ();
|
||||
3
node_modules/stubborn-fs/tsconfig.json
generated
vendored
Executable file
3
node_modules/stubborn-fs/tsconfig.json
generated
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "tsex/tsconfig.json"
|
||||
}
|
||||
Reference in New Issue
Block a user