dd
This commit is contained in:
42
extensions/Excel转JSON/node_modules/atomically/src/utils/attemptify.ts
generated
vendored
Normal file
42
extensions/Excel转JSON/node_modules/atomically/src/utils/attemptify.ts
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import {NOOP} from '../consts';
|
||||
import {Exception, FN} from '../types';
|
||||
|
||||
/* ATTEMPTIFY */
|
||||
|
||||
//TODO: Maybe publish this as a standalone package
|
||||
//FIXME: The type castings here aren't exactly correct
|
||||
|
||||
const attemptifyAsync = <T extends FN> ( fn: T, onError: FN<[Exception]> = NOOP ): T => {
|
||||
|
||||
return function () {
|
||||
|
||||
return fn.apply ( undefined, arguments ).catch ( onError );
|
||||
|
||||
} as T;
|
||||
|
||||
};
|
||||
|
||||
const attemptifySync = <T extends FN> ( fn: T, onError: FN<[Exception]> = NOOP ): T => {
|
||||
|
||||
return function () {
|
||||
|
||||
try {
|
||||
|
||||
return fn.apply ( undefined, arguments );
|
||||
|
||||
} catch ( error ) {
|
||||
|
||||
return onError ( error );
|
||||
|
||||
}
|
||||
|
||||
} as T;
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export {attemptifyAsync, attemptifySync};
|
||||
51
extensions/Excel转JSON/node_modules/atomically/src/utils/fs.ts
generated
vendored
Normal file
51
extensions/Excel转JSON/node_modules/atomically/src/utils/fs.ts
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import * as fs from 'fs';
|
||||
import {promisify} from 'util';
|
||||
import {attemptifyAsync, attemptifySync} from './attemptify';
|
||||
import Handlers from './fs_handlers';
|
||||
import {retryifyAsync, retryifySync} from './retryify';
|
||||
|
||||
/* FS */
|
||||
|
||||
const FS = {
|
||||
|
||||
chmodAttempt: attemptifyAsync ( promisify ( fs.chmod ), Handlers.onChangeError ),
|
||||
chownAttempt: attemptifyAsync ( promisify ( fs.chown ), Handlers.onChangeError ),
|
||||
closeAttempt: attemptifyAsync ( promisify ( fs.close ) ),
|
||||
fsyncAttempt: attemptifyAsync ( promisify ( fs.fsync ) ),
|
||||
mkdirAttempt: attemptifyAsync ( promisify ( fs.mkdir ) ),
|
||||
realpathAttempt: attemptifyAsync ( promisify ( fs.realpath ) ),
|
||||
statAttempt: attemptifyAsync ( promisify ( fs.stat ) ),
|
||||
unlinkAttempt: attemptifyAsync ( promisify ( fs.unlink ) ),
|
||||
|
||||
closeRetry: retryifyAsync ( promisify ( fs.close ), Handlers.isRetriableError ),
|
||||
fsyncRetry: retryifyAsync ( promisify ( fs.fsync ), Handlers.isRetriableError ),
|
||||
openRetry: retryifyAsync ( promisify ( fs.open ), Handlers.isRetriableError ),
|
||||
readFileRetry: retryifyAsync ( promisify ( fs.readFile ), Handlers.isRetriableError ),
|
||||
renameRetry: retryifyAsync ( promisify ( fs.rename ), Handlers.isRetriableError ),
|
||||
statRetry: retryifyAsync ( promisify ( fs.stat ), Handlers.isRetriableError ),
|
||||
writeRetry: retryifyAsync ( promisify ( fs.write ), Handlers.isRetriableError ),
|
||||
|
||||
chmodSyncAttempt: attemptifySync ( fs.chmodSync, Handlers.onChangeError ),
|
||||
chownSyncAttempt: attemptifySync ( fs.chownSync, Handlers.onChangeError ),
|
||||
closeSyncAttempt: attemptifySync ( fs.closeSync ),
|
||||
mkdirSyncAttempt: attemptifySync ( fs.mkdirSync ),
|
||||
realpathSyncAttempt: attemptifySync ( fs.realpathSync ),
|
||||
statSyncAttempt: attemptifySync ( fs.statSync ),
|
||||
unlinkSyncAttempt: attemptifySync ( fs.unlinkSync ),
|
||||
|
||||
closeSyncRetry: retryifySync ( fs.closeSync, Handlers.isRetriableError ),
|
||||
fsyncSyncRetry: retryifySync ( fs.fsyncSync, Handlers.isRetriableError ),
|
||||
openSyncRetry: retryifySync ( fs.openSync, Handlers.isRetriableError ),
|
||||
readFileSyncRetry: retryifySync ( fs.readFileSync, Handlers.isRetriableError ),
|
||||
renameSyncRetry: retryifySync ( fs.renameSync, Handlers.isRetriableError ),
|
||||
statSyncRetry: retryifySync ( fs.statSync, Handlers.isRetriableError ),
|
||||
writeSyncRetry: retryifySync ( fs.writeSync, Handlers.isRetriableError )
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export default FS;
|
||||
45
extensions/Excel转JSON/node_modules/atomically/src/utils/fs_handlers.ts
generated
vendored
Normal file
45
extensions/Excel转JSON/node_modules/atomically/src/utils/fs_handlers.ts
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import {IS_USER_ROOT} from '../consts';
|
||||
import {Exception} from '../types';
|
||||
|
||||
/* FS HANDLERS */
|
||||
|
||||
const Handlers = {
|
||||
|
||||
isChangeErrorOk: ( error: Exception ): boolean => { //URL: https://github.com/isaacs/node-graceful-fs/blob/master/polyfills.js#L315-L342
|
||||
|
||||
const {code} = error;
|
||||
|
||||
if ( code === 'ENOSYS' ) return true;
|
||||
|
||||
if ( !IS_USER_ROOT && ( code === 'EINVAL' || code === 'EPERM' ) ) return true;
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
isRetriableError: ( error: Exception ): boolean => {
|
||||
|
||||
const {code} = error;
|
||||
|
||||
if ( code === 'EMFILE' || code === 'ENFILE' || code === 'EAGAIN' || code === 'EBUSY' || code === 'EACCESS' || code === 'EACCS' || code === 'EPERM' ) return true;
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
onChangeError: ( error: Exception ): void => {
|
||||
|
||||
if ( Handlers.isChangeErrorOk ( error ) ) return;
|
||||
|
||||
throw error;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export default Handlers;
|
||||
28
extensions/Excel转JSON/node_modules/atomically/src/utils/lang.ts
generated
vendored
Normal file
28
extensions/Excel转JSON/node_modules/atomically/src/utils/lang.ts
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
/* LANG */
|
||||
|
||||
const Lang = {
|
||||
|
||||
isFunction: ( x: any ): x is Function => {
|
||||
|
||||
return typeof x === 'function';
|
||||
|
||||
},
|
||||
|
||||
isString: ( x: any ): x is string => {
|
||||
|
||||
return typeof x === 'string';
|
||||
|
||||
},
|
||||
|
||||
isUndefined: ( x: any ): x is undefined => {
|
||||
|
||||
return typeof x === 'undefined';
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export default Lang;
|
||||
78
extensions/Excel转JSON/node_modules/atomically/src/utils/retryify.ts
generated
vendored
Normal file
78
extensions/Excel转JSON/node_modules/atomically/src/utils/retryify.ts
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import {Exception, FN} from '../types';
|
||||
import RetryfyQueue from './retryify_queue';
|
||||
|
||||
/* RETRYIFY */
|
||||
|
||||
const retryifyAsync = <T extends FN> ( fn: T, isRetriableError: FN<[Exception], boolean | void> ): FN<[number], T> => {
|
||||
|
||||
return function ( timestamp: number ) {
|
||||
|
||||
return function attempt () {
|
||||
|
||||
return RetryfyQueue.schedule ().then ( cleanup => {
|
||||
|
||||
return fn.apply ( undefined, arguments ).then ( result => {
|
||||
|
||||
cleanup ();
|
||||
|
||||
return result;
|
||||
|
||||
}, error => {
|
||||
|
||||
cleanup ();
|
||||
|
||||
if ( Date.now () >= timestamp ) throw error;
|
||||
|
||||
if ( isRetriableError ( error ) ) {
|
||||
|
||||
const delay = Math.round ( 100 + ( 400 * Math.random () ) ),
|
||||
delayPromise = new Promise ( resolve => setTimeout ( resolve, delay ) );
|
||||
|
||||
return delayPromise.then ( () => attempt.apply ( undefined, arguments ) );
|
||||
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
} as T;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
const retryifySync = <T extends FN> ( fn: T, isRetriableError: FN<[Exception], boolean | void> ): FN<[number], T> => {
|
||||
|
||||
return function ( timestamp: number ) {
|
||||
|
||||
return function attempt () {
|
||||
|
||||
try {
|
||||
|
||||
return fn.apply ( undefined, arguments );
|
||||
|
||||
} catch ( error ) {
|
||||
|
||||
if ( Date.now () > timestamp ) throw error;
|
||||
|
||||
if ( isRetriableError ( error ) ) return attempt.apply ( undefined, arguments );
|
||||
|
||||
throw error;
|
||||
|
||||
}
|
||||
|
||||
} as T;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export {retryifyAsync, retryifySync};
|
||||
95
extensions/Excel转JSON/node_modules/atomically/src/utils/retryify_queue.ts
generated
vendored
Normal file
95
extensions/Excel转JSON/node_modules/atomically/src/utils/retryify_queue.ts
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import {LIMIT_FILES_DESCRIPTORS} from '../consts';
|
||||
|
||||
/* RETRYIFY QUEUE */
|
||||
|
||||
const RetryfyQueue = {
|
||||
|
||||
interval: 25,
|
||||
intervalId: <NodeJS.Timeout | undefined> undefined,
|
||||
limit: LIMIT_FILES_DESCRIPTORS,
|
||||
queueActive: new Set<Function> (),
|
||||
queueWaiting: new Set<Function> (),
|
||||
|
||||
init: (): void => {
|
||||
|
||||
if ( RetryfyQueue.intervalId ) return;
|
||||
|
||||
RetryfyQueue.intervalId = setInterval ( RetryfyQueue.tick, RetryfyQueue.interval );
|
||||
|
||||
},
|
||||
|
||||
reset: (): void => {
|
||||
|
||||
if ( !RetryfyQueue.intervalId ) return;
|
||||
|
||||
clearInterval ( RetryfyQueue.intervalId );
|
||||
|
||||
delete RetryfyQueue.intervalId;
|
||||
|
||||
},
|
||||
|
||||
add: ( fn: Function ): void => {
|
||||
|
||||
RetryfyQueue.queueWaiting.add ( fn );
|
||||
|
||||
if ( RetryfyQueue.queueActive.size < ( RetryfyQueue.limit / 2 ) ) { // Active queue not under preassure, executing immediately
|
||||
|
||||
RetryfyQueue.tick ();
|
||||
|
||||
} else {
|
||||
|
||||
RetryfyQueue.init ();
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
remove: ( fn: Function ): void => {
|
||||
|
||||
RetryfyQueue.queueWaiting.delete ( fn );
|
||||
|
||||
RetryfyQueue.queueActive.delete ( fn );
|
||||
|
||||
},
|
||||
|
||||
schedule: (): Promise<Function> => {
|
||||
|
||||
return new Promise ( resolve => {
|
||||
|
||||
const cleanup = () => RetryfyQueue.remove ( resolver );
|
||||
|
||||
const resolver = () => resolve ( cleanup );
|
||||
|
||||
RetryfyQueue.add ( resolver );
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
tick: (): void => {
|
||||
|
||||
if ( RetryfyQueue.queueActive.size >= RetryfyQueue.limit ) return;
|
||||
|
||||
if ( !RetryfyQueue.queueWaiting.size ) return RetryfyQueue.reset ();
|
||||
|
||||
for ( const fn of RetryfyQueue.queueWaiting ) {
|
||||
|
||||
if ( RetryfyQueue.queueActive.size >= RetryfyQueue.limit ) break;
|
||||
|
||||
RetryfyQueue.queueWaiting.delete ( fn );
|
||||
RetryfyQueue.queueActive.add ( fn );
|
||||
|
||||
fn ();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export default RetryfyQueue;
|
||||
60
extensions/Excel转JSON/node_modules/atomically/src/utils/scheduler.ts
generated
vendored
Normal file
60
extensions/Excel转JSON/node_modules/atomically/src/utils/scheduler.ts
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import {Disposer} from '../types';
|
||||
|
||||
/* VARIABLES */
|
||||
|
||||
const Queues: Record<string, Function[] | undefined> = {};
|
||||
|
||||
/* SCHEDULER */
|
||||
|
||||
//TODO: Maybe publish this as a standalone package
|
||||
|
||||
const Scheduler = {
|
||||
|
||||
next: ( id: string ): void => {
|
||||
|
||||
const queue = Queues[id];
|
||||
|
||||
if ( !queue ) return;
|
||||
|
||||
queue.shift ();
|
||||
|
||||
const job = queue[0];
|
||||
|
||||
if ( job ) {
|
||||
|
||||
job ( () => Scheduler.next ( id ) );
|
||||
|
||||
} else {
|
||||
|
||||
delete Queues[id];
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
schedule: ( id: string ): Promise<Disposer> => {
|
||||
|
||||
return new Promise ( resolve => {
|
||||
|
||||
let queue = Queues[id];
|
||||
|
||||
if ( !queue ) queue = Queues[id] = [];
|
||||
|
||||
queue.push ( resolve );
|
||||
|
||||
if ( queue.length > 1 ) return;
|
||||
|
||||
resolve ( () => Scheduler.next ( id ) );
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export default Scheduler;
|
||||
97
extensions/Excel转JSON/node_modules/atomically/src/utils/temp.ts
generated
vendored
Normal file
97
extensions/Excel转JSON/node_modules/atomically/src/utils/temp.ts
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
|
||||
/* IMPORT */
|
||||
|
||||
import * as path from 'path';
|
||||
import {LIMIT_BASENAME_LENGTH} from '../consts';
|
||||
import {Disposer} from '../types';
|
||||
import FS from './fs';
|
||||
|
||||
/* TEMP */
|
||||
|
||||
//TODO: Maybe publish this as a standalone package
|
||||
|
||||
const Temp = {
|
||||
|
||||
store: <Record<string, boolean>> {}, // filePath => purge
|
||||
|
||||
create: ( filePath: string ): string => {
|
||||
|
||||
const randomness = `000000${Math.floor ( Math.random () * 16777215 ).toString ( 16 )}`.slice ( -6 ), // 6 random-enough hex characters
|
||||
timestamp = Date.now ().toString ().slice ( -10 ), // 10 precise timestamp digits
|
||||
prefix = 'tmp-',
|
||||
suffix = `.${prefix}${timestamp}${randomness}`,
|
||||
tempPath = `${filePath}${suffix}`;
|
||||
|
||||
return tempPath;
|
||||
|
||||
},
|
||||
|
||||
get: ( filePath: string, creator: ( filePath: string ) => string, purge: boolean = true ): [string, Disposer] => {
|
||||
|
||||
const tempPath = Temp.truncate ( creator ( filePath ) );
|
||||
|
||||
if ( tempPath in Temp.store ) return Temp.get ( filePath, creator, purge ); // Collision found, try again
|
||||
|
||||
Temp.store[tempPath] = purge;
|
||||
|
||||
const disposer = () => delete Temp.store[tempPath];
|
||||
|
||||
return [tempPath, disposer];
|
||||
|
||||
},
|
||||
|
||||
purge: ( filePath: string ): void => {
|
||||
|
||||
if ( !Temp.store[filePath] ) return;
|
||||
|
||||
delete Temp.store[filePath];
|
||||
|
||||
FS.unlinkAttempt ( filePath );
|
||||
|
||||
},
|
||||
|
||||
purgeSync: ( filePath: string ): void => {
|
||||
|
||||
if ( !Temp.store[filePath] ) return;
|
||||
|
||||
delete Temp.store[filePath];
|
||||
|
||||
FS.unlinkSyncAttempt ( filePath );
|
||||
|
||||
},
|
||||
|
||||
purgeSyncAll: (): void => {
|
||||
|
||||
for ( const filePath in Temp.store ) {
|
||||
|
||||
Temp.purgeSync ( filePath );
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
truncate: ( filePath: string ): string => { // Truncating paths to avoid getting an "ENAMETOOLONG" error //FIXME: This doesn't really always work, the actual filesystem limits must be detected for this to be implemented correctly
|
||||
|
||||
const basename = path.basename ( filePath );
|
||||
|
||||
if ( basename.length <= LIMIT_BASENAME_LENGTH ) return filePath; //FIXME: Rough and quick attempt at detecting ok lengths
|
||||
|
||||
const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec ( basename );
|
||||
|
||||
if ( !truncable ) return filePath; //FIXME: No truncable part detected, can't really do much without also changing the parent path, which is unsafe, hoping for the best here
|
||||
|
||||
const truncationLength = basename.length - LIMIT_BASENAME_LENGTH;
|
||||
|
||||
return `${filePath.slice ( 0, - basename.length )}${truncable[1]}${truncable[2].slice ( 0, - truncationLength )}${truncable[3]}`; //FIXME: The truncable part might be shorter than needed here
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* INIT */
|
||||
|
||||
process.on ( 'exit', Temp.purgeSyncAll ); // Ensuring purgeable temp files are purged on exit
|
||||
|
||||
/* EXPORT */
|
||||
|
||||
export default Temp;
|
||||
Reference in New Issue
Block a user